Passed
Push — master ( dd6e64...a763da )
by Christian
12:12 queued 11s
created

CartOrderRoute   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 144
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 55
dl 0
loc 144
rs 10
c 0
b 0
f 0
wmc 11

6 Methods

Rating   Name   Duplication   Size   Complexity  
A addAffiliateTracking() 0 7 3
A order() 0 44 3
A getDecorated() 0 3 1
A addCustomerComment() 0 9 2
A __construct() 0 14 1
A fetchCustomer() 0 10 1
1
<?php declare(strict_types=1);
2
3
namespace Shopware\Core\Checkout\Cart\SalesChannel;
4
5
use OpenApi\Annotations as OA;
6
use Shopware\Core\Checkout\Cart\Cart;
7
use Shopware\Core\Checkout\Cart\CartCalculator;
8
use Shopware\Core\Checkout\Cart\CartPersisterInterface;
9
use Shopware\Core\Checkout\Cart\Event\CheckoutOrderPlacedEvent;
10
use Shopware\Core\Checkout\Cart\Order\OrderPersisterInterface;
11
use Shopware\Core\Checkout\Order\Aggregate\OrderCustomer\OrderCustomerEntity;
12
use Shopware\Core\Checkout\Order\OrderEntity;
13
use Shopware\Core\Checkout\Order\SalesChannel\OrderService;
14
use Shopware\Core\Checkout\Payment\Exception\InvalidOrderException;
15
use Shopware\Core\Framework\Context;
16
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
17
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
18
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
19
use Shopware\Core\Framework\Plugin\Exception\DecorationPatternException;
20
use Shopware\Core\Framework\Routing\Annotation\RouteScope;
21
use Shopware\Core\Framework\Routing\Annotation\Since;
22
use Shopware\Core\Framework\Validation\DataBag\DataBag;
23
use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
24
use Shopware\Core\System\SalesChannel\SalesChannelContext;
25
use Symfony\Component\Routing\Annotation\Route;
26
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
27
28
/**
29
 * @RouteScope(scopes={"store-api"})
30
 */
31
class CartOrderRoute extends AbstractCartOrderRoute
32
{
33
    /**
34
     * @var CartCalculator
35
     */
36
    private $cartCalculator;
37
38
    /**
39
     * @var EntityRepositoryInterface
40
     */
41
    private $orderRepository;
42
43
    /**
44
     * @var OrderPersisterInterface
45
     */
46
    private $orderPersister;
47
48
    /**
49
     * @var CartPersisterInterface
50
     */
51
    private $cartPersister;
52
53
    /**
54
     * @var EventDispatcherInterface
55
     */
56
    private $eventDispatcher;
57
58
    /**
59
     * @var EntityRepositoryInterface
60
     */
61
    private $orderCustomerRepository;
62
63
    public function __construct(
64
        CartCalculator $cartCalculator,
65
        EntityRepositoryInterface $orderRepository,
66
        EntityRepositoryInterface $orderCustomerRepository,
67
        OrderPersisterInterface $orderPersister,
68
        CartPersisterInterface $cartPersister,
69
        EventDispatcherInterface $eventDispatcher
70
    ) {
71
        $this->cartCalculator = $cartCalculator;
72
        $this->orderRepository = $orderRepository;
73
        $this->orderPersister = $orderPersister;
74
        $this->cartPersister = $cartPersister;
75
        $this->eventDispatcher = $eventDispatcher;
76
        $this->orderCustomerRepository = $orderCustomerRepository;
77
    }
78
79
    public function getDecorated(): AbstractCartOrderRoute
80
    {
81
        throw new DecorationPatternException(self::class);
82
    }
83
84
    /**
85
     * @Since("6.3.0.0")
86
     * @OA\Post(
87
     *      path="/checkout/order",
88
     *      summary="Create a new order from cart",
89
     *      operationId="createOrder",
90
     *      tags={"Store API", "Cart"},
91
     *      @OA\Response(
92
     *          response="200",
93
     *          description="Order",
94
     *          @OA\JsonContent(ref="#/components/schemas/order_flat")
95
     *     )
96
     * )
97
     * @Route("/store-api/v{version}/checkout/order", name="store-api.checkout.cart.order", methods={"POST"})
98
     */
99
    public function order(Cart $cart, SalesChannelContext $context, ?RequestDataBag $data = null): CartOrderRouteResponse
100
    {
101
        $calculatedCart = $this->cartCalculator->calculate($cart, $context);
102
103
        // @deprecated tag:v6.4.0 - Parameter $data will be mandatory in future implementation
104
        if ($data !== null) {
105
            $this->addCustomerComment($calculatedCart, $data);
106
            $this->addAffiliateTracking($calculatedCart, $data);
107
        }
108
109
        $orderId = $this->orderPersister->persist($calculatedCart, $context);
110
111
        $criteria = new Criteria([$orderId]);
112
        $criteria
113
            ->addAssociation('deliveries.shippingMethod')
114
            ->addAssociation('deliveries.shippingOrderAddress.country')
115
            ->addAssociation('transactions.paymentMethod')
116
            ->addAssociation('lineItems')
117
            ->addAssociation('currency')
118
            ->addAssociation('addresses.country');
119
120
        /** @var OrderEntity|null $orderEntity */
121
        $orderEntity = $this->orderRepository->search($criteria, $context->getContext())->first();
122
123
        if (!$orderEntity) {
124
            throw new InvalidOrderException($orderId);
125
        }
126
127
        // todo@dr: can be merged with above criteria after NEXT-4466
128
        $orderEntity->setOrderCustomer(
129
            $this->fetchCustomer($orderEntity->getId(), $context->getContext())
130
        );
131
132
        $orderPlacedEvent = new CheckoutOrderPlacedEvent(
133
            $context->getContext(),
134
            $orderEntity,
135
            $context->getSalesChannel()->getId()
136
        );
137
138
        $this->eventDispatcher->dispatch($orderPlacedEvent);
139
140
        $this->cartPersister->delete($context->getToken(), $context);
141
142
        return new CartOrderRouteResponse($orderEntity);
143
    }
144
145
    private function fetchCustomer(string $orderId, Context $context): OrderCustomerEntity
146
    {
147
        $criteria = new Criteria();
148
        $criteria->addFilter(new EqualsFilter('orderId', $orderId));
149
        $criteria->addAssociation('customer');
150
        $criteria->addAssociation('salutation');
151
152
        return $this->orderCustomerRepository
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->orderCusto...ria, $context)->first() could return the type null which is incompatible with the type-hinted return Shopware\Core\Checkout\O...mer\OrderCustomerEntity. Consider adding an additional type-check to rule them out.
Loading history...
153
            ->search($criteria, $context)
154
            ->first();
155
    }
156
157
    private function addCustomerComment(Cart $cart, DataBag $data): void
158
    {
159
        $customerComment = ltrim(rtrim((string) $data->get(OrderService::CUSTOMER_COMMENT_KEY, '')));
160
161
        if ($customerComment === '') {
162
            return;
163
        }
164
165
        $cart->setCustomerComment($customerComment);
166
    }
167
168
    private function addAffiliateTracking(Cart $cart, DataBag $data): void
169
    {
170
        $affiliateCode = $data->get(OrderService::AFFILIATE_CODE_KEY);
171
        $campaignCode = $data->get(OrderService::CAMPAIGN_CODE_KEY);
172
        if ($affiliateCode !== null && $campaignCode !== null) {
173
            $cart->setAffiliateCode($affiliateCode);
174
            $cart->setCampaignCode($campaignCode);
175
        }
176
    }
177
}
178