custom/plugins/NetiNextEasyCoupon/src/Subscriber/OrderSubscriber.php line 267

Open in your IDE?
  1. <?php
  2. /**
  3.  * @copyright Copyright (c) 2020, Net Inventors GmbH
  4.  * @category  Shopware
  5.  * @author    mpeters
  6.  */
  7. declare(strict_types=1);
  8. namespace NetInventors\NetiNextEasyCoupon\Subscriber;
  9. use NetInventors\NetiNextEasyCoupon\Core\Content\EasyCoupon\EasyCouponEntity;
  10. use NetInventors\NetiNextEasyCoupon\Core\Framework\Exception\InvalidTypeException;
  11. use NetInventors\NetiNextEasyCoupon\Service\OrderVoucherService;
  12. use NetInventors\NetiNextEasyCoupon\Service\PluginConfigFactory;
  13. use NetInventors\NetiNextEasyCoupon\Service\RedemptionAndChargebackTransactionsService;
  14. use NetInventors\NetiNextEasyCoupon\Service\Repository\VoucherRepository;
  15. use NetInventors\NetiNextEasyCoupon\Service\RequestedDelivery\RequestedDeliveryMailer;
  16. use NetInventors\NetiNextEasyCoupon\Service\RequestedDelivery\RequestedDeliveryProcessor;
  17. use NetInventors\NetiNextEasyCoupon\Struct\PluginConfigStruct;
  18. use Shopware\Core\Checkout\Cart\Event\CheckoutOrderPlacedEvent;
  19. use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemCollection;
  20. use Shopware\Core\Checkout\Order\OrderEntity;
  21. use Shopware\Core\Defaults;
  22. use Shopware\Core\Framework\Api\Context\SalesChannelApiSource;
  23. use Shopware\Core\Framework\Context;
  24. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  25. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenEvent;
  26. use Shopware\Core\System\SalesChannel\Context\AbstractSalesChannelContextFactory;
  27. use Shopware\Core\System\SalesChannel\Context\SalesChannelContextService;
  28. use Shopware\Core\System\SalesChannel\Context\SalesChannelContextServiceInterface;
  29. use Shopware\Core\System\SalesChannel\Context\SalesChannelContextServiceParameters;
  30. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  31. use Shopware\Core\System\SalesChannel\Event\SalesChannelContextSwitchEvent;
  32. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  33. use Shopware\Core\System\StateMachine\Event\StateMachineStateChangeEvent;
  34. use Shopware\Storefront\Page\Checkout\Finish\CheckoutFinishPageLoadedEvent;
  35. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  36. use Symfony\Component\HttpFoundation\RequestStack;
  37. class OrderSubscriber implements EventSubscriberInterface
  38. {
  39.     protected PluginConfigStruct                         $pluginConfig;
  40.     protected OrderVoucherService                        $orderVoucherService;
  41.     private RequestStack                                 $requestStack;
  42.     private SalesChannelContextServiceInterface          $salesChannelContextService;
  43.     private AbstractSalesChannelContextFactory           $salesChannelContextFactory;
  44.     private EntityRepositoryInterface                    $orderRepository;
  45.     protected RedemptionAndChargebackTransactionsService $redemptionAndChargebackTransactionsService;
  46.     private VoucherRepository                            $voucherRepository;
  47.     private RequestedDeliveryProcessor                   $deliveryProcessor;
  48.     private RequestedDeliveryMailer                      $deliveryMailer;
  49.     private PluginConfigFactory                          $pluginConfigFactory;
  50.     private string                                       $swVersion;
  51.     public function __construct(
  52.         PluginConfigStruct                         $pluginConfig,
  53.         OrderVoucherService                        $orderVoucherService,
  54.         RequestStack                               $requestStack,
  55.         SalesChannelContextServiceInterface        $salesChannelContextService,
  56.         AbstractSalesChannelContextFactory         $salesChannelContextFactory,
  57.         EntityRepositoryInterface                  $orderRepository,
  58.         RedemptionAndChargebackTransactionsService $redemptionAndChargebackTransactionsService,
  59.         VoucherRepository                          $voucherRepository,
  60.         RequestedDeliveryProcessor                 $deliveryProcessor,
  61.         RequestedDeliveryMailer                    $deliveryMailer,
  62.         PluginConfigFactory                        $pluginConfigFactory,
  63.         ?string                                    $swVersion
  64.     ) {
  65.         $this->pluginConfig                               $pluginConfig;
  66.         $this->orderVoucherService                        $orderVoucherService;
  67.         $this->requestStack                               $requestStack;
  68.         $this->salesChannelContextService                 $salesChannelContextService;
  69.         $this->orderRepository                            $orderRepository;
  70.         $this->salesChannelContextFactory                 $salesChannelContextFactory;
  71.         $this->redemptionAndChargebackTransactionsService $redemptionAndChargebackTransactionsService;
  72.         $this->voucherRepository                          $voucherRepository;
  73.         $this->deliveryProcessor                          $deliveryProcessor;
  74.         $this->deliveryMailer                             $deliveryMailer;
  75.         $this->pluginConfigFactory                        $pluginConfigFactory;
  76.         $this->swVersion                                  = (string) $swVersion;
  77.     }
  78.     public static function getSubscribedEvents(): array
  79.     {
  80.         return [
  81.             CheckoutOrderPlacedEvent::class => [
  82.                 [ 'createVoucherByLineItems'],
  83.                 [ 'createTransactions'5000 ],
  84.             ],
  85.             'state_machine.order_transaction.state_changed' => [
  86.                 'sendVoucherActivateMailByOrderTransaction',
  87.             ],
  88.             // @TODO Also remove redemption voucher on context switch
  89.             SalesChannelContextSwitchEvent::class           => [
  90.                 'removePurchaseVoucherFromCart',
  91.             ],
  92.             CheckoutFinishPageLoadedEvent::class => [
  93.                 [ 'displayPurchaseVouchers'],
  94.                 [ 'displayVoucherRestValue'5000 ],
  95.                 [ 'removeVoucherCodesFromSession'9000 ],
  96.             ],
  97.             'order_line_item.written'           => 'onOrderLineItemWritten',
  98.             'state_machine.order.state_changed' => 'onOrderStateChange',
  99.         ];
  100.     }
  101.     /**
  102.      * @param StateMachineStateChangeEvent $event
  103.      *
  104.      * @return void
  105.      * @throws InvalidTypeException
  106.      */
  107.     public function onOrderStateChange(StateMachineStateChangeEvent $event): void
  108.     {
  109.         $order               $this->getOrder($event->getTransition()->getEntityId(), $event->getContext());
  110.         $pluginConfigOfOrder $this->pluginConfigFactory->create($order->getSalesChannelId());
  111.         if (!$pluginConfigOfOrder->isActive()) {
  112.             return;
  113.         }
  114.         $this->redemptionAndChargebackTransactionsService->chargebackTransaction(
  115.             $order,
  116.             $event->getContext(),
  117.             \in_array(
  118.                 $event->getNextState()->getId(),
  119.                 $pluginConfigOfOrder->getVoucherCancelOrderStatus(),
  120.                 true
  121.             )
  122.         );
  123.     }
  124.     protected function getOrder(string $orderIdContext $context): ?OrderEntity
  125.     {
  126.         $criteria = new Criteria([ $orderId ]);
  127.         $criteria->addAssociation('lineItems');
  128.         return $this->orderRepository->search($criteria$context)->first();
  129.     }
  130.     public function onOrderLineItemWritten(EntityWrittenEvent $event): void
  131.     {
  132.         // if the live version of the order_line_item is updated, the associated transaction should be updated, too.
  133.         foreach ($event->getWriteResults() as $writeResult) {
  134.             $existence $writeResult->getExistence();
  135.             if (null === $existence || !$existence->exists()) {
  136.                 continue; // we don't care about deleted items here
  137.             }
  138.             $payload $writeResult->getPayload();
  139.             if ($payload['versionId'] !== Defaults::LIVE_VERSION) {
  140.                 continue; // we only update the transaction if the live version order_line_item is updated
  141.             }
  142.             $this->orderVoucherService->updateTransactionValueFromOrderLineItem(
  143.                 $writeResult->getPrimaryKey(),
  144.                 $event->getContext()
  145.             );
  146.         }
  147.     }
  148.     public function createVoucherByLineItems(CheckoutOrderPlacedEvent $event): void
  149.     {
  150.         if (!$this->pluginConfig->isActive()) {
  151.             return;
  152.         }
  153.         if (!$event->getOrder()->getLineItems() instanceof OrderLineItemCollection) {
  154.             return;
  155.         }
  156.         if (!$this->isProperCheckoutOrderPlacedEvent($event)) {
  157.             return;
  158.         }
  159.         $filteredItems $this->orderVoucherService->filterLineItemsByEnteredValued($event->getOrder()->getLineItems());
  160.         if ([] === $filteredItems->getElements()) {
  161.             return;
  162.         }
  163.         $salesChannelContext $this->requestStack->getCurrentRequest()->get('sw-sales-channel-context');
  164.         if (!$salesChannelContext instanceof SalesChannelContext) {
  165.             // We are presumably in an administration context
  166.             $salesChannelId $this->requestStack->getCurrentRequest()->get('salesChannelId');
  167.             $contextToken   $this->requestStack->getCurrentRequest()->headers->get('sw-context-token');
  168.             if ($salesChannelId && $contextToken) {
  169.                 $parameters          = new SalesChannelContextServiceParameters($salesChannelId$contextToken);
  170.                 $salesChannelContext $this->salesChannelContextService->get($parameters);
  171.             }
  172.         }
  173.         if (!$salesChannelContext instanceof SalesChannelContext) {
  174.             // No Sales Channel provided, so we have to create it on our own
  175.             $salesChannelContext $this->salesChannelContextFactory->create(
  176.                 '',
  177.                 $event->getOrder()->getSalesChannelId(),
  178.                 [
  179.                     SalesChannelContextService::LANGUAGE_ID => $event->getOrder()->getLanguageId(),
  180.                     SalesChannelContextService::CURRENCY_ID => $event->getOrder()->getCurrencyId(),
  181.                 ]
  182.             );
  183.         }
  184.         $this->orderVoucherService->createVoucherByLineItems(
  185.             $event->getOrder()->getLineItems(),
  186.             $event->getContext(),
  187.             $salesChannelContext
  188.         );
  189.     }
  190.     public function displayPurchaseVouchers(CheckoutFinishPageLoadedEvent $event): void
  191.     {
  192.         if (!$this->pluginConfig->isActive() || !$this->pluginConfig->isVouchersInfoAtCheckoutActive()) {
  193.             return;
  194.         }
  195.         $this->orderVoucherService->addPurchaseVouchersToCheckoutConfirmPage($event->getPage(), $event->getContext());
  196.     }
  197.     public function displayVoucherRestValue(CheckoutFinishPageLoadedEvent $event): void
  198.     {
  199.         if (!$this->pluginConfig->isActive() || !$this->pluginConfig->isVouchersInfoAtCheckoutActive()) {
  200.             return;
  201.         }
  202.         $this->orderVoucherService->displayVoucherRestValueAtCheckoutConfirmPage(
  203.             $event->getPage(),
  204.             $event->getContext(),
  205.             $event->getSalesChannelContext()
  206.         );
  207.     }
  208.     public function createTransactions(CheckoutOrderPlacedEvent $event): void
  209.     {
  210.         if (!$this->pluginConfig->isActive()) {
  211.             return;
  212.         }
  213.         if (!$this->isProperCheckoutOrderPlacedEvent($event)) {
  214.             return;
  215.         }
  216.         $this->orderVoucherService->createTransactionsForRedeemedVouchers($event->getOrder(), $event->getContext());
  217.     }
  218.     public function sendVoucherActivateMailByOrderTransaction(StateMachineStateChangeEvent $event): void
  219.     {
  220.         if (
  221.             !$this->pluginConfig->isActive()
  222.             || !\in_array($event->getNextState()->getId(), $this->pluginConfig->getVoucherActivatePaymentStatus(), true)
  223.         ) {
  224.             return;
  225.         }
  226.         $context $event->getContext();
  227.         /** @var array<string, EasyCouponEntity> $orderedVouchers */
  228.         $orderedVouchers $this->voucherRepository->collectOrderedVouchersByTransactionId(
  229.             $event->getTransition()->getEntityId(),
  230.             $context
  231.         )->getElements();
  232.         if ([] === $orderedVouchers) {
  233.             return;
  234.         }
  235.         $order $this->orderVoucherService->getOrderFromTransactionId($event->getTransition()->getEntityId(), $context);
  236.         if ($order instanceof OrderEntity) {
  237.             $orderPluginConfig $this->pluginConfigFactory->create($order->getSalesChannelId());
  238.         } else {
  239.             $orderPluginConfig $this->pluginConfig;
  240.         }
  241.         if ($orderPluginConfig->isActivationMailRequired()) {
  242.             // Send voucher activation mails
  243.             $this->orderVoucherService->sendVoucherActivateMailByOrderTransaction(
  244.                 $orderedVouchers,
  245.                 $context
  246.             );
  247.         }
  248.         // Send requested delivery mails
  249.         $easyCouponIds array_map(static function(EasyCouponEntity $voucher) {
  250.             return $voucher->getId();
  251.         }, $orderedVouchers);
  252.         $deliveries $this->deliveryProcessor->collectDeliveries($context$easyCouponIds);
  253.         if (null === $deliveries) {
  254.             return;
  255.         }
  256.         $this->deliveryMailer->deliverVouchers($deliveries$context);
  257.     }
  258.     public function removePurchaseVoucherFromCart(SalesChannelContextSwitchEvent $event): void
  259.     {
  260.         if (!$this->pluginConfig->isActive()) {
  261.             return;
  262.         }
  263.         $currencyId $event->getRequestDataBag()->get('currencyId');
  264.         if (!(\is_string($currencyId) && '' !== $currencyId)) {
  265.             return;
  266.         }
  267.         $this->orderVoucherService->removePurchaseVoucherFromCart($event->getSalesChannelContext());
  268.     }
  269.     public function removeVoucherCodesFromSession(CheckoutFinishPageLoadedEvent $event): void
  270.     {
  271.         if (!$this->pluginConfig->isActive()) {
  272.             return;
  273.         }
  274.         $event->getRequest()->getSession()->remove('EasyCouponVoucherCodes');
  275.     }
  276.     private function isProperCheckoutOrderPlacedEvent(CheckoutOrderPlacedEvent $event): bool
  277.     {
  278.         $contextSource $event->getContext()->getSource();
  279.         $contextStates $event->getContext()->getStates();
  280.         return (
  281.             \version_compare($this->swVersion'6.4.11.0''<')
  282.             || !$contextSource instanceof SalesChannelApiSource
  283.             || (\in_array('checkout-order-route'$contextStatestrue))
  284.         );
  285.     }
  286. }