vendor/shopware/core/Checkout/Order/SalesChannel/OrderService.php line 81

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Checkout\Order\SalesChannel;
  3. use Shopware\Core\Checkout\Cart\Cart;
  4. use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
  5. use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStates;
  6. use Shopware\Core\Checkout\Order\Exception\PaymentMethodNotAvailableException;
  7. use Shopware\Core\Checkout\Order\OrderEntity;
  8. use Shopware\Core\Content\Product\State;
  9. use Shopware\Core\Framework\Context;
  10. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  11. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  13. use Shopware\Core\Framework\Log\Package;
  14. use Shopware\Core\Framework\Validation\BuildValidationEvent;
  15. use Shopware\Core\Framework\Validation\DataBag\DataBag;
  16. use Shopware\Core\Framework\Validation\DataValidationDefinition;
  17. use Shopware\Core\Framework\Validation\DataValidationFactoryInterface;
  18. use Shopware\Core\Framework\Validation\DataValidator;
  19. use Shopware\Core\Framework\Validation\Exception\ConstraintViolationException;
  20. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  21. use Shopware\Core\System\StateMachine\Aggregation\StateMachineState\StateMachineStateEntity;
  22. use Shopware\Core\System\StateMachine\Exception\StateMachineStateNotFoundException;
  23. use Shopware\Core\System\StateMachine\StateMachineRegistry;
  24. use Shopware\Core\System\StateMachine\Transition;
  25. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  26. use Symfony\Component\HttpFoundation\ParameterBag;
  27. use Symfony\Component\Validator\Constraints\NotBlank;
  28. #[Package('customer-order')]
  29. class OrderService
  30. {
  31.     public const CUSTOMER_COMMENT_KEY 'customerComment';
  32.     public const AFFILIATE_CODE_KEY 'affiliateCode';
  33.     public const CAMPAIGN_CODE_KEY 'campaignCode';
  34.     public const ALLOWED_TRANSACTION_STATES = [
  35.         OrderTransactionStates::STATE_OPEN,
  36.         OrderTransactionStates::STATE_CANCELLED,
  37.         OrderTransactionStates::STATE_REMINDED,
  38.         OrderTransactionStates::STATE_FAILED,
  39.         OrderTransactionStates::STATE_CHARGEBACK,
  40.         OrderTransactionStates::STATE_UNCONFIRMED,
  41.     ];
  42.     private DataValidator $dataValidator;
  43.     private DataValidationFactoryInterface $orderValidationFactory;
  44.     private EventDispatcherInterface $eventDispatcher;
  45.     private CartService $cartService;
  46.     private EntityRepositoryInterface $paymentMethodRepository;
  47.     private StateMachineRegistry $stateMachineRegistry;
  48.     /**
  49.      * @internal
  50.      */
  51.     public function __construct(
  52.         DataValidator $dataValidator,
  53.         DataValidationFactoryInterface $orderValidationFactory,
  54.         EventDispatcherInterface $eventDispatcher,
  55.         CartService $cartService,
  56.         EntityRepositoryInterface $paymentMethodRepository,
  57.         StateMachineRegistry $stateMachineRegistry
  58.     ) {
  59.         $this->dataValidator $dataValidator;
  60.         $this->orderValidationFactory $orderValidationFactory;
  61.         $this->eventDispatcher $eventDispatcher;
  62.         $this->cartService $cartService;
  63.         $this->paymentMethodRepository $paymentMethodRepository;
  64.         $this->stateMachineRegistry $stateMachineRegistry;
  65.     }
  66.     /**
  67.      * @throws ConstraintViolationException
  68.      */
  69.     public function createOrder(DataBag $dataSalesChannelContext $context): string
  70.     {
  71.         $cart $this->cartService->getCart($context->getToken(), $context);
  72.         $this->validateOrderData($data$context$cart->getLineItems()->hasLineItemWithState(State::IS_DOWNLOAD));
  73.         $this->validateCart($cart$context->getContext());
  74.         return $this->cartService->order($cart$context$data->toRequestDataBag());
  75.     }
  76.     /**
  77.      * @internal Should not be called from outside the core
  78.      */
  79.     public function orderStateTransition(
  80.         string $orderId,
  81.         string $transition,
  82.         ParameterBag $data,
  83.         Context $context
  84.     ): StateMachineStateEntity {
  85.         $stateFieldName $data->get('stateFieldName''stateId');
  86.         $stateMachineStates $this->stateMachineRegistry->transition(
  87.             new Transition(
  88.                 'order',
  89.                 $orderId,
  90.                 $transition,
  91.                 $stateFieldName
  92.             ),
  93.             $context
  94.         );
  95.         $toPlace $stateMachineStates->get('toPlace');
  96.         if (!$toPlace) {
  97.             throw new StateMachineStateNotFoundException('order_transaction'$transition);
  98.         }
  99.         return $toPlace;
  100.     }
  101.     /**
  102.      * @internal Should not be called from outside the core
  103.      */
  104.     public function orderTransactionStateTransition(
  105.         string $orderTransactionId,
  106.         string $transition,
  107.         ParameterBag $data,
  108.         Context $context
  109.     ): StateMachineStateEntity {
  110.         $stateFieldName $data->get('stateFieldName''stateId');
  111.         $stateMachineStates $this->stateMachineRegistry->transition(
  112.             new Transition(
  113.                 'order_transaction',
  114.                 $orderTransactionId,
  115.                 $transition,
  116.                 $stateFieldName
  117.             ),
  118.             $context
  119.         );
  120.         $toPlace $stateMachineStates->get('toPlace');
  121.         if (!$toPlace) {
  122.             throw new StateMachineStateNotFoundException('order_transaction'$transition);
  123.         }
  124.         return $toPlace;
  125.     }
  126.     /**
  127.      * @internal Should not be called from outside the core
  128.      */
  129.     public function orderDeliveryStateTransition(
  130.         string $orderDeliveryId,
  131.         string $transition,
  132.         ParameterBag $data,
  133.         Context $context
  134.     ): StateMachineStateEntity {
  135.         $stateFieldName $data->get('stateFieldName''stateId');
  136.         $stateMachineStates $this->stateMachineRegistry->transition(
  137.             new Transition(
  138.                 'order_delivery',
  139.                 $orderDeliveryId,
  140.                 $transition,
  141.                 $stateFieldName
  142.             ),
  143.             $context
  144.         );
  145.         $toPlace $stateMachineStates->get('toPlace');
  146.         if (!$toPlace) {
  147.             throw new StateMachineStateNotFoundException('order_transaction'$transition);
  148.         }
  149.         return $toPlace;
  150.     }
  151.     public function isPaymentChangeableByTransactionState(OrderEntity $order): bool
  152.     {
  153.         if ($order->getTransactions() === null) {
  154.             return true;
  155.         }
  156.         $transaction $order->getTransactions()->last();
  157.         if ($transaction === null || $transaction->getStateMachineState() === null) {
  158.             return true;
  159.         }
  160.         $state $transaction->getStateMachineState()->getTechnicalName();
  161.         if (\in_array($stateself::ALLOWED_TRANSACTION_STATEStrue)) {
  162.             return true;
  163.         }
  164.         return false;
  165.     }
  166.     private function validateCart(Cart $cartContext $context): void
  167.     {
  168.         $idsOfPaymentMethods = [];
  169.         foreach ($cart->getTransactions() as $paymentMethod) {
  170.             $idsOfPaymentMethods[] = $paymentMethod->getPaymentMethodId();
  171.         }
  172.         $criteria = new Criteria();
  173.         $criteria->addFilter(
  174.             new EqualsFilter('active'true)
  175.         );
  176.         $paymentMethods $this->paymentMethodRepository->searchIds($criteria$context);
  177.         if ($paymentMethods->getTotal() !== \count(array_unique($idsOfPaymentMethods))) {
  178.             foreach ($cart->getTransactions() as $paymentMethod) {
  179.                 if (!\in_array($paymentMethod->getPaymentMethodId(), $paymentMethods->getIds(), true)) {
  180.                     throw new PaymentMethodNotAvailableException($paymentMethod->getPaymentMethodId());
  181.                 }
  182.             }
  183.         }
  184.     }
  185.     /**
  186.      * @throws ConstraintViolationException
  187.      */
  188.     private function validateOrderData(
  189.         ParameterBag $data,
  190.         SalesChannelContext $context,
  191.         bool $hasVirtualGoods
  192.     ): void {
  193.         $definition $this->getOrderCreateValidationDefinition(new DataBag($data->all()), $context$hasVirtualGoods);
  194.         $violations $this->dataValidator->getViolations($data->all(), $definition);
  195.         if ($violations->count() > 0) {
  196.             throw new ConstraintViolationException($violations$data->all());
  197.         }
  198.     }
  199.     private function getOrderCreateValidationDefinition(
  200.         DataBag $data,
  201.         SalesChannelContext $context,
  202.         bool $hasVirtualGoods
  203.     ): DataValidationDefinition {
  204.         $validation $this->orderValidationFactory->create($context);
  205.         if ($hasVirtualGoods) {
  206.             $validation->add('revocation', new NotBlank());
  207.         }
  208.         $validationEvent = new BuildValidationEvent($validation$data$context->getContext());
  209.         $this->eventDispatcher->dispatch($validationEvent$validationEvent->getName());
  210.         return $validation;
  211.     }
  212. }