custom/plugins/NewsletterSendinblue/src/Subscriber/CartOrderSubscriber.php line 149

Open in your IDE?
  1. <?php
  2. namespace NewsletterSendinblue\Subscriber;
  3. use Monolog\Logger;
  4. use NewsletterSendinblue\Service\BaseSyncService;
  5. use NewsletterSendinblue\Service\Cart\CartEventProducer;
  6. use NewsletterSendinblue\Service\ConfigService;
  7. use NewsletterSendinblue\Service\Constant;
  8. use NewsletterSendinblue\Service\Customer\CustomerProducer;
  9. use NewsletterSendinblue\Service\OrderSyncService;
  10. use NewsletterSendinblue\Traits\HelperTrait;
  11. use Shopware\Core\Checkout\Cart\Event\CheckoutOrderPlacedEvent;
  12. use Shopware\Core\Checkout\Cart\LineItem\LineItem;
  13. use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionDefinition;
  14. use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity;
  15. use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStates;
  16. use Shopware\Core\Checkout\Order\OrderDefinition;
  17. use Shopware\Core\Checkout\Order\OrderEntity;
  18. use Shopware\Core\Checkout\Order\OrderStates;
  19. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  20. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  21. use Shopware\Core\Framework\Uuid\Uuid;
  22. use Shopware\Core\System\SalesChannel\Context\AbstractSalesChannelContextFactory;
  23. use Shopware\Core\System\SalesChannel\Context\SalesChannelContextService;
  24. use Shopware\Core\System\StateMachine\Aggregation\StateMachineState\StateMachineStateEntity;
  25. use Shopware\Core\System\StateMachine\Event\StateMachineTransitionEvent;
  26. use Shopware\Core\Checkout\Order\Aggregate\OrderCustomer\OrderCustomerEntity;
  27. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  28. use Symfony\Component\HttpFoundation\RequestStack;
  29. class CartOrderSubscriber implements EventSubscriberInterface
  30. {
  31.     use HelperTrait;
  32.     const ORDER_OPEN = 'open';
  33.     /**
  34.      * @var AbstractSalesChannelContextFactory
  35.      */
  36.     private $salesChannelContextFactory;
  37.     /**
  38.      * @var ConfigService
  39.     */
  40.     private $configService;
  41.     /**
  42.      * @var CartEventProducer
  43.     */
  44.     private $cartEventProducer;
  45.     /**
  46.      * @var CustomerProducer
  47.      */
  48.     private $customerProducer;
  49.     /**
  50.      * @var EntityRepositoryInterface
  51.      */
  52.     private $systemConfigRepository;
  53.     /**
  54.      * @var EntityRepositoryInterface
  55.      */
  56.     private $orderTransactionRepository;
  57.     /**
  58.      * @var OrderSyncService
  59.      */
  60.     private $orderSyncService;
  61.     /**
  62.      * @var Logger
  63.      */
  64.     private $logger;
  65.     /**
  66.      * @var EntityRepositoryInterface
  67.      */
  68.     private $brevoAbandonedCartRepository;
  69.     /**
  70.      * @var BaseSyncService
  71.      */
  72.     private $productSyncService;
  73.     /**
  74.      * @var RequestStack
  75.      */
  76.     private $requestStack;
  77.     /**
  78.      * @param AbstractSalesChannelContextFactory $salesChannelContextFactory
  79.      * @param ConfigService $configService
  80.      * @param CartEventProducer $cartEventProducer
  81.      * @param CustomerProducer $customerProducer
  82.      * @param EntityRepositoryInterface $systemConfigRepository
  83.      * @param EntityRepositoryInterface $orderTransactionRepository
  84.      * @param OrderSyncService $orderSyncService
  85.      * @param Logger $logger
  86.      * @param EntityRepositoryInterface $brevoAbandonedCartRepository
  87.      * @param BaseSyncService $productSyncService
  88.      * @param RequestStack $requestStack
  89.      */
  90.     public function __construct(
  91.         AbstractSalesChannelContextFactory $salesChannelContextFactory,
  92.         ConfigService                      $configService,
  93.         CartEventProducer                  $cartEventProducer,
  94.         CustomerProducer                   $customerProducer,
  95.         EntityRepositoryInterface          $systemConfigRepository,
  96.         EntityRepositoryInterface          $orderTransactionRepository,
  97.         OrderSyncService                   $orderSyncService,
  98.         Logger                             $logger,
  99.         EntityRepositoryInterface          $brevoAbandonedCartRepository,
  100.         BaseSyncService                    $productSyncService,
  101.         RequestStack                       $requestStack
  102.     )
  103.     {
  104.         $this->salesChannelContextFactory = $salesChannelContextFactory;
  105.         $this->configService = $configService;
  106.         $this->cartEventProducer = $cartEventProducer;
  107.         $this->customerProducer = $customerProducer;
  108.         $this->systemConfigRepository = $systemConfigRepository;
  109.         $this->orderTransactionRepository = $orderTransactionRepository;
  110.         $this->orderSyncService = $orderSyncService;
  111.         $this->logger = $logger;
  112.         $this->brevoAbandonedCartRepository = $brevoAbandonedCartRepository;
  113.         $this->productSyncService = $productSyncService;
  114.         $this->requestStack = $requestStack;
  115.     }
  116.     /**
  117.      * @return string[]
  118.      */
  119.     public static function getSubscribedEvents(): array
  120.     {
  121.         return [
  122.             CheckoutOrderPlacedEvent::class => 'onOrderPlaced',
  123.             StateMachineTransitionEvent::class => 'onStateChanged',
  124.         ];
  125.     }
  126.     /**
  127.      * @param StateMachineTransitionEvent $event
  128.      * @return void
  129.      */
  130.     public function onStateChanged(StateMachineTransitionEvent $event): void
  131.     {
  132.         $orderId = $this->getOrderId($event);
  133.         if (!$orderId) {
  134.             return;
  135.         }
  136.         $order = $this->orderSyncService->getOrder($orderId, $event->getContext());
  137.         if (!$order instanceof OrderEntity) {
  138.             return;
  139.         }
  140.         $salesChannelOptions = [
  141.             SalesChannelContextService::LANGUAGE_ID => $order->getLanguageId(),
  142.             SalesChannelContextService::CURRENCY_ID => $order->getCurrencyId(),
  143.         ];
  144.         $orderCustomer = $order->getOrderCustomer();
  145.         if ($orderCustomer !== null) {
  146.             $salesChannelOptions[SalesChannelContextService::CUSTOMER_ID] = $orderCustomer->getId();
  147.         }
  148.         $salesChannelContext = $this->salesChannelContextFactory->create(
  149.             Uuid::randomHex(),   // unique token
  150.             $order->getSalesChannelId(),
  151.             $salesChannelOptions
  152.         );
  153.         $this->configService->setSalesChannelId($order->getSalesChannelId());
  154.         $connectionId = $this->getAutoSyncConnectionId(
  155.             ConfigService::CONFIG_IS_ORDERS_AUTO_SYNC_ENABLED,
  156.             $salesChannelContext->getContext(),
  157.             $order->getSalesChannelId()
  158.         );
  159.         if (empty($connectionId)) {
  160.             return;
  161.         }
  162.         $entity = $event->getEntityName();
  163.         $toState = $event->getToPlace()->getTechnicalName();
  164.         if ($entity === OrderDefinition::ENTITY_NAME
  165.             && $toState === OrderStates::STATE_CANCELLED
  166.         ) {
  167.             foreach ($order->getLineItems() as $lineItem) {
  168.                 if ($lineItem->getType() !== LineItem::PRODUCT_LINE_ITEM_TYPE) {
  169.                     continue;
  170.                 }
  171.                 if (!empty($lineItem->getProductId())) {
  172.                     $this->productSyncService->sync(
  173.                         $lineItem->getProductId(),
  174.                         $connectionId,
  175.                         $salesChannelContext->getContext()
  176.                     );
  177.                 }
  178.             }
  179.         }
  180.         if (
  181.             ($entity === OrderTransactionDefinition::ENTITY_NAME
  182.                 && in_array($toState, OrderSyncService::PAYMENT_SYNC_STATUSES))
  183.         ) {
  184.             if ($toState === OrderTransactionStates::STATE_CHARGEBACK) {
  185.                 $toState = OrderTransactionStates::STATE_OPEN;
  186.             }
  187.             $this->orderSyncService->sync($order, $connectionId, $toState, $salesChannelContext->getContext(), true);
  188.         }
  189.     }
  190.     /**
  191.      * @param CheckoutOrderPlacedEvent $event
  192.      * @return void
  193.      */
  194.     public function onOrderPlaced(CheckoutOrderPlacedEvent $event): void
  195.     {
  196.         $this->configService->setSalesChannelId($event->getSalesChannelId());
  197.         if ($this->configService->isAutoSyncEnabled()) {
  198.             $this->cartEventProducer->createTransactionalOrder(
  199.                 $event->getOrder(), $event->getContext(), $event->getSalesChannelId()
  200.             );
  201.         }
  202.         // ORDER AUTO SYNC
  203.         $connectionId = $this->getAutoSyncConnectionId(
  204.             ConfigService::CONFIG_IS_ORDERS_AUTO_SYNC_ENABLED,
  205.             $event->getContext(),
  206.             $event->getSalesChannelId()
  207.         );
  208.         if (!empty($connectionId)) {
  209.             $order = $event->getOrder();
  210.             $toState = self::ORDER_OPEN;
  211.             if ($order->getTransactions()->first() instanceof OrderTransactionEntity
  212.                 && $order->getTransactions()->first()->getStateMachineState() instanceof StateMachineStateEntity
  213.             ) {
  214.                 $toState = $order->getTransactions()->first()->getStateMachineState()->getTechnicalName();
  215.             }
  216.             $this->orderSyncService->sync($order, $connectionId, $toState, $event->getContext());
  217.         }
  218.         // PRODUCT AUTO SYNC (stock)
  219.         $productAutoSyncConnectionId = $this->getAutoSyncConnectionId(
  220.             ConfigService::CONFIG_IS_PRODUCTS_AUTO_SYNC_ENABLED,
  221.             $event->getContext(),
  222.             $event->getSalesChannelId()
  223.         );
  224.         if (!empty($productAutoSyncConnectionId)) {
  225.             $order = $event->getOrder();
  226.             foreach ($order->getLineItems() as $lineItem) {
  227.                 if ($lineItem->getType() !== LineItem::PRODUCT_LINE_ITEM_TYPE) {
  228.                     continue;
  229.                 }
  230.                 if (!empty($lineItem->getProductId())) {
  231.                     $this->productSyncService->sync(
  232.                         $lineItem->getProductId(),
  233.                         $productAutoSyncConnectionId,
  234.                         $event->getContext()
  235.                     );
  236.                 }
  237.             }
  238.         }
  239.         if ($this->configService->isAbandonedCartTrackingEnabled()) {
  240.             if ($event->getOrder()->getOrderCustomer() instanceof OrderCustomerEntity) {
  241.                 try {
  242.                     $this->brevoAbandonedCartRepository->delete([
  243.                         ['id' => md5($event->getOrder()->getOrderCustomer()->getEmail())]
  244.                     ], $event->getContext());
  245.                 } catch (\Throwable $e) {
  246.                 }
  247.             }
  248.         }
  249.         // OPT-IN ATTR
  250.         if ($this->configService->isFullCustomerSyncEnabled()
  251.             && $this->requestStack
  252.             && ($request = $this->requestStack->getCurrentRequest())
  253.         ) {
  254.             try {
  255.                 $isCheckoutOptInChecked = $request->request->get('brevoCheckoutConfirmOptin', false);
  256.                 $this->customerProducer->setAdditionalUpdateData([
  257.                     Constant::OPT_IN_ENABLED => $this->configService->isDisplayOptInEnabled() ? 'true' : 'false',
  258.                     Constant::OPT_IN_CHECKED => $isCheckoutOptInChecked ? 'true' : 'false',
  259.                 ]);
  260.                 $this->customerProducer->updateContact($event->getCustomerId(), $event->getContext(), $event->getSalesChannelId());
  261.             } catch (\Throwable $e) {
  262.                 $this->logger->addRecord(Logger::ERROR, $e->getMessage(), ['onOrderPlaced: opt-in attr.']);
  263.             }
  264.         }
  265.     }
  266.     /**
  267.      * @param StateMachineTransitionEvent $event
  268.      * @return string|null
  269.      */
  270.     private function getOrderId(StateMachineTransitionEvent $event): ?string
  271.     {
  272.         $orderId = null;
  273.         switch ($event->getEntityName()) {
  274.             case OrderDefinition::ENTITY_NAME:
  275.                 $orderId = $event->getEntityId();
  276.                 break;
  277.             case OrderTransactionDefinition::ENTITY_NAME:
  278.                 $transactions = $this->orderTransactionRepository->search(new Criteria([$event->getEntityId()]), $event->getContext());
  279.                 if (count($transactions) > 0) {
  280.                     $orderId = $transactions->first()->getOrderId();
  281.                 }
  282.                 break;
  283.         }
  284.         return $orderId;
  285.     }
  286. }