src/Controller/Front/CatalogueController.php line 198

Open in your IDE?
  1. <?php
  2. namespace App\Controller\Front;
  3. use App\Adapter\ProductAdapter;
  4. use App\Constants\Setting as SettingConst;
  5. use App\Entity\CustomerProduct;
  6. use App\Entity\CustomProduct;
  7. use App\Entity\CustomProductField;
  8. use App\Entity\CustomProductOrder;
  9. use App\Entity\CustomProductType;
  10. use App\Entity\RequestProductAvailable;
  11. use App\Entity\ServicePro;
  12. use App\Entity\ServiceProField;
  13. use App\Entity\ServiceUser;
  14. use App\Entity\User;
  15. use App\Exception\CatalogueException;
  16. use App\Exception\PurchaseDeclarationException;
  17. use App\Services\Common\ModuleSettingService;
  18. use App\Services\Common\Point\UserPointService;
  19. use App\Form\Type\AddToCartType;
  20. use App\Model\Product;
  21. use App\Model\ProductDeclination;
  22. use App\Services\Common\AclServiceV2;
  23. use App\Services\Common\MailerService;
  24. use App\Services\Common\PlatformService;
  25. use App\Services\Common\Point\TransactionService;
  26. use App\Services\Common\Point\UserPointServiceInterface;
  27. use App\Services\Common\SettingStatusService;
  28. use App\Services\Common\StockService;
  29. use App\Services\Common\Translation\CatalogueTranslation;
  30. use App\Services\Common\Translation\TranslationBase;
  31. use App\Services\Common\UserService;
  32. use App\Services\CommonServices;
  33. use App\Services\DTV\YamlConfig\YamlReader;
  34. use App\Services\Front\CartService;
  35. use App\Services\Front\Catalogue\CatalogueService;
  36. use App\Services\Front\Catalogue\ElasticSearchCatalogueService;
  37. use App\Services\Front\Catalogue\JsonCatalogueService;
  38. use App\Services\Old\CatalogSearch;
  39. use App\Services\Old\PriceManager;
  40. use DateTime;
  41. use Doctrine\ORM\EntityManagerInterface;
  42. use Exception;
  43. use JMS\Serializer\SerializationContext;
  44. use JMS\Serializer\SerializerInterface;
  45. use JsonException;
  46. use SlopeIt\BreadcrumbBundle\Annotation\Breadcrumb;
  47. use SlopeIt\BreadcrumbBundle\Service\BreadcrumbBuilder;
  48. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  49. use Symfony\Component\HttpFoundation\JsonResponse;
  50. use Symfony\Component\HttpFoundation\RedirectResponse;
  51. use Symfony\Component\HttpFoundation\Request;
  52. use Symfony\Component\HttpFoundation\RequestStack;
  53. use Symfony\Component\HttpFoundation\Response;
  54. use Symfony\Contracts\Translation\TranslatorInterface;
  55. use Throwable;
  56. /**
  57. * @Breadcrumb({"label" = "Accueil", "route" = "front_homepage" })
  58. */
  59. class CatalogueController extends AbstractController
  60. {
  61. public const WARNINGCBPAYMENTREQUIRED = 4;
  62. public const LABEL_CATALOGUE = [
  63. 'pub' => 'Objets pub',
  64. 'service' => 'Service pro',
  65. 'services' => 'Services pro',
  66. 'shopping' => 'Shopping',
  67. 'evasion' => 'evasion',
  68. 'paniers_garnis' => 'Paniers garnis',
  69. 'cheques_cadeaux' => 'Chèques cadeaux',
  70. ];
  71. private RequestStack $requestStack;
  72. private CatalogSearch $catalogSearch;
  73. private PriceManager $priceManager;
  74. private EntityManagerInterface $em;
  75. private PlatformService $platformService;
  76. private MailerService $mailerService;
  77. private CartService $cartService;
  78. private YamlReader $yamlReader;
  79. private JsonCatalogueService $jsonCatalogueService;
  80. private ElasticSearchCatalogueService $elasticSearchCatalogueService;
  81. private CommonServices $commonServices;
  82. private ProductAdapter $productAdapter;
  83. private SerializerInterface $serializer;
  84. private UserPointServiceInterface $userPointService;
  85. private TranslationBase $translationBase;
  86. private CatalogueTranslation $catalogueTranslation;
  87. private TranslatorInterface $translator;
  88. private CatalogueService $catalogueService;
  89. private BreadcrumbBuilder $breadcrumbBuilder;
  90. private StockService $stockService;
  91. private AclServiceV2 $aclService;
  92. private SettingStatusService $settingStatusService;
  93. private TransactionService $transactionService;
  94. private ModuleSettingService $moduleSettingService;
  95. private UserService $userService;
  96. /**
  97. * @param RequestStack $requestStack
  98. * @param CatalogSearch $catalogSearch
  99. * @param PriceManager $priceManager
  100. * @param EntityManagerInterface $em
  101. * @param PlatformService $platformService
  102. * @param CartService $cartService
  103. * @param YamlReader $yamlReader
  104. * @param JsonCatalogueService $jsonCatalogueService
  105. * @param ElasticSearchCatalogueService $elasticSearchCatalogueService
  106. * @param MailerService $mailerService
  107. * @param ProductAdapter $productAdapter
  108. * @param CommonServices $commonServices
  109. * @param SerializerInterface $serializer
  110. * @param UserPointService $userPointService
  111. * @param TranslationBase $translationBase
  112. * @param CatalogueTranslation $catalogueTranslation
  113. * @param TranslatorInterface $translator
  114. * @param CatalogueService $catalogueService
  115. * @param BreadcrumbBuilder $breadcrumbBuilder
  116. * @param StockService $stockService
  117. * @param AclServiceV2 $aclService
  118. * @param SettingStatusService $settingStatusService
  119. * @param TransactionService $transactionService
  120. * @param ModuleSettingService $moduleSettingService
  121. * @param UserService $userService
  122. *
  123. * @throws Exception
  124. */
  125. public function __construct(
  126. RequestStack $requestStack,
  127. CatalogSearch $catalogSearch,
  128. PriceManager $priceManager,
  129. EntityManagerInterface $em,
  130. PlatformService $platformService,
  131. CartService $cartService,
  132. YamlReader $yamlReader,
  133. JsonCatalogueService $jsonCatalogueService,
  134. ElasticSearchCatalogueService $elasticSearchCatalogueService,
  135. MailerService $mailerService,
  136. ProductAdapter $productAdapter,
  137. CommonServices $commonServices,
  138. SerializerInterface $serializer,
  139. UserPointService $userPointService,
  140. TranslationBase $translationBase,
  141. CatalogueTranslation $catalogueTranslation,
  142. TranslatorInterface $translator,
  143. CatalogueService $catalogueService,
  144. BreadcrumbBuilder $breadcrumbBuilder,
  145. StockService $stockService,
  146. AclServiceV2 $aclService,
  147. SettingStatusService $settingStatusService,
  148. TransactionService $transactionService,
  149. ModuleSettingService $moduleSettingService,
  150. UserService $userService
  151. ) {
  152. $this->cartService = $cartService;
  153. $this->yamlReader = $yamlReader;
  154. $this->jsonCatalogueService = $jsonCatalogueService;
  155. $this->elasticSearchCatalogueService = $elasticSearchCatalogueService;
  156. $this->requestStack = $requestStack;
  157. $this->catalogSearch = $catalogSearch;
  158. $this->priceManager = $priceManager;
  159. $this->em = $em;
  160. $this->platformService = $platformService;
  161. $this->mailerService = $mailerService;
  162. $this->productAdapter = $productAdapter;
  163. $this->commonServices = $commonServices;
  164. $this->serializer = $serializer;
  165. $this->userPointService = $userPointService;
  166. $this->translationBase = $translationBase;
  167. $this->catalogueTranslation = $catalogueTranslation;
  168. $this->translator = $translator;
  169. $this->catalogueService = $catalogueService;
  170. $this->breadcrumbBuilder = $breadcrumbBuilder;
  171. $this->stockService = $stockService;
  172. $this->aclService = $aclService;
  173. $this->settingStatusService = $settingStatusService;
  174. $this->transactionService = $transactionService;
  175. $this->moduleSettingService = $moduleSettingService;
  176. $this->userService = $userService;}
  177. /**
  178. * Page du catalogue
  179. *
  180. * @param Request $request
  181. *
  182. * @return Response
  183. *
  184. * @throws CatalogueException
  185. * @throws JsonException
  186. */
  187. public function show(Request $request): Response
  188. {
  189. $global = $this->yamlReader->getGlobal();
  190. // Hack de redirection des services pros à partir de la version 1.4.0
  191. if (
  192. $this->platformService->versionIsGreaterOrEqualThan('1.4.0') &&
  193. in_array($request->get('catalogue'), [
  194. 'service',
  195. 'services',
  196. 'services-pro',
  197. ])
  198. ) {
  199. foreach($global[ 'shop' ][ 'catalogues' ] as $catalog) {
  200. if($catalog['slug'] === 'service'){
  201. $label = $catalog['label'];
  202. }
  203. }
  204. return new RedirectResponse(
  205. $this->generateUrl('front_catalogue_service_pros', [
  206. 'label' => $this->commonServices->toURL($label),
  207. ]),
  208. );
  209. }
  210. // fin de hack
  211. $catalogueType = $this->catalogueService->getCatalogueSlugIfNotExists($request->get('catalogue'));
  212. $session = $this->requestStack->getSession();
  213. $session->set('last-catalogue', $catalogueType);
  214. // on regarde si le user à le droit de regarder le catalogue
  215. if (!$this->aclService->userIsGrantedCatalogue($this->getUser(), $catalogueType, TRUE)) {
  216. throw $this->createAccessDeniedException('Vous ne disposez pas des droits suffisants pour voir ce catalogue');
  217. }
  218. $catalogues = $this->yamlReader->getShop()[ "catalogues" ] ?? [];
  219. $catalogue = array_filter($catalogues, static function ($catalogue) use ($catalogueType) {
  220. return $catalogue[ 'slug' ] === $catalogueType;
  221. }) ?? [];
  222. $catalogue = array_shift($catalogue);
  223. if (isset($catalogue[ 'types' ]) && in_array('customer', $catalogue[ 'types' ], TRUE)) {
  224. /** @var User $user */
  225. $user = $this->getUser();
  226. $pointConfig = $this->yamlReader->getPoint();
  227. $displayConfig = $pointConfig->getDisplayByUser($user);
  228. $catalogueName = $catalogue[ 'label' ];
  229. $displayConfig = [
  230. 'display_config' => [
  231. 'round_price' => $this->settingStatusService->isSettingStatusEnabled(SettingConst::POINT_ENABLED),
  232. // On arrondit les prix si les points sont activés
  233. 'symbol' => $displayConfig->getSymbol(),
  234. 'label' => $displayConfig->getLabel(),
  235. 'multiplication' => $displayConfig->getMultiplication(),
  236. ]
  237. ];
  238. $customerProducts = array_merge(
  239. $this->em->getRepository(CustomerProduct::class)->getAllEnabledJson(false),
  240. $displayConfig
  241. );
  242. $params = [
  243. 'parameters' => [],
  244. 'availablestock' => FALSE,
  245. 'customerProducts' => json_encode($customerProducts),
  246. ];
  247. } else {
  248. $this->catalogSearch->setParameters($request);
  249. $catalogueName = $this->catalogueService->getCatalogueName($catalogueType);
  250. $params = [
  251. 'parameters' => $this->catalogSearch->getParameters(),
  252. 'availablestock' => $this->getParameter("display_available_stock"),
  253. 'customerProducts' => NULL,
  254. ];
  255. }
  256. $params[ 'catalogue' ] = $catalogueName;
  257. $params[ 'catalogueSlug' ] = $catalogueType;
  258. $this->breadcrumbBuilder->addItem($catalogueName);
  259. return $this->render('front/catalogue/index.html.twig', $params);
  260. }
  261. /**
  262. * @param $label
  263. *
  264. * @return Response
  265. * @deprecated
  266. */
  267. public function serviceProList($label): Response
  268. {
  269. $services = $this->em->getRepository(ServicePro::class)->findBy(
  270. [
  271. 'status' => TRUE,
  272. ],
  273. );
  274. return $this->render('front/catalogue/service-pro.html.twig', [
  275. 'label' => $label,
  276. 'services' => $services,
  277. ]);
  278. }
  279. /**
  280. * @param Request $request
  281. * @param ServicePro $service
  282. *
  283. * @return Response
  284. * @throws Exception
  285. * @deprecated
  286. */
  287. public function serviceProShow(Request $request, ServicePro $service): Response
  288. {
  289. if (!$service->isStatus()) {
  290. throw $this->createNotFoundException("Cette page n'existe pas (ou plus) !");
  291. }
  292. if (strtolower($request->getMethod()) == 'post') {
  293. $fields = $request->request->get('service');
  294. $quantity = $request->request->get('quantity');
  295. $formatted_fields = [];
  296. if (is_array($fields)) {
  297. foreach ($fields as $key => $value) {
  298. $index = str_replace('field_', '', $key);
  299. $field = $this->em->getRepository(ServiceProField::class)->find($index);
  300. if (!empty($field)) {
  301. $formatted_fields[] = [
  302. 'label' => $field->getLabel(),
  303. 'value' => $value,
  304. ];
  305. } else {
  306. $formatted_fields[] = [
  307. 'label' => $key,
  308. 'value' => $value,
  309. ];
  310. }
  311. }
  312. }
  313. /** @var User $currentUser */
  314. $currentUser = $this->getUser();
  315. $mailConfig = $this->platformService->getMailerConfig();
  316. $email = $this->renderView('front/catalogue/service-pro-email.html.twig', [
  317. 'service' => $service,
  318. 'fields' => $formatted_fields,
  319. 'user' => $currentUser,
  320. 'header' => $mailConfig[ 'template' ][ 'header' ],
  321. 'project' => $this->platformService->getDomain(),
  322. 'footer' => $mailConfig[ 'template' ][ 'footer' ],
  323. 'quantity' => $quantity,
  324. ]);
  325. $content = [
  326. 'service' => [
  327. 'title' => $service->getTitle(),
  328. 'description' => $service->getDescription(),
  329. 'value' => $service->getValue(),
  330. 'contacts' => $service->getContactEmails(),
  331. ],
  332. 'user' => [
  333. 'id' => $currentUser->getId(),
  334. 'firstName' => $currentUser->getFirstName(),
  335. 'lastName' => $currentUser->getLastName(),
  336. 'email' => $currentUser->getEmail(),
  337. ],
  338. ];
  339. $content = array_merge($content, ['formatted_fields' => $formatted_fields]);
  340. $serviceUser = (new ServiceUser())
  341. ->setUser($currentUser)
  342. ->setQuantity($quantity)
  343. ->setContent(json_encode($content))
  344. ->setTitle($service->getTitle())
  345. ->setDescription($service->getDescription() ?? ' ')
  346. ->setImage($service->getImage() ?? ' ')
  347. ->setStatus(0)
  348. ->setCreatedAt(new DateTime())
  349. ;
  350. $this->em->persist($serviceUser);
  351. $this->em->flush();
  352. $to = $service->getContactEmails();
  353. $subject = 'Une commande de services pros a été passée';
  354. $this->mailerService->sendMailRaw('from', $to, $subject, $email);
  355. $this->addFlash('success', 'Votre commande a été enregistrée avec succès !');
  356. return $this->redirectToRoute('front_user_order');
  357. }
  358. return $this->render('front/catalogue/service-pro-show.html.twig', [
  359. 'service' => $service,
  360. ]);
  361. }
  362. /**
  363. * Liste des produits personnalisés
  364. *
  365. *
  366. * @param string $slug
  367. *
  368. * @return Response
  369. */
  370. public function customProductList(string $slug): Response
  371. {
  372. $products = $this->em->getRepository(CustomProduct::class)->findByEnabledTypeStockNullOrSuppZero($slug);
  373. $rate = $this->yamlReader->getGlobal()[ 'price' ][ 'rate' ] ?? 1;
  374. $type = $this->em->getRepository(CustomProductType::class)->findOneBy(['slug' => $slug]);
  375. return $this->render('front/catalogue/custom-product-list.html.twig', [
  376. 'slug' => $slug,
  377. 'type' => $type,
  378. 'products' => $products,
  379. 'rate' => $rate,
  380. ]);
  381. }
  382. /**
  383. * Focus sur un produit personnalisé
  384. *
  385. *
  386. * @param Request $request
  387. * @param string $slug
  388. * @param CustomProduct $product
  389. *
  390. * @return Response
  391. * @throws Exception
  392. */
  393. public function customProductShow(Request $request, string $slug, CustomProduct $product): Response
  394. {
  395. $pointConfig = $this->yamlReader->getPoint();
  396. if (!$product->isEnabled()) {
  397. throw $this->createNotFoundException("Cette page n'existe pas (ou plus) !");
  398. }
  399. if ($product->getSku()) {
  400. $catalogueType = 'catalogue-v2';
  401. $productObject = $this->jsonCatalogueService->getProductBySkuFromCatalogue($product->getSku(), $catalogueType);
  402. return $this->productFocus($request, $productObject->getSku(), $catalogueType);
  403. }
  404. if (strtolower($request->getMethod()) === 'post') {
  405. $catalogueSlugs = [];
  406. $totalProductPrice = [];
  407. $fields = $request->request->get('product');
  408. $quantity = $request->request->get('quantity');
  409. $catalogueTypes = $this->em->getRepository(CustomProductType::class)->findAll();
  410. $availablePoints = $this->userPointService->getAvailablePoints($this->getUser(), NULL, TRUE);
  411. if ($this->moduleSettingService->isModuleActive('quotas_orders_products')) {
  412. // check tous les quotas produits/commandes
  413. $canBuy = $this->userService->checkQuotasProductOrder($this->getUser(), $product, $product->getType()->getSlug(), $quantity);
  414. if(!$canBuy['valide']) {
  415. $this->addFlash('danger', $canBuy['message']);
  416. return $this->redirectToRoute('front_catalogue_custom_product_show', [
  417. 'slug' => $slug,
  418. 'product' => $product->getId(),
  419. ]);
  420. }
  421. }
  422. if($pointConfig->getPointCategory()) {
  423. $categoryValues = $product->getArrayCategoryValues();
  424. foreach ($categoryValues as $catSlug => $categoryValue) {
  425. $totalProductPrice[$catSlug] = $categoryValue * (int) $quantity;
  426. }
  427. try {
  428. $this->cartService->checkRemainingPointWithProduct($availablePoints, $totalProductPrice);
  429. }catch (\Exception $e){
  430. $this->addFlash('danger', $e->getMessage());
  431. return $this->redirectToRoute('front_catalogue_custom_product_show', [
  432. 'slug' => $slug,
  433. 'product' => $product->getId(),
  434. ]);
  435. }
  436. }else{
  437. // check point valable pour l'utilisateur
  438. if($availablePoints < $quantity * $product->getValue()){
  439. $this->addFlash('danger', "Vous n'avez pas assez de points pour commander ce produit !");
  440. return $this->redirectToRoute('front_catalogue_custom_product_show', [
  441. 'slug' => $slug,
  442. 'product' => $product->getId(),
  443. ]);
  444. }
  445. }
  446. // check stock produit
  447. if($product->getStock() && ($quantity > $product->getStock())){
  448. $this->addFlash('danger', "La quantité demandée est supérieur au stock actuel !");
  449. return $this->redirectToRoute('front_catalogue_custom_product_show', [
  450. 'slug' => $slug,
  451. 'product' => $product->getId(),
  452. ]);
  453. }
  454. foreach ($catalogueTypes as $catalogueType){
  455. $catalogueSlugs[] = $catalogueType->getSlug();
  456. }
  457. $domain = $this->yamlReader->getGlobal()[ 'subdomain' ];
  458. $files = $request->files->get('product') ?? [];
  459. $uploadDir = $this->getParameter('kernel.project_dir') . '/medias/' . $domain;
  460. $fileNames = [];
  461. // enregistrement fichier si champs "file" dans le formulaire
  462. foreach ($files as $key => $file) {
  463. if($file){
  464. $clientOriginalName = str_replace('.' . $file->guessExtension(), '', $file->getClientOriginalName());
  465. $newFilename = $clientOriginalName . '_' . md5(uniqid()) . '.' . $file->guessExtension();
  466. $file->move(
  467. $uploadDir,
  468. $newFilename,
  469. );
  470. $fileNames[$key] = $newFilename;
  471. }
  472. }
  473. $fields = array_merge($fields,$fileNames);
  474. // enregistrement des champs du formulaire en json dans la commande
  475. $formatted_fields = [];
  476. if (is_array($fields)) {
  477. foreach ($fields as $key => $value) {
  478. $index = str_replace('field_', '', $key);
  479. if (in_array($slug, $catalogueSlugs, TRUE)) {
  480. $field = $this->em->getRepository(CustomProductField::class)->find($index);
  481. } else {
  482. $field = $this->em->getRepository(ServiceProField::class)->find($index);
  483. }
  484. if (!empty($field)) {
  485. if($field instanceof CustomProductField){
  486. $formatted_fields[] = [
  487. 'label' => $field->getLabel(),
  488. 'value' => $value,
  489. 'type' => $field->getType(),
  490. 'position' => $field->getPosition(),
  491. 'required' => $field->isRequired(),
  492. ];
  493. }else{
  494. $formatted_fields[] = [
  495. 'label' => $key,
  496. 'value' => $value,
  497. ];
  498. }
  499. } else {
  500. $formatted_fields[] = [
  501. 'label' => $key,
  502. 'value' => $value,
  503. ];
  504. }
  505. }
  506. }
  507. /** @var User $currentUser */
  508. $currentUser = $this->getUser();
  509. if($pointConfig->getPointCategory()) {
  510. $totalValue = json_encode($this->cartService->getTotalChosenSpentPointsWithProduct($availablePoints, $totalProductPrice));
  511. }else{
  512. $totalValue = json_encode($product->getValue() * $quantity);
  513. }
  514. // creation de la customProductOrder
  515. $order = (new CustomProductOrder())
  516. ->setUser($currentUser)
  517. ->setQuantity($quantity)
  518. ->setType($product->getType())
  519. ->setFormData(json_encode($formatted_fields))
  520. ->setValue($totalValue)
  521. ->setProduct(
  522. $this->serializer->serialize(
  523. $product,
  524. 'json',
  525. SerializationContext::create()
  526. ->setGroups(['customProduct:item']),
  527. ),
  528. )
  529. ;
  530. $this->em->persist($order);
  531. // décrément le stock du produit si stock
  532. if($product->getStock()){
  533. $product->setStock($product->getStock() - $quantity);
  534. }
  535. $this->em->flush();
  536. // creation de la transaction
  537. $this->transactionService->createOrderTransactionWithCustomProduct($order);
  538. // met à jour les points des utilisateurs
  539. $this->userPointService->getPointsOfUser($currentUser, null, true);
  540. $this->addFlash('success', 'Votre commande a été enregistrée avec succès !');
  541. return $this->redirectToRoute('front_user_order');
  542. }
  543. return $this->render('front/catalogue/custom-product-show.html.twig', [
  544. 'slug' => $slug,
  545. 'product' => $product,
  546. ]);
  547. }
  548. /**
  549. * Focus sur un produit
  550. *
  551. *
  552. * @param Request $request
  553. * @param string $sku
  554. * @param string $catalogueType
  555. *
  556. * @return Response
  557. *
  558. * @throws Exception
  559. */
  560. public function productFocus(Request $request, string $sku, string $catalogueType): Response
  561. {
  562. /** @var User $user */
  563. $user = $this->getUser();
  564. $displayIfNoStock = false;
  565. // Modification des catalogues depuis la version 1.4.0 : pub.json devient obj_pub.json
  566. if ($catalogueType === 'pub') $catalogueType = 'obj_pub';
  567. // Fin de modification
  568. $catalogueType = $this->catalogueService->getCatalogueSlugIfNotExists($catalogueType);
  569. $catalogueConfig = $this->catalogueService->getCatalogueConfigByType($catalogueType);
  570. if(isset($catalogueConfig["displayIfNoStock"])) {
  571. $displayIfNoStock = $catalogueConfig["displayIfNoStock"];
  572. }
  573. $file = $this->getParameter('kernel.project_dir') . '/data/catalogue/' . $this->platformService->getDomain() . '/' . $catalogueType . '.json';
  574. if (!file_exists($file)) {
  575. return new Response("Le catalogue $catalogueType n'existe pas.", 404);
  576. }
  577. $content = file_get_contents($file);
  578. $parsedJson = json_decode($content, TRUE);
  579. $searchSku = $sku;
  580. $searchProduct = array_filter($parsedJson[ 'products' ], function ($element) use ($searchSku) {
  581. return isset($element[ 'sku' ]) && $element[ 'sku' ] === $searchSku;
  582. });
  583. $product = reset($searchProduct);
  584. if (($product[ 'sku' ] ?? 'not found') != $sku) {
  585. return new Response("L'article ($sku) n'a pas été trouvé.", 404);
  586. }
  587. $productFocus = $this->productAdapter->getProduct($product);
  588. if ($this->translationBase->translationIsEnabled()) {
  589. $locale = $this->translationBase->getLanguageFromCurrentUser();
  590. $productFocus = $this->catalogueTranslation->getTranslatedProduct($productFocus, $locale, $catalogueType);
  591. }
  592. $form = $this->createForm(AddToCartType::class, NULL, [
  593. 'exploded' => FALSE,
  594. 'quantity' => 1,
  595. 'product' => $productFocus,
  596. 'catalogueType' => $request->attributes->get('catalogueType'),
  597. ]);
  598. $declinations = [];
  599. /** @var ProductDeclination $declination */
  600. foreach ($productFocus->getDeclinations() as $declination) {
  601. $declinations[] = $declination->getName();
  602. }
  603. // Breadcrumb pour le catalogue correspondant
  604. $catalogues = $this->yamlReader->getShop()[ "catalogues" ] ?? [];
  605. $catalogue = array_filter($catalogues, static function ($catalogue) use ($catalogueType) {
  606. return $catalogue[ 'slug' ] === $catalogueType;
  607. });
  608. $catalogue = reset($catalogue);
  609. $this->breadcrumbBuilder->addItem($catalogue[ 'label' ], 'front_catalogue_homepage', ["catalogue" => $catalogueType]);
  610. $this->breadcrumbBuilder->addItem($productFocus->getName());
  611. $message = $this->cartService->userCanOrderVerbose($user);
  612. $catalogOrderActive = $this->cartService->catalogOrderActive($catalogueType);
  613. return $this->render('front/catalogue/productSheet/show.html.twig', [
  614. "product" => $productFocus,
  615. "form" => $form->createView(),
  616. "declinations" => array_unique($declinations),
  617. "catalogueType" => $catalogueType,
  618. "closeEcommerceMessage" => $message,
  619. 'catalogOrderActive' => $catalogOrderActive,
  620. 'displayIfNoStock' => $displayIfNoStock
  621. ]);
  622. }
  623. /**
  624. * Retourne le catalogue selon le type de catalogue
  625. *
  626. * @param string $catalogueType Type du catalogue (shopping, pub, ...)
  627. *
  628. * @return JsonResponse
  629. *
  630. * @throws Exception
  631. */
  632. public function getCatalogueByType(string $catalogueType): JsonResponse
  633. {
  634. $catalogueType = $this->catalogueService->getCatalogueSlugIfNotExists($catalogueType);
  635. $catalogueConfig = $this->catalogueService->getCatalogueConfigByType($catalogueType);
  636. $jsonCatalogue = $this->jsonCatalogueService->getCatalogueByType($catalogueType);
  637. $displayIfNoStock = false;
  638. $displayIfPriceZero = false;
  639. if(isset($catalogueConfig["displayIfNoStock"])) {
  640. $displayIfNoStock = $catalogueConfig[ 'displayIfNoStock' ];
  641. }
  642. if(isset($catalogueConfig["displayIfPriceZero"])) {
  643. $displayIfPriceZero = $catalogueConfig[ 'displayIfPriceZero' ];
  644. }
  645. /**
  646. * START : Code pour minimiser le json.
  647. * Sur OpenDream, il dépasse les 20mo et les navigateurs n'étaient plus en capacité de le traiter en js
  648. */
  649. $json = json_decode($jsonCatalogue, TRUE);
  650. $products = $json[ 'products' ] ?? '';
  651. if(empty($products)) {
  652. $products = [];
  653. }
  654. foreach ($products as $index => &$product)
  655. {
  656. // retire du json les produits qui n'ont pas de stock et qu'ils sont de type obj_pub et status inactif.
  657. if (
  658. !$product[ 'status' ] ||
  659. ($product[ 'type' ] === 'obj_pub' && ((int)$product[ 'totalstock' ] === 0 && !$displayIfNoStock)) ||
  660. ($product['type'] !== 'obj_pub' && (
  661. !isset($product['level']) ||
  662. !is_array($product['level']) ||
  663. empty($product['level']['id']) ||
  664. empty($product['level']['value'])
  665. ) && !$displayIfPriceZero)
  666. ) {
  667. array_splice($products, $index, 1);
  668. continue;
  669. }
  670. unset($products[ $index ][ 'description' ]);
  671. unset($products[ $index ][ 'reference' ]);
  672. unset($products[ $index ][ 'accroche' ]);
  673. unset($products[ $index ][ 'status' ]);
  674. unset($products[ $index ][ 'level' ]);
  675. unset($products[ $index ][ 'slug' ]);
  676. unset($products[ $index ][ 'slugs' ]);
  677. unset($products[ $index ][ 'display_if_unavailable' ]);
  678. unset($products[ $index ][ 'is_managed_by_customer' ]);
  679. unset($products[ $index ][ 'campagnes' ]);
  680. unset($products[ $index ][ 'enable_stock_alert' ]);
  681. unset($products[ $index ][ 'stock_alert' ]);
  682. unset($products[ $index ][ 'logistic_weight' ]);
  683. unset($products[ $index ][ 'created_at' ]);
  684. foreach ($products[ $index ][ 'categories' ] as $cat_index => $category) {
  685. unset($products[ $index ][ 'categories' ][ $cat_index ][ 'description' ]);
  686. unset($products[ $index ][ 'categories' ][ $cat_index ][ 'orderItem' ]);
  687. }
  688. unset($products[ $index ][ 'images' ][ 0 ][ 'id' ]);
  689. unset($products[ $index ][ 'images' ][ 1 ]);
  690. unset($products[ $index ][ 'images' ][ 2 ]);
  691. unset($products[ $index ][ 'images' ][ 3 ]);
  692. unset($products[ $index ][ 'images' ][ 4 ]);
  693. unset($products[ $index ][ 'images' ][ 5 ]);
  694. if (isset($product[ 'categories' ])) {
  695. foreach ($product[ 'categories' ] as $cat_index => $category) {
  696. $products[ $index ][ 'categories' ][ $cat_index ][ 'title' ] = $this->translator->trans($category[ 'title' ]);
  697. }
  698. }
  699. }
  700. unset($product);
  701. $json[ 'totalHits' ] = count($products);
  702. $json[ 'products' ] = $products;
  703. $jsonCatalogue = json_encode($json);
  704. /**
  705. * END
  706. */
  707. $pointConfig = $this->yamlReader->getPoint();
  708. /** @var User $currentUser */
  709. $currentUser = $this->getUser();
  710. $displayConfig = $pointConfig->getDisplayByUser($currentUser);
  711. $response = [
  712. 'products' => $jsonCatalogue,
  713. 'display_config' => [
  714. 'round_price' => $this->settingStatusService->isSettingStatusEnabled(SettingConst::POINT_ENABLED),
  715. // On arrondit les prix si les points sont activés
  716. 'symbol' => $displayConfig->getSymbol(),
  717. 'label' => $displayConfig->getLabel(),
  718. 'multiplication' => $displayConfig->getMultiplication(),
  719. ],
  720. ];
  721. return new JsonResponse(
  722. $response,
  723. Response::HTTP_OK,
  724. );
  725. }
  726. // /**
  727. // * @param Request $request
  728. // *
  729. // * @return JsonResponse
  730. // *
  731. // * @throws Exception
  732. // */
  733. // public function search( Request $request ): JsonResponse
  734. // {
  735. // $session = $request->getSession();
  736. //
  737. // return $this->elasticSearchCatalogueService->getProducts( $session, $request );
  738. // }
  739. /**
  740. * Focus sur un produit
  741. *
  742. *
  743. * @param Request $request
  744. * @param CustomerProduct $customerProduct
  745. *
  746. * @return Response
  747. *
  748. */
  749. public function customerProductFocus(Request $request, CustomerProduct $customerProduct): Response
  750. {
  751. $catalogueType = $request->get('catalogueType');
  752. $catalogues = $this->yamlReader->getShop()[ "catalogues" ] ?? [];
  753. $catalogue = array_filter($catalogues, static function ($catalogue) use ($catalogueType) {
  754. return $catalogue[ 'slug' ] === $catalogueType;
  755. }) ?? [];
  756. $catalogue = array_shift($catalogue);
  757. return $this->render('front/catalogue/productSheet/customer-show.html.twig', [
  758. 'product' => $customerProduct,
  759. 'catalogue' => $catalogue,
  760. ]);
  761. }
  762. /**
  763. * Ajoute un produit dans le panier
  764. *
  765. * @param Request $request
  766. *
  767. * @return JsonResponse
  768. *
  769. * @throws Exception
  770. */
  771. public function addToCart(Request $request): JsonResponse
  772. {
  773. $cart = $this->cartService->load();
  774. $catalogueType = $request->request->get("catalogueType");
  775. $catalogue = $request->request->get("catalogue");
  776. try {
  777. $declinations = $request->request->get("declinations");
  778. if ($declinations && count($declinations) > 0) {
  779. foreach ($declinations as $declination) {
  780. if (isset($declination[ "sku" ])) {
  781. $product = $this->jsonCatalogueService->findProductBySku($declination[ "sku" ]);
  782. $this->cartService->addProduct($cart, $product, $declination[ "quantity" ], $catalogueType);
  783. }
  784. }
  785. } else {
  786. if ($catalogue && isset($catalogue[ 'types' ]) && in_array('customer', $catalogue[ 'types' ], TRUE)) {
  787. $product = $this->em->getRepository(CustomerProduct::class)->findOneBy([
  788. 'id' => $request->request->get("id")
  789. ]);
  790. $sku = $product->getName();
  791. }else{
  792. $sku = $request->request->get('product');
  793. $product = $this->jsonCatalogueService->findProductBySku($sku);
  794. }
  795. if(!$product)
  796. {
  797. return new JsonResponse(
  798. [
  799. "quantity" => 0,
  800. "error" => 500,
  801. 'message' => "Le produit $sku est introuvable",
  802. ], Response::HTTP_OK,
  803. );
  804. }
  805. $cart = $this->cartService->addProduct($cart, $product, $request->request->get('quantityTotal'), $catalogueType);
  806. }
  807. }
  808. catch (Exception $e)
  809. {
  810. return new JsonResponse(
  811. [
  812. "quantity" => 0,
  813. "error" => $e->getCode(),
  814. 'message' => $e->getMessage(),
  815. ], Response::HTTP_OK,
  816. );
  817. }
  818. return new JsonResponse(
  819. [
  820. "quantity" => $cart->totalQuantity(),
  821. "error" => 0,
  822. ], Response::HTTP_OK,
  823. );
  824. }
  825. /**
  826. * @param Request $request
  827. *
  828. * @return JsonResponse
  829. *
  830. * @throws Exception|Throwable
  831. */
  832. public function declinationAjax(Request $request): JsonResponse
  833. {
  834. $idProduct = $request->request->get("product");
  835. $idDeclination = $request->request->get("declination");
  836. $typeDeclination = $request->request->get("type");
  837. $es = $this->catalogSearch;
  838. /** @var Product $product */
  839. $product = $es->find($idProduct);
  840. $declinations = $product->getDeclinations();
  841. //récupère productDeclinationId
  842. $productDeclinationIds = [];
  843. $declinationNextField = [];
  844. $priceManager = $this->priceManager;
  845. if ($idDeclination !== "") {
  846. foreach ($declinations as $declination) {
  847. if ($declination->getId() === $idDeclination) {
  848. $productDeclinationIds[] = $declination->getDeclinationId();
  849. }
  850. }
  851. } else {
  852. foreach ($declinations as $declination) {
  853. $productDeclinationIds[] = $declination->getDeclinationId();
  854. }
  855. }
  856. //vérifie si c'est par lot
  857. $unit = (bool)$product->getCombinationValueByKey("lot_de_x_pieces");
  858. foreach ($declinations as $declination2) {
  859. if ($declination2->getStatus() && in_array(
  860. $declination2->getDeclinationId(),
  861. $productDeclinationIds,
  862. TRUE,
  863. ) && $typeDeclination === $this->validFieldName($declination2->getName())) {
  864. $declinationNextField[ $declination2->getId() ] =
  865. [
  866. "id" => $declination2->getId(),
  867. "name" => $declination2->getName(),
  868. "value" => ucfirst(
  869. $this->get("translator")
  870. ->trans(
  871. $declination2->getValue(),
  872. [],
  873. "declinations",
  874. ),
  875. ),
  876. "sku" => $declination2->getSku(),
  877. "enableStock" => $product->getEnableStockAlert(),
  878. "displayStock" => $this->getParameter('display_available_stock'),
  879. "stock" => $declination2->getStock(),
  880. "price" => $declination2->getPrice(),
  881. "priceHtml" => $priceManager->setProduct($declination2->getProduct())
  882. ->getTransformPriceValuetoHtml(
  883. $declination2->getPrice(),
  884. [
  885. "unit" => $unit,
  886. "display" => TRUE,
  887. ],
  888. ),
  889. "priceHtmlbatchof" => $priceManager->setProduct($declination2->getProduct())
  890. ->getTransformPriceValuetoHtml(
  891. $declination2->getPrice(),
  892. [
  893. "unit" => FALSE,
  894. "display" => FALSE,
  895. ],
  896. ),
  897. "status" => $declination2->getStatus(),
  898. ];
  899. }
  900. }
  901. $declinationNextField = array_values($declinationNextField);
  902. $response = new JsonResponse();
  903. $response->setData($declinationNextField);
  904. return $response;
  905. }
  906. /**
  907. * @param $name
  908. *
  909. * @return string
  910. */
  911. private function validFieldName($name): string
  912. {
  913. return strtolower(preg_replace("/\s+/", "_", $name));
  914. }
  915. /**
  916. * Récupère le stock en temps réel pour un produit
  917. *
  918. * @param Request $request
  919. *
  920. * @return JsonResponse
  921. */
  922. public function getProductStockFromBo(Request $request): JsonResponse
  923. {
  924. $sku = $request->request->get("sku");
  925. $stock = $this->stockService->getProductStockFromBoAndPendingOrder($sku);
  926. return new JsonResponse(
  927. [
  928. 'stock' => $stock,
  929. 'sku' => $sku,
  930. 'displayStockInformation' => TRUE,
  931. ],
  932. Response::HTTP_OK,
  933. );
  934. }
  935. /**
  936. * @param Request $request
  937. *
  938. * @return Response
  939. */
  940. public function requestAvailableAjax(Request $request): Response
  941. {
  942. /** @var User $user */
  943. $user = $this->getUser();
  944. $rpa = (new RequestProductAvailable())
  945. ->setUser($user)
  946. ->setSku($request->get("sku"))
  947. ->setQuantity($request->get("quantity"))
  948. ;
  949. $user->addRequestProductAvailable($rpa);
  950. $this->em->persist($user);
  951. $this->em->persist($rpa);
  952. $this->em->flush();
  953. // @TODO : envoi email
  954. return new Response(NULL, 200);
  955. }
  956. /**
  957. * Traduction des titres des produits
  958. *
  959. * @param Request $request
  960. *
  961. * @return JsonResponse
  962. *
  963. * @throws JsonException
  964. */
  965. public function translateProductTitles(Request $request): JsonResponse
  966. {
  967. if (!$this->translationBase->translationIsEnabled('product_title')) {
  968. return new JsonResponse(['error' => 'La traduction n\'est pas active'], Response::HTTP_OK);
  969. }
  970. $products = $request->get('productTranslation');
  971. if ($products === NULL) {
  972. return new JsonResponse(['error' => 'Aucun produit à traduire'], Response::HTTP_OK);
  973. }
  974. $formattedProducts = [];
  975. foreach ($products as $product) {
  976. $formattedProducts[ $product[ 'sku' ] ] = $product[ 'name' ];
  977. }
  978. $catalogue = $request->get('catalogue');
  979. $locale = $this->translationBase->getLanguageFromCurrentUser();
  980. $translations = $this->catalogueTranslation->getTranslatedTitlesWithSku($formattedProducts, $locale, $catalogue);
  981. return new JsonResponse(['products' => $translations], Response::HTTP_OK);
  982. }
  983. }