<?php
namespace App\Controller\Front;
use App\Entity\Cadre;
use App\Entity\CaisseAmericaine;
use App\Entity\ClientOrder;
use App\Entity\Internaute;
use App\Entity\PassePartout;
use App\Entity\Sujet;
use App\Manager\SettingManager;
use App\Repository\ClientOrderRepository;
use App\Repository\ImageRepository;
use App\Repository\ProjectRepository;
use App\Repository\TinyUrlRepository;
use App\Service\ClientOrderManager;
use App\Service\Mailer;
use App\Service\OrderProductService;
use Doctrine\Persistence\ManagerRegistry;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/client-order', name: 'client_order.')]
class ClientOrderController extends AbstractController
{
public function __construct(
private ImageRepository $imageRepository,
private ProjectRepository $projectRepository,
private ClientOrderManager $clientOrderManager,
private TinyUrlRepository $tinyUrlRepository,
private ClientOrderRepository $clientOrderRepository,
private SettingManager $settingManager,
private ManagerRegistry $doctrine,
private OrderProductService $orderProductService,
) {}
#[Route('/generate-devis', name: 'generate_devis', methods: ['POST'])]
public function generateDevis(Request $request): BinaryFileResponse|JsonResponse
{
$data = json_decode($request->getContent(), true);
if (!$data) {
return new JsonResponse(['error' => 'Invalid JSON'], Response::HTTP_BAD_REQUEST);
}
$project = $this->projectRepository->findOneBy(['token' => $data['project_token'] ?? '']);
if (!$project) {
return new JsonResponse(['error' => 'No project found'], Response::HTTP_BAD_REQUEST);
}
$clientOrder = new ClientOrder();
$clientOrder->setProject($project);
$clientOrder->setReference('DEVIS-' . date('YmdHis') . '-' . substr(uniqid(), -4));
$totalPrice = 0;
if (!empty($data['client'])) {
$internaute = $this->clientOrderManager->makeInternauteFromData($data['client']);
$clientOrder->setInternaute($internaute);
}
foreach ($data['groups'] ?? [] as $group) {
if (!empty($group['cadre'])) {
$cadre = $this->clientOrderManager->makeCadreFromData($group['cadre']);
$cadre->setPrice((float) ($group['cadre']['price'] ?? 0));
$cadre->setQuantity((int) ($group['cadre']['quantity'] ?? 1));
$clientOrder->addOrderProduct($cadre);
$totalPrice += (float) ($group['cadre']['price'] ?? 0);
}
if (!empty($group['passepartout'])) {
$pp = $group['passepartout'];
if (($pp['marge_haut'] ?? 0) != 0 || ($pp['marge_bas'] ?? 0) != 0 || ($pp['marge_gauche'] ?? 0) != 0 || ($pp['marge_droit'] ?? 0) != 0) {
$passepartout = $this->clientOrderManager->makePassePartoutFromData($pp);
$passepartout->setPrice((float) ($pp['price'] ?? 0));
$passepartout->setQuantity((int) ($pp['quantity'] ?? 1));
$clientOrder->addOrderProduct($passepartout);
$totalPrice += (float) ($pp['price'] ?? 0);
}
}
if (!empty($group['caisseamericaine'])) {
$ca = $this->clientOrderManager->makeCaisseAmericaineFromData($group['caisseamericaine']);
$ca->setPrice((float) ($group['caisseamericaine']['price'] ?? 0));
$ca->setQuantity((int) ($group['caisseamericaine']['quantity'] ?? 1));
$clientOrder->addOrderProduct($ca);
$totalPrice += (float) ($group['caisseamericaine']['price'] ?? 0);
}
if (!empty($group['sujet']) && ($group['sujet']['price'] ?? $group['sujet']['largeur'] ?? null)) {
$sujet = $this->clientOrderManager->makeSujetFromData($group['sujet']);
$sujet->setPrice((float) ($group['sujet']['price'] ?? 0));
$clientOrder->addOrderProduct($sujet);
$totalPrice += (float) ($group['sujet']['price'] ?? 0);
}
if (!empty($group['partial'])) {
$partial = $this->clientOrderManager->makePartialFromApiData($group['partial']);
if ($partial) {
$clientOrder->addOrderProduct($partial);
$totalPrice += (float) ($group['partial']['price'] ?? 0);
}
}
if (!empty($group['save_code'])) {
$clientOrder->setSaveLink($group['save_code']);
}
}
$clientOrder->setPrice(round($totalPrice, 2));
$project->settings = $this->settingManager->getProjectSettings($project);
$clientOrder->setProject($project);
$filePath = sprintf("%s/var/cache/pdf/client-order/%s.pdf",
$this->getParameter('kernel.project_dir'),
uniqid('devis_')
);
$this->clientOrderManager->streamPdf($clientOrder, $filePath);
if (!file_exists($filePath)) {
return new JsonResponse(['error' => 'PDF generation failed'], Response::HTTP_INTERNAL_SERVER_ERROR);
}
$response = $this->file($filePath, 'devis_' . $clientOrder->getReference() . '.pdf', ResponseHeaderBag::DISPOSITION_INLINE);
$response->deleteFileAfterSend(true);
return $response;
}
// Used for wordpress plugin call
#[Route('/create', name: 'create')]
public function create(Request $request, ManagerRegistry $em, Mailer $mailer, LoggerInterface $logger): JsonResponse
{
$r_cadre = $request->get('cadre');
$r_passepartout = $request->get('passepartout');
$r_caisseamericaine = $request->get('caisseamericaine');
$r_sujet = $request->get('sujet');
$r_intenaute = $request->get('client');
$r_partial = $request->get('partial');
if (!$r_cadre && !$r_passepartout && !$r_sujet && !$r_caisseamericaine) return new JsonResponse(['error' => 'No data provided'], Response::HTTP_BAD_REQUEST);
$clientOrder = new ClientOrder();
$price = 0;
if ( $r_cadre )
{
$cadre = $this->clientOrderManager->makeCadreFromData($r_cadre);
array_key_exists('price', $r_cadre) ? $price += (float) $r_cadre['price'] : $price += 0;
$clientOrder->setCadre($cadre);
$em->getManager()->persist($cadre);
}
if ( $r_passepartout )
{
if ( ($r_passepartout['marge_haut'] ?? 0) != 0 || ($r_passepartout['marge_bas'] ?? 0) != 0 || ($r_passepartout['marge_gauche'] ?? 0) != 0 || ($r_passepartout['marge_droit'] ?? 0) != 0 )
{
$passepartout = $this->clientOrderManager->makePassePartoutFromData($r_passepartout);
array_key_exists('price', $r_passepartout) ? $price += (float) $r_passepartout['price'] : $price += 0;
$clientOrder->setPassePartout($passepartout);
$em->getManager()->persist($passepartout);
}
}
if ( $r_caisseamericaine )
{
$caisseAmericaine = $this->clientOrderManager->makeCaisseAmericaineFromData($r_caisseamericaine);
array_key_exists('price', $r_caisseamericaine) ? $price += (float) $r_caisseamericaine['price'] : $price += 0;
$clientOrder->setCaisseAmericaine($caisseAmericaine);
$em->getManager()->persist($caisseAmericaine);
}
if ( $r_sujet && ($r_sujet['price'] ?? $r_sujet['largeur'] ?? null) )
{
$sujet = $this->clientOrderManager->makeSujetFromData($r_sujet);
array_key_exists('price', $r_sujet) ? $price += (float) $r_sujet['price'] : $price += 0;
$clientOrder->setSujet($sujet);
$em->getManager()->persist($sujet);
}
if ( $r_partial )
{
$partial = $this->clientOrderManager->makePartialFromApiData($r_partial);
if ($partial) {
array_key_exists('price', $r_partial) ? $price += (float) $r_partial['price'] : $price += 0;
$clientOrder->setPartial($partial);
$em->getManager()->persist($partial);
}
}
if ( $r_intenaute )
{
$internaute = $this->clientOrderManager->makeInternauteFromData($r_intenaute);
$clientOrder->setInternaute($internaute);
$em->getManager()->persist($internaute);
}
$this->clientOrderManager->setReference($clientOrder);
$project = $this->projectRepository->findOneBy(['id' => $request->get('project_id')]);
if (!$project) {
$project = $this->projectRepository->findOneBy(['token' => $request->get('project_token')]);
}
if (!$project) {
return new JsonResponse(['error' => 'No project found'], Response::HTTP_BAD_REQUEST);
}
$clientOrder->setSellerName($request->get('seller_name'));
$clientOrder->setComment($request->get('comment'));
$clientOrder->setSaveLink($request->get('save_code'));
$clientOrder->setPrice(round($price, 2));
$clientOrder->setProject($project);
$clientOrder->setStatus(ClientOrder::STATUS_TOSEND);
foreach (($request->get('tiny_urls') ?? []) as $url) {
$urlParts = explode('/', $url);
$slug = end($urlParts);
if ($tinyUrl = $this->tinyUrlRepository->findOneBy(['project' => $project, 'slug' => $slug])) {
$tinyUrl->setClientOrder($clientOrder);
}
}
$em->getManager()->persist($clientOrder);
$em->getManager()->flush();
if ($clientOrder->getStatus() != ClientOrder::STATUS_DRAFT) {
$mailer->sendCreated($clientOrder);
}
if ($project->getProjectSettings()->isHasAutomaticSendToNielsen() && $clientOrder->getStatus() === ClientOrder::STATUS_TOSEND)
{
$this->clientOrderManager->sendToNielsen($clientOrder);
}
return new JsonResponse("ok", Response::HTTP_OK);
}
#[Route('/{reference}/print-by-reference', name: 'print_by_reference')]
public function printByReference(ClientOrder $clientOrder): RedirectResponse
{
return $this->redirectToRoute('client_order.print', ['id' => $clientOrder->getId()]);
}
#[Route('/{id}/print', name: 'print')]
#[Route('/print', name: 'print_data')]
// public function print(Request $request, ?ClientOrder $clientOrder = null): BinaryFileResponse
public function print(Request $request, ?ClientOrder $clientOrder = null)
{
$clientOrder = $clientOrder
?? $this->makeClientOrderFromData($request->query->all());
$project = $clientOrder->getProject();
$project->settings = $this->settingManager->getProjectSettings($clientOrder->getProject());
$clientOrder->setProject($project);
// return $this->render('client_order/ticket/ticket.pdf.twig', [
// 'clientOrder' => $clientOrder,
// ]);
$filePath = sprintf("%s/var/cache/pdf/client-order/%s.pdf",
$this->getParameter('kernel.project_dir'),
uniqid()
);
$this->clientOrderManager->streamPdf($clientOrder, $filePath);
return $this->file($filePath, 'composition_' . $clientOrder->getReference() ?? "XXXXXX" . '.pdf', ResponseHeaderBag::DISPOSITION_INLINE);
}
private function makeClientOrderFromData(array $data): ClientOrder
{
$r_cadre = $data['cadre'] ?? [];
$r_passepartout = $data['passepartout'] ?? [];
$r_caisseamericaine = $data['caisseamericaine'] ?? [];
$r_sujet = $data['sujet'] ?? [];
$r_intenaute = $data['client'] ?? [];
if ( isset($data['reference']) && $clientOrder = $this->clientOrderRepository->findOneBy(['reference' => $data['reference']])) {
$save_link = $data['save_link'] ?? null;
// if save_link is 6 characters or less, find the url from the tiny_url table
if (strlen($save_link) <= 6)
{
$tinyUrl = $this->tinyUrlRepository->findOneBy(['slug' => $save_link]);
if ($tinyUrl)
{
$url = $tinyUrl->getUrl();
if ( !$url )
{
$save_link = $this->generateUrl('project.app.new', ['token' => $clientOrder->getProject()->getToken()], 0) . '?save_code=' . $save_link;
}
}
}
$clientOrder->setSaveLink($save_link);
return $clientOrder;
}
$clientOrder = new ClientOrder();
$price = 0;
if ( !empty($r_cadre) ) {
$cadre = $this->clientOrderManager->makeCadreFromData($r_cadre);
$clientOrder->setCadre($cadre);
$price += (float) ($r_cadre['price'] ?? 0);
}
if ( !empty($r_passepartout) && $r_passepartout['marge_haut'] !== "0" ) {
$passepartout = $this->clientOrderManager->makePassePartoutFromData($r_passepartout);
$clientOrder->setPassePartout($passepartout);
$price += (float) ($r_passepartout['price'] ?? 0);
}
if ( !empty($r_caisseamericaine) ) {
$caisseAmericaine = $this->clientOrderManager->makeCaisseAmericaineFromData($r_caisseamericaine);
$clientOrder->setCaisseAmericaine($caisseAmericaine);
$price += (float) ($r_caisseamericaine['price'] ?? 0);
}
if ( !empty($r_sujet) ) {
$sujet = $this->clientOrderManager->makeSujetFromData($r_sujet);
$clientOrder->setSujet($sujet);
$price += (float) ($r_sujet['price'] ?? 0);
}
if ( !empty($r_intenaute) ) {
$internaute = $this->clientOrderManager->makeInternauteFromData($r_intenaute);
$clientOrder->setInternaute($internaute);
}
$project = null;
if (key_exists('project_id', $data))
{
$project = $this->projectRepository->findOneBy(['id' => $data['project_id']]);
}
if (key_exists('project_token', $data))
{
$project = $this->projectRepository->findOneBy(['token' => $data['project_token']]);
}
$save_link = $data['save_link'] ?? null;
// if save_link is 6 characters or less, find the url from the tiny_url table
if (strlen($save_link) <= 6)
{
$tinyUrl = $this->tinyUrlRepository->findOneBy(['slug' => $save_link]);
if ($tinyUrl)
{
$url = $tinyUrl->getUrl();
if ( !$url )
{
$save_link = $this->generateUrl('project.app.new', ['token' => $project->getToken()], 0) . '?save_code=' . $save_link;
}
}
}
$clientOrder
->setReference($data['reference'] ?? null)
->setSellerName($data['seller_name'])
->setComment($data['comment'])
->setPrice($price)
->setProject($project)
->setSaveLink($save_link)
;
return $clientOrder;
}
#[Route('/{reference}/update', name: 'update', methods: ['PUT', 'PATCH'])]
public function update(Request $request, ClientOrder $clientOrder): JsonResponse
{
$data = json_decode($request->getContent(), true);
if (!$data) {
return new JsonResponse(['error' => 'Invalid JSON data'], Response::HTTP_BAD_REQUEST);
}
try {
// Handle cart items update - support both new cart structure and legacy cartItems
$cartItems = $this->getCartItemsFromConfig($data);
if (!empty($cartItems)) {
$this->updateClientOrderFromCartItems($clientOrder, $cartItems);
} else {
// Legacy system fallback using ClientOrderManager
$this->clientOrderManager->updateFromConfig($clientOrder, $data);
}
// Update additional fields - support both new cart structure and legacy
$cartTotal = $this->getCartTotalFromConfig($data);
if ($cartTotal !== null) {
$clientOrder->setPrice(round($cartTotal, 2));
}
if (isset($data['comment'])) {
$clientOrder->setComment($data['comment']);
}
if (isset($data['sellerName'])) {
$clientOrder->setSellerName($data['sellerName']);
}
$em = $this->doctrine->getManager();
// Update the associated TinyUrl's config with the full configuration state
$tinyUrl = $this->tinyUrlRepository->findOneBy(['clientOrder' => $clientOrder]);
if ($tinyUrl) {
// Remove backend-specific fields that shouldn't be stored in the config
$configToStore = $data;
unset($configToStore['projectToken']); // This is project-specific, not config-specific
$tinyUrl->setAppConfig($configToStore);
$em->persist($tinyUrl);
}
$clientOrder->setPriceNetRefToSend($this->clientOrderManager->getTotalNet($clientOrder));
$em->persist($clientOrder);
$em->flush();
return new JsonResponse([
'success' => true,
'reference' => $clientOrder->getReference(),
'price' => $clientOrder->getPrice(),
'message' => 'ClientOrder updated successfully'
], Response::HTTP_OK);
} catch (\Exception $e) {
return new JsonResponse([
'error' => 'Failed to update ClientOrder',
'message' => $e->getMessage()
], Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
private function updateClientOrderFromCartItems(ClientOrder $clientOrder, array $cartItems): void
{
$em = $this->doctrine->getManager();
// Clear existing order products
foreach ($clientOrder->getOrderProducts() as $orderProduct) {
$clientOrder->removeOrderProduct($orderProduct);
$em->remove($orderProduct);
}
// Clear legacy products for backward compatibility
foreach (['cadre', 'passePartout', 'caisseAmericaine', 'sujet'] as $property) {
$getter = 'get' . ucfirst($property);
$setter = 'set' . ucfirst($property);
if (method_exists($clientOrder, $getter) && $entity = $clientOrder->$getter()) {
$clientOrder->$setter(null);
$em->remove($entity);
}
}
// Create new order products from cart items
$this->createOrderProductsFromCartItems($clientOrder, $cartItems);
}
private function createOrderProductsFromCartItems(ClientOrder $clientOrder, array $cartItems): void
{
$em = $this->doctrine->getManager();
foreach ($cartItems as $cartItem) {
$orderProduct = $this->orderProductService->createOrderProductFromCartItem($cartItem);
if ($orderProduct) {
$clientOrder->addOrderProduct($orderProduct);
$em->persist($orderProduct);
}
}
}
/**
* Helper method to get cart items from config
*/
private function getCartItemsFromConfig(array $config): array
{
// Use new cart structure: config.cart.items
if (isset($config['cart']['items']) && is_array($config['cart']['items'])) {
return $config['cart']['items'];
}
return [];
}
/**
* Helper method to get cart total from config
*/
private function getCartTotalFromConfig(array $config): ?float
{
// Use new cart structure: config.cart.total
if (isset($config['cart']['total']) && is_numeric($config['cart']['total'])) {
return (float) $config['cart']['total'];
}
return null;
}
}