core/lib/Thelia/Action/Order.php line 106

  1. <?php
  2. /*
  3.  * This file is part of the Thelia package.
  4.  * http://www.thelia.net
  5.  *
  6.  * (c) OpenStudio <info@thelia.net>
  7.  *
  8.  * For the full copyright and license information, please view the LICENSE
  9.  * file that was distributed with this source code.
  10.  */
  11. namespace Thelia\Action;
  12. use Propel\Runtime\Propel;
  13. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  14. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  15. use Symfony\Component\HttpFoundation\RequestStack;
  16. use Thelia\Core\Event\Order\GetStockUpdateOperationOnOrderStatusChangeEvent;
  17. use Thelia\Core\Event\Order\OrderAddressEvent;
  18. use Thelia\Core\Event\Order\OrderEvent;
  19. use Thelia\Core\Event\Order\OrderManualEvent;
  20. use Thelia\Core\Event\Order\OrderPaymentEvent;
  21. use Thelia\Core\Event\Order\OrderPayTotalEvent;
  22. use Thelia\Core\Event\Product\VirtualProductOrderHandleEvent;
  23. use Thelia\Core\Event\TheliaEvents;
  24. use Thelia\Core\HttpFoundation\Request;
  25. use Thelia\Core\Security\SecurityContext;
  26. use Thelia\Core\Security\User\UserInterface;
  27. use Thelia\Exception\TheliaProcessException;
  28. use Thelia\Log\Tlog;
  29. use Thelia\Mailer\MailerFactory;
  30. use Thelia\Model\AddressQuery;
  31. use Thelia\Model\Cart as CartModel;
  32. use Thelia\Model\ConfigQuery;
  33. use Thelia\Model\Currency as CurrencyModel;
  34. use Thelia\Model\Lang as LangModel;
  35. use Thelia\Model\Map\OrderTableMap;
  36. use Thelia\Model\ModuleQuery;
  37. use Thelia\Model\Order as ModelOrder;
  38. use Thelia\Model\Order as OrderModel;
  39. use Thelia\Model\OrderAddress;
  40. use Thelia\Model\OrderAddressQuery;
  41. use Thelia\Model\OrderProduct;
  42. use Thelia\Model\OrderProductAttributeCombination;
  43. use Thelia\Model\OrderProductTax;
  44. use Thelia\Model\OrderStatusQuery;
  45. use Thelia\Model\OrderVersionQuery;
  46. use Thelia\Model\ProductI18n;
  47. use Thelia\Model\ProductSaleElements;
  48. use Thelia\Model\ProductSaleElementsQuery;
  49. use Thelia\Model\TaxRuleI18n;
  50. use Thelia\Tools\I18n;
  51. /**
  52.  * Class Order.
  53.  *
  54.  * @author Etienne Roudeix <eroudeix@openstudio.fr>
  55.  * @author Franck Allimant <thelia@cqfdev.fr>
  56.  */
  57. class Order extends BaseAction implements EventSubscriberInterface
  58. {
  59.     /** @var RequestStack */
  60.     protected $requestStack;
  61.     /** @var MailerFactory */
  62.     protected $mailer;
  63.     /** @var SecurityContext */
  64.     protected $securityContext;
  65.     public function __construct(RequestStack $requestStackMailerFactory $mailerSecurityContext $securityContext)
  66.     {
  67.         $this->requestStack $requestStack;
  68.         $this->mailer $mailer;
  69.         $this->securityContext $securityContext;
  70.     }
  71.     public function setDeliveryAddress(OrderEvent $event): void
  72.     {
  73.         $order $event->getOrder();
  74.         $order->setChoosenDeliveryAddress($event->getDeliveryAddress());
  75.         $event->setOrder($order);
  76.     }
  77.     public function setDeliveryModule(OrderEvent $event): void
  78.     {
  79.         $order $event->getOrder();
  80.         $deliveryModuleId $event->getDeliveryModule();
  81.         $order->setDeliveryModuleId($deliveryModuleId);
  82.         // Reset postage cost if the delivery module had been removed
  83.         if ($deliveryModuleId <= 0) {
  84.             $order->setPostage(0);
  85.             $order->setPostageTax(0);
  86.             $order->setPostageTaxRuleTitle(null);
  87.         }
  88.         $event->setOrder($order);
  89.     }
  90.     public function setPostage(OrderEvent $event): void
  91.     {
  92.         $order $event->getOrder();
  93.         $order->setPostage($event->getPostage());
  94.         $order->setPostageTax($event->getPostageTax());
  95.         $order->setPostageTaxRuleTitle($event->getPostageTaxRuleTitle());
  96.         $event->setOrder($order);
  97.     }
  98.     public function setInvoiceAddress(OrderEvent $event): void
  99.     {
  100.         $order $event->getOrder();
  101.         $order->setChoosenInvoiceAddress($event->getInvoiceAddress());
  102.         $event->setOrder($order);
  103.     }
  104.     public function setPaymentModule(OrderEvent $event): void
  105.     {
  106.         $order $event->getOrder();
  107.         $order->setPaymentModuleId($event->getPaymentModule());
  108.         $event->setOrder($order);
  109.     }
  110.     /**
  111.      * @param bool $unusedArgument           deprecated argument. Will be removed in 2.5
  112.      * @param bool $useOrderDefinedAddresses if true, the delivery and invoice OrderAddresses will be used instead of creating new OrderAdresses using Order::getChoosenXXXAddress()
  113.      *
  114.      * @throws \Exception
  115.      * @throws \Propel\Runtime\Exception\PropelException
  116.      *
  117.      * @return ModelOrder
  118.      */
  119.     protected function createOrder(
  120.         EventDispatcherInterface $dispatcher,
  121.         ModelOrder $sessionOrder,
  122.         CurrencyModel $currency,
  123.         LangModel $lang,
  124.         CartModel $cart,
  125.         UserInterface $customer,
  126.         $unusedArgument null,
  127.         $useOrderDefinedAddresses false
  128.     ) {
  129.         $con Propel::getConnection(
  130.             OrderTableMap::DATABASE_NAME
  131.         );
  132.         $con->beginTransaction();
  133.         $placedOrder $sessionOrder->copy();
  134.         // Be sure to create a brand new order, as copy raises the modified flag for all fields
  135.         // and will also copy order reference and id.
  136.         $placedOrder->setId(null)->setRef(null)->setNew(true);
  137.         // Dates should be marked as not updated so that Propel will update them.
  138.         $placedOrder->resetModified(OrderTableMap::COL_CREATED_AT);
  139.         $placedOrder->resetModified(OrderTableMap::COL_UPDATED_AT);
  140.         $placedOrder->resetModified(OrderTableMap::COL_VERSION_CREATED_AT);
  141.         $cartItems $cart->getCartItems();
  142.         /* fulfill order */
  143.         $placedOrder->setCustomerId($customer->getId());
  144.         $placedOrder->setCurrencyId($currency->getId());
  145.         $placedOrder->setCurrencyRate($currency->getRate());
  146.         $placedOrder->setLangId($lang->getId());
  147.         if ($useOrderDefinedAddresses) {
  148.             $taxCountry =
  149.                 OrderAddressQuery::create()
  150.                     ->findPk($placedOrder->getDeliveryOrderAddressId())
  151.                     ->getCountry()
  152.             ;
  153.         } else {
  154.             $deliveryAddress AddressQuery::create()->findPk($sessionOrder->getChoosenDeliveryAddress());
  155.             $invoiceAddress AddressQuery::create()->findPk($sessionOrder->getChoosenInvoiceAddress());
  156.             /* hard save the delivery and invoice addresses */
  157.             $deliveryOrderAddress = new OrderAddress();
  158.             $deliveryOrderAddress
  159.                 ->setCustomerTitleId($deliveryAddress->getTitleId())
  160.                 ->setCompany($deliveryAddress->getCompany())
  161.                 ->setFirstname($deliveryAddress->getFirstname())
  162.                 ->setLastname($deliveryAddress->getLastname())
  163.                 ->setAddress1($deliveryAddress->getAddress1())
  164.                 ->setAddress2($deliveryAddress->getAddress2())
  165.                 ->setAddress3($deliveryAddress->getAddress3())
  166.                 ->setZipcode($deliveryAddress->getZipcode())
  167.                 ->setCity($deliveryAddress->getCity())
  168.                 ->setPhone($deliveryAddress->getPhone())
  169.                 ->setCellphone($deliveryAddress->getCellphone())
  170.                 ->setCountryId($deliveryAddress->getCountryId())
  171.                 ->setStateId($deliveryAddress->getStateId())
  172.                 ->save($con);
  173.             $invoiceOrderAddress = new OrderAddress();
  174.             $invoiceOrderAddress
  175.                 ->setCustomerTitleId($invoiceAddress->getTitleId())
  176.                 ->setCompany($invoiceAddress->getCompany())
  177.                 ->setFirstname($invoiceAddress->getFirstname())
  178.                 ->setLastname($invoiceAddress->getLastname())
  179.                 ->setAddress1($invoiceAddress->getAddress1())
  180.                 ->setAddress2($invoiceAddress->getAddress2())
  181.                 ->setAddress3($invoiceAddress->getAddress3())
  182.                 ->setZipcode($invoiceAddress->getZipcode())
  183.                 ->setCity($invoiceAddress->getCity())
  184.                 ->setPhone($invoiceAddress->getPhone())
  185.                 ->setCellphone($invoiceAddress->getCellphone())
  186.                 ->setCountryId($invoiceAddress->getCountryId())
  187.                 ->setStateId($deliveryAddress->getStateId())
  188.                 ->save($con);
  189.             $placedOrder->setDeliveryOrderAddressId($deliveryOrderAddress->getId());
  190.             $placedOrder->setInvoiceOrderAddressId($invoiceOrderAddress->getId());
  191.             $taxCountry $deliveryAddress->getCountry();
  192.         }
  193.         $placedOrder->setStatusId(
  194.             OrderStatusQuery::getNotPaidStatus()->getId()
  195.         );
  196.         $placedOrder->setCartId($cart->getId());
  197.         /* memorize discount */
  198.         $placedOrder->setDiscount(
  199.             $cart->getDiscount()
  200.         );
  201.         $placedOrder->save($con);
  202.         $manageStock $placedOrder->isStockManagedOnOrderCreation($dispatcher);
  203.         /* fulfill order_products and decrease stock */
  204.         foreach ($cartItems as $cartItem) {
  205.             $product $cartItem->getProduct();
  206.             /* get translation */
  207.             /** @var ProductI18n $productI18n */
  208.             $productI18n I18n::forceI18nRetrieving($lang->getLocale(), 'Product'$product->getId());
  209.             $pse $cartItem->getProductSaleElements();
  210.             // get the virtual document path
  211.             $virtualDocumentEvent = new VirtualProductOrderHandleEvent($placedOrder$pse->getId());
  212.             // essentially used for virtual product. modules that handles virtual product can
  213.             // allow the use of stock even for virtual products
  214.             $useStock true;
  215.             $virtual 0;
  216.             // if the product is virtual, dispatch an event to collect information
  217.             if ($product->getVirtual() === 1) {
  218.                 $dispatcher->dispatch($virtualDocumentEventTheliaEvents::VIRTUAL_PRODUCT_ORDER_HANDLE);
  219.                 $useStock $virtualDocumentEvent->isUseStock();
  220.                 $virtual $virtualDocumentEvent->isVirtual() ? 0;
  221.             }
  222.             /* check still in stock */
  223.             if ($cartItem->getQuantity() > $pse->getQuantity()
  224.                 && true === ConfigQuery::checkAvailableStock()
  225.                 && $useStock) {
  226.                 throw new TheliaProcessException('Not enough stock'TheliaProcessException::CART_ITEM_NOT_ENOUGH_STOCK$cartItem);
  227.             }
  228.             if ($useStock && $manageStock) {
  229.                 /* decrease stock for non virtual product */
  230.                 $allowNegativeStock = (int) ConfigQuery::read('allow_negative_stock'0);
  231.                 $newStock $pse->getQuantity() - $cartItem->getQuantity();
  232.                 // Forbid negative stock
  233.                 if ($newStock && === $allowNegativeStock) {
  234.                     $newStock 0;
  235.                 }
  236.                 $pse->setQuantity(
  237.                     $newStock
  238.                 );
  239.                 $pse->save($con);
  240.             }
  241.             /* get tax */
  242.             /** @var TaxRuleI18n $taxRuleI18n */
  243.             $taxRuleI18n I18n::forceI18nRetrieving($lang->getLocale(), 'TaxRule'$product->getTaxRuleId());
  244.             $taxDetail $product->getTaxRule()->getTaxDetail(
  245.                 $product,
  246.                 $taxCountry,
  247.                 $cartItem->getPrice(),
  248.                 $cartItem->getPromoPrice(),
  249.                 $lang->getLocale()
  250.             );
  251.             $orderProduct = new OrderProduct();
  252.             $orderProduct
  253.                 ->setOrderId($placedOrder->getId())
  254.                 ->setProductRef($product->getRef())
  255.                 ->setProductSaleElementsRef($pse->getRef())
  256.                 ->setProductSaleElementsId($pse->getId())
  257.                 ->setTitle($productI18n->getTitle())
  258.                 ->setChapo($productI18n->getChapo())
  259.                 ->setDescription($productI18n->getDescription())
  260.                 ->setPostscriptum($productI18n->getPostscriptum())
  261.                 ->setVirtual($virtual)
  262.                 ->setVirtualDocument($virtualDocumentEvent->getPath())
  263.                 ->setQuantity($cartItem->getQuantity())
  264.                 ->setPrice($cartItem->getPrice())
  265.                 ->setPromoPrice($cartItem->getPromoPrice())
  266.                 ->setWasNew($pse->getNewness())
  267.                 ->setWasInPromo($cartItem->getPromo())
  268.                 ->setWeight($pse->getWeight())
  269.                 ->setTaxRuleTitle($taxRuleI18n->getTitle())
  270.                 ->setTaxRuleDescription($taxRuleI18n->getDescription())
  271.                 ->setEanCode($pse->getEanCode())
  272.                 ->setCartItemId($cartItem->getId())
  273.                 ->save($con)
  274.             ;
  275.             /* fulfill order_product_tax */
  276.             /** @var OrderProductTax $tax */
  277.             foreach ($taxDetail as $tax) {
  278.                 $tax->setOrderProductId($orderProduct->getId());
  279.                 $tax->save($con);
  280.             }
  281.             /* fulfill order_attribute_combination and decrease stock */
  282.             foreach ($pse->getAttributeCombinations() as $attributeCombination) {
  283.                 /** @var \Thelia\Model\Attribute $attribute */
  284.                 $attribute I18n::forceI18nRetrieving($lang->getLocale(), 'Attribute'$attributeCombination->getAttributeId());
  285.                 /** @var \Thelia\Model\AttributeAv $attributeAv */
  286.                 $attributeAv I18n::forceI18nRetrieving($lang->getLocale(), 'AttributeAv'$attributeCombination->getAttributeAvId());
  287.                 $orderAttributeCombination = new OrderProductAttributeCombination();
  288.                 $orderAttributeCombination
  289.                     ->setOrderProductId($orderProduct->getId())
  290.                     ->setAttributeTitle($attribute->getTitle())
  291.                     ->setAttributeChapo($attribute->getChapo())
  292.                     ->setAttributeDescription($attribute->getDescription())
  293.                     ->setAttributePostscriptum($attribute->getPostscriptum())
  294.                     ->setAttributeAvTitle($attributeAv->getTitle())
  295.                     ->setAttributeAvChapo($attributeAv->getChapo())
  296.                     ->setAttributeAvDescription($attributeAv->getDescription())
  297.                     ->setAttributeAvPostscriptum($attributeAv->getPostscriptum())
  298.                     ->save($con);
  299.             }
  300.         }
  301.         $con->commit();
  302.         return $placedOrder;
  303.     }
  304.     /**
  305.      * Create an order outside of the front-office context, e.g. manually from the back-office.
  306.      *
  307.      * @throws \Exception
  308.      * @throws \Propel\Runtime\Exception\PropelException
  309.      */
  310.     public function createManual(OrderManualEvent $event$eventNameEventDispatcherInterface $dispatcher): void
  311.     {
  312.         $event->setPlacedOrder(
  313.             $this->createOrder(
  314.                 $dispatcher,
  315.                 $event->getOrder(),
  316.                 $event->getCurrency(),
  317.                 $event->getLang(),
  318.                 $event->getCart(),
  319.                 $event->getCustomer(),
  320.                 null,
  321.                 $event->getUseOrderDefinedAddresses()
  322.             )
  323.         );
  324.         $event->setOrder(new OrderModel());
  325.     }
  326.     /**
  327.      * @throws \Thelia\Exception\TheliaProcessException
  328.      * @throws \Exception
  329.      * @throws \Propel\Runtime\Exception\PropelException
  330.      */
  331.     public function create(OrderEvent $event$eventNameEventDispatcherInterface $dispatcher): void
  332.     {
  333.         $session $this->getSession();
  334.         $order $event->getOrder();
  335.         $paymentModule ModuleQuery::create()->findPk($order->getPaymentModuleId());
  336.         $placedOrder $this->createOrder(
  337.             $dispatcher,
  338.             $event->getOrder(),
  339.             $session->getCurrency(),
  340.             $session->getLang(),
  341.             $session->getSessionCart($dispatcher),
  342.             $this->securityContext->getCustomerUser()
  343.         );
  344.         $dispatcher->dispatch(new OrderEvent($placedOrder), TheliaEvents::ORDER_BEFORE_PAYMENT);
  345.         /* but memorize placed order */
  346.         $event->setOrder(new OrderModel());
  347.         $event->setPlacedOrder($placedOrder);
  348.         /* call pay method */
  349.         $payEvent = new OrderPaymentEvent($placedOrder);
  350.         $dispatcher->dispatch($payEventTheliaEvents::MODULE_PAY);
  351.         if ($payEvent->hasResponse()) {
  352.             $event->setResponse($payEvent->getResponse());
  353.         }
  354.     }
  355.     public function orderBeforePayment(OrderEvent $event$eventNameEventDispatcherInterface $dispatcher): void
  356.     {
  357.         $dispatcher->dispatch(clone $eventTheliaEvents::ORDER_SEND_CONFIRMATION_EMAIL);
  358.         $dispatcher->dispatch(clone $eventTheliaEvents::ORDER_SEND_NOTIFICATION_EMAIL);
  359.     }
  360.     /**
  361.      * Clear the cart and the order in the customer session once the order is placed,
  362.      * and the payment performed.
  363.      */
  364.     public function orderCartClear(/* @noinspection PhpUnusedParameterInspection */ OrderEvent $event$eventNameEventDispatcherInterface $dispatcher): void
  365.     {
  366.         // Empty cart and clear current order
  367.         $session $this->getSession();
  368.         $session->clearSessionCart($dispatcher);
  369.         $session->setOrder(new OrderModel());
  370.     }
  371.     /**
  372.      * @throws \Exception if the message cannot be loaded
  373.      */
  374.     public function sendConfirmationEmail(OrderEvent $event): void
  375.     {
  376.         $this->mailer->sendEmailToCustomer(
  377.             'order_confirmation',
  378.             $event->getOrder()->getCustomer(),
  379.             [
  380.                 'order_id' => $event->getOrder()->getId(),
  381.                 'order_ref' => $event->getOrder()->getRef(),
  382.             ]
  383.         );
  384.     }
  385.     /**
  386.      * @throws \Exception if the message cannot be loaded
  387.      */
  388.     public function sendNotificationEmail(OrderEvent $event): void
  389.     {
  390.         $this->mailer->sendEmailToShopManagers(
  391.             'order_notification',
  392.             [
  393.                 'order_id' => $event->getOrder()->getId(),
  394.                 'order_ref' => $event->getOrder()->getRef(),
  395.             ]
  396.         );
  397.     }
  398.     /**
  399.      * @throws \Exception
  400.      * @throws \Propel\Runtime\Exception\PropelException
  401.      */
  402.     public function updateStatus(OrderEvent $event$eventNameEventDispatcherInterface $dispatcher): void
  403.     {
  404.         $order $event->getOrder();
  405.         $newStatus $event->getStatus();
  406.         $con Propel::getConnection(OrderTableMap::DATABASE_NAME);
  407.         // Prevent partial stock update on status change.
  408.         $con->beginTransaction();
  409.         try {
  410.             $this->updateQuantity($order$newStatus$dispatcher);
  411.             $order->setStatusId($newStatus)->save();
  412.             $event->setOrder($order);
  413.             $con->commit();
  414.         } catch (\Exception $ex) {
  415.             $con->rollBack();
  416.             throw $ex;
  417.         }
  418.     }
  419.     /**
  420.      * Check if a stock update is required on order products for a given order status change, and compute if
  421.      * the stock should be decreased or increased.
  422.      *
  423.      * @throws \Propel\Runtime\Exception\PropelException
  424.      */
  425.     public function getStockUpdateOnOrderStatusChange(GetStockUpdateOperationOnOrderStatusChangeEvent $event$eventNameEventDispatcherInterface $dispatcher): void
  426.     {
  427.         // The order
  428.         $order $event->getOrder();
  429.         // The new order status
  430.         $newStatus $event->getNewOrderStatus();
  431.         if ($newStatus->getId() !== $order->getStatusId()) {
  432.             // We have to change the stock in the following cases :
  433.             // 1) The order is currently paid, and will become unpaid (get products back in stock unconditionnaly)
  434.             // 2) The order is currently unpaid, and will become paid (remove products from stock, except if was done at order creation $manageStockOnCreation == false)
  435.             // 3) The order is currently NOT PAID, and will become canceled or the like (get products back in stock if it was done at order creation $manageStockOnCreation == true)
  436.             // We consider the ManageStockOnCreation flag only if the order status as not yet changed.
  437.             // Count distinct order statuses (e.g. NOT_PAID to something else) in the order version table.
  438.             if (OrderVersionQuery::create()->groupByStatusId()->filterById($order->getId())->count() > 1) {
  439.                 // A status change occured. Ignore $manageStockOnCreation
  440.                 $manageStockOnCreation false;
  441.             } else {
  442.                 // A status has not yet occured. Consider the ManageStockOnCreation flag
  443.                 $manageStockOnCreation $order->isStockManagedOnOrderCreation($dispatcher);
  444.             }
  445.             if (($order->isPaid(false) && $newStatus->isNotPaid(false)) // Case 1
  446.                 ||
  447.                 ($order->isNotPaid(true) && $newStatus->isNotPaid(false) && $manageStockOnCreation === true// Case 3
  448.             ) {
  449.                 $event->setOperation($event::INCREASE_STOCK);
  450.             }
  451.             if ($order->isNotPaid(false// Case 2
  452.                 &&
  453.                 $newStatus->isPaid(false)
  454.                 &&
  455.                 $manageStockOnCreation === false) {
  456.                 $event->setOperation($event::DECREASE_STOCK);
  457.             }
  458.             Tlog::getInstance()->addInfo(
  459.                 'Checking stock operation for status change of order : '.$order->getRef()
  460.                 .', version: '.$order->getVersion()
  461.                 .', manageStockOnCreation: '.($manageStockOnCreation 1)
  462.                 .', paid:'.($order->isPaid(false) ? 0)
  463.                 .', is not paid:'.($order->isNotPaid(false) ? 0)
  464.                 .', new status paid:'.($newStatus->isPaid(false) ? 0)
  465.                 .', new status is not paid:'.($newStatus->isNotPaid(false) ? 0)
  466.                 .' = operation: '.$event->getOperation()
  467.             );
  468.         }
  469.     }
  470.     /**
  471.      * Update order products stock after an order status change.
  472.      *
  473.      * @param int $newStatus the new status ID
  474.      *
  475.      * @throws \Exception
  476.      * @throws \Propel\Runtime\Exception\PropelException
  477.      */
  478.     protected function updateQuantity(ModelOrder $order$newStatusEventDispatcherInterface $dispatcher): void
  479.     {
  480.         if ($newStatus !== $order->getStatusId()) {
  481.             if (null !== $newStatusModel OrderStatusQuery::create()->findPk($newStatus)) {
  482.                 $operationEvent = new GetStockUpdateOperationOnOrderStatusChangeEvent($order$newStatusModel);
  483.                 $dispatcher->dispatch(
  484.                     $operationEvent,
  485.                     TheliaEvents::ORDER_GET_STOCK_UPDATE_OPERATION_ON_ORDER_STATUS_CHANGE
  486.                 );
  487.                 if ($operationEvent->getOperation() !== $operationEvent::DO_NOTHING) {
  488.                     $orderProductList $order->getOrderProducts();
  489.                     /** @var OrderProduct $orderProduct */
  490.                     foreach ($orderProductList as $orderProduct) {
  491.                         $productSaleElementsId $orderProduct->getProductSaleElementsId();
  492.                         /** @var ProductSaleElements $productSaleElements */
  493.                         if (null !== $productSaleElements ProductSaleElementsQuery::create()->findPk($productSaleElementsId)) {
  494.                             $offset 0;
  495.                             if ($operationEvent->getOperation() == $operationEvent::INCREASE_STOCK) {
  496.                                 $offset $orderProduct->getQuantity();
  497.                             } elseif ($operationEvent->getOperation() == $operationEvent::DECREASE_STOCK) {
  498.                                 /* Check if we have enough stock */
  499.                                 if ($orderProduct->getQuantity() > $productSaleElements->getQuantity() && true === ConfigQuery::checkAvailableStock()) {
  500.                                     throw new TheliaProcessException($productSaleElements->getRef().' : Not enough stock 2');
  501.                                 }
  502.                                 $offset = -$orderProduct->getQuantity();
  503.                             }
  504.                             Tlog::getInstance()->addError('Product stock: '.$productSaleElements->getQuantity().' -> '.($productSaleElements->getQuantity() + $offset));
  505.                             $productSaleElements
  506.                                 ->setQuantity($productSaleElements->getQuantity() + $offset)
  507.                                 ->save();
  508.                         }
  509.                     }
  510.                 }
  511.             }
  512.         }
  513.     }
  514.     /**
  515.      * @throws \Propel\Runtime\Exception\PropelException
  516.      */
  517.     public function updateDeliveryRef(OrderEvent $event): void
  518.     {
  519.         $order $event->getOrder();
  520.         $order->setDeliveryRef($event->getDeliveryRef())->save();
  521.         $event->setOrder($order);
  522.     }
  523.     /**
  524.      * @throws \Propel\Runtime\Exception\PropelException
  525.      */
  526.     public function updateTransactionRef(OrderEvent $event): void
  527.     {
  528.         $order $event->getOrder();
  529.         $order->setTransactionRef($event->getTransactionRef())->save();
  530.         $event->setOrder($order);
  531.     }
  532.     /**
  533.      * @throws \Propel\Runtime\Exception\PropelException
  534.      */
  535.     public function updateAddress(OrderAddressEvent $event): void
  536.     {
  537.         $orderAddress $event->getOrderAddress();
  538.         $orderAddress
  539.             ->setCustomerTitleId($event->getTitle())
  540.             ->setCompany($event->getCompany())
  541.             ->setFirstname($event->getFirstname())
  542.             ->setLastname($event->getLastname())
  543.             ->setAddress1($event->getAddress1())
  544.             ->setAddress2($event->getAddress2())
  545.             ->setAddress3($event->getAddress3())
  546.             ->setZipcode($event->getZipcode())
  547.             ->setCity($event->getCity())
  548.             ->setCountryId($event->getCountry())
  549.             ->setStateId($event->getState())
  550.             ->setPhone($event->getPhone())
  551.             ->setCellphone($event->getCellphone())
  552.             ->save()
  553.         ;
  554.         $event->setOrderAddress($orderAddress);
  555.     }
  556.     /**
  557.      * @throws \Propel\Runtime\Exception\PropelException
  558.      */
  559.     public function getOrderPayTotal(OrderPayTotalEvent $event): void
  560.     {
  561.         $order $event->getOrder();
  562.         $tax $event->getTax();
  563.         $total $order->getTotalAmount(
  564.             $tax,
  565.             $event->isIncludePostage(),
  566.             $event->isIncludeDiscount()
  567.         );
  568.         $event->setTotal($total);
  569.         $event->setTax($tax);
  570.     }
  571.     /**
  572.      * {@inheritdoc}
  573.      */
  574.     public static function getSubscribedEvents()
  575.     {
  576.         return [
  577.             TheliaEvents::ORDER_SET_DELIVERY_ADDRESS => ['setDeliveryAddress'128],
  578.             TheliaEvents::ORDER_SET_DELIVERY_MODULE => ['setDeliveryModule'128],
  579.             TheliaEvents::ORDER_SET_POSTAGE => ['setPostage'128],
  580.             TheliaEvents::ORDER_SET_INVOICE_ADDRESS => ['setInvoiceAddress'128],
  581.             TheliaEvents::ORDER_SET_PAYMENT_MODULE => ['setPaymentModule'128],
  582.             TheliaEvents::ORDER_PAY => ['create'128],
  583.             TheliaEvents::ORDER_PAY_GET_TOTAL => ['getOrderPayTotal'128],
  584.             TheliaEvents::ORDER_CART_CLEAR => ['orderCartClear'128],
  585.             TheliaEvents::ORDER_BEFORE_PAYMENT => ['orderBeforePayment'128],
  586.             TheliaEvents::ORDER_SEND_CONFIRMATION_EMAIL => ['sendConfirmationEmail'128],
  587.             TheliaEvents::ORDER_SEND_NOTIFICATION_EMAIL => ['sendNotificationEmail'128],
  588.             TheliaEvents::ORDER_UPDATE_STATUS => ['updateStatus'128],
  589.             TheliaEvents::ORDER_UPDATE_DELIVERY_REF => ['updateDeliveryRef'128],
  590.             TheliaEvents::ORDER_UPDATE_TRANSACTION_REF => ['updateTransactionRef'128],
  591.             TheliaEvents::ORDER_UPDATE_ADDRESS => ['updateAddress'128],
  592.             TheliaEvents::ORDER_CREATE_MANUAL => ['createManual'128],
  593.             TheliaEvents::ORDER_GET_STOCK_UPDATE_OPERATION_ON_ORDER_STATUS_CHANGE => ['getStockUpdateOnOrderStatusChange'128],
  594.         ];
  595.     }
  596.     /**
  597.      * Returns the session from the current request.
  598.      *
  599.      * @return \Thelia\Core\HttpFoundation\Session\Session
  600.      */
  601.     protected function getSession()
  602.     {
  603.         /** @var Request $request */
  604.         $request $this->requestStack->getCurrentRequest();
  605.         return $request->getSession();
  606.     }
  607. }