Passed
Push — master ( 28073e...99ed61 )
by Christian
11:34 queued 10s
created

OrderConverter::assembleSalesChannelContext()   B

Complexity

Conditions 8
Paths 10

Size

Total Lines 49
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 8.0036

Importance

Changes 0
Metric Value
cc 8
eloc 28
c 0
b 0
f 0
nc 10
nop 2
dl 0
loc 49
rs 8.4444
ccs 25
cts 26
cp 0.9615
crap 8.0036
1
<?php declare(strict_types=1);
2
3
namespace Shopware\Core\Checkout\Cart\Order;
4
5
use Shopware\Core\Checkout\Cart\Cart;
6
use Shopware\Core\Checkout\Cart\Delivery\DeliveryProcessor;
7
use Shopware\Core\Checkout\Cart\Delivery\Struct\Delivery;
8
use Shopware\Core\Checkout\Cart\Delivery\Struct\DeliveryCollection;
9
use Shopware\Core\Checkout\Cart\Delivery\Struct\DeliveryDate;
10
use Shopware\Core\Checkout\Cart\Delivery\Struct\DeliveryPosition;
11
use Shopware\Core\Checkout\Cart\Delivery\Struct\DeliveryPositionCollection;
12
use Shopware\Core\Checkout\Cart\Delivery\Struct\ShippingLocation;
13
use Shopware\Core\Checkout\Cart\Exception\InvalidPayloadException;
14
use Shopware\Core\Checkout\Cart\Exception\InvalidQuantityException;
15
use Shopware\Core\Checkout\Cart\Exception\LineItemNotStackableException;
16
use Shopware\Core\Checkout\Cart\Exception\MissingOrderRelationException;
17
use Shopware\Core\Checkout\Cart\Exception\MixedLineItemTypeException;
18
use Shopware\Core\Checkout\Cart\Exception\OrderInconsistentException;
19
use Shopware\Core\Checkout\Cart\LineItem\LineItemCollection;
20
use Shopware\Core\Checkout\Cart\Order\Transformer\AddressTransformer;
21
use Shopware\Core\Checkout\Cart\Order\Transformer\CartTransformer;
22
use Shopware\Core\Checkout\Cart\Order\Transformer\CustomerTransformer;
23
use Shopware\Core\Checkout\Cart\Order\Transformer\DeliveryTransformer;
24
use Shopware\Core\Checkout\Cart\Order\Transformer\LineItemTransformer;
25
use Shopware\Core\Checkout\Cart\Order\Transformer\TransactionTransformer;
26
use Shopware\Core\Checkout\Customer\CustomerEntity;
27
use Shopware\Core\Checkout\Order\Aggregate\OrderAddress\OrderAddressEntity;
28
use Shopware\Core\Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryCollection;
29
use Shopware\Core\Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryStates;
30
use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStates;
31
use Shopware\Core\Checkout\Order\Exception\DeliveryWithoutAddressException;
32
use Shopware\Core\Checkout\Order\OrderDefinition;
33
use Shopware\Core\Checkout\Order\OrderEntity;
34
use Shopware\Core\Checkout\Order\OrderStates;
35
use Shopware\Core\Checkout\Promotion\Cart\PromotionCollector;
36
use Shopware\Core\Content\Product\Cart\ProductCartProcessor;
37
use Shopware\Core\Framework\Context;
38
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
39
use Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException;
40
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
41
use Shopware\Core\Framework\Feature;
42
use Shopware\Core\Framework\Uuid\Uuid;
43
use Shopware\Core\System\NumberRange\ValueGenerator\NumberRangeValueGeneratorInterface;
44
use Shopware\Core\System\SalesChannel\Context\SalesChannelContextFactory;
45
use Shopware\Core\System\SalesChannel\Context\SalesChannelContextService;
46
use Shopware\Core\System\SalesChannel\SalesChannelContext;
47
use Shopware\Core\System\StateMachine\StateMachineRegistry;
48
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
49
50
class OrderConverter
51
{
52
    public const CART_CONVERTED_TO_ORDER_EVENT = 'cart.convertedToOrder.event';
53
54
    public const CART_TYPE = 'recalculation';
55
56
    public const ORIGINAL_ID = 'originalId';
57
58
    public const ORIGINAL_ORDER_NUMBER = 'originalOrderNumber';
59
60
    private const ADMIN_EDIT_ORDER_PERMISSIONS = [
61
        ProductCartProcessor::ALLOW_PRODUCT_PRICE_OVERWRITES => true,
62
        ProductCartProcessor::SKIP_PRODUCT_RECALCULATION => true,
63
        DeliveryProcessor::SKIP_DELIVERY_PRICE_RECALCULATION => true,
64
        DeliveryProcessor::SKIP_DELIVERY_TAX_RECALCULATION => true,
65
        PromotionCollector::SKIP_PROMOTION => true,
66
        ProductCartProcessor::SKIP_PRODUCT_STOCK_VALIDATION => true,
67
    ];
68
69
    /**
70
     * @var EntityRepositoryInterface
71
     */
72
    protected $customerRepository;
73
74 2
    /**
75
     * @var SalesChannelContextFactory
76
     */
77
    protected $salesChannelContextFactory;
78
79
    /**
80 2
     * @var EventDispatcherInterface
81 2
     */
82 2
    protected $eventDispatcher;
83 2
84 2
    /**
85
     * @var StateMachineRegistry
86
     */
87
    private $stateMachineRegistry;
88
89 25
    /**
90
     * @var NumberRangeValueGeneratorInterface
91
     */
92 25
    private $numberRangeValueGenerator;
93 22
94 22
    /**
95
     * @var OrderDefinition
96
     */
97
    private $orderDefinition;
98 25
99 25
    /**
100 25
     * @var EntityRepositoryInterface
101
     */
102
    private $orderAddressRepository;
103 25
104 25
    public function __construct(
105
        EntityRepositoryInterface $customerRepository,
106
        SalesChannelContextFactory $salesChannelContextFactory,
107 25
        StateMachineRegistry $stateMachineRegistry,
108
        EventDispatcherInterface $eventDispatcher,
109 25
        NumberRangeValueGeneratorInterface $numberRangeValueGenerator,
110
        OrderDefinition $orderDefinition,
111 25
        EntityRepositoryInterface $orderAddressRepository
112 25
    ) {
113 25
        $this->customerRepository = $customerRepository;
114 25
        $this->salesChannelContextFactory = $salesChannelContextFactory;
115 25
        $this->stateMachineRegistry = $stateMachineRegistry;
116 25
        $this->eventDispatcher = $eventDispatcher;
117 25
        $this->numberRangeValueGenerator = $numberRangeValueGenerator;
118 25
        $this->orderDefinition = $orderDefinition;
119
        $this->orderAddressRepository = $orderAddressRepository;
120
    }
121
122 25
    /**
123 25
     * @throws DeliveryWithoutAddressException
124
     */
125 25
    public function convertToOrder(Cart $cart, SalesChannelContext $context, OrderConversionContext $conversionContext): array
126 22
    {
127
        foreach ($cart->getDeliveries() as $delivery) {
128 3
            if ($delivery->getLocation()->getAddress() !== null || $delivery->hasExtensionOfType(self::ORIGINAL_ID, IdStruct::class)) {
129 3
                continue;
130 3
            }
131
132 25
            throw new DeliveryWithoutAddressException();
133
        }
134
        $data = CartTransformer::transform(
135 25
            $cart,
136 25
            $context,
137 25
            $this->stateMachineRegistry->getInitialState(OrderStates::STATE_MACHINE, $context->getContext())->getId()
138 25
        );
139
140
        if ($conversionContext->shouldIncludeCustomer()) {
141 25
            $data['orderCustomer'] = CustomerTransformer::transform($context->getCustomer());
0 ignored issues
show
Bug introduced by
It seems like $context->getCustomer() can also be of type null; however, parameter $customer of Shopware\Core\Checkout\C...ransformer::transform() does only seem to accept Shopware\Core\Checkout\Customer\CustomerEntity, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

141
            $data['orderCustomer'] = CustomerTransformer::transform(/** @scrutinizer ignore-type */ $context->getCustomer());
Loading history...
142
        }
143
144 25
        $data['languageId'] = $context->getSalesChannel()->getLanguageId();
145 25
146 5
        $convertedLineItems = LineItemTransformer::transformCollection($cart->getLineItems());
147
        $shippingAddresses = [];
148
149 25
        if ($conversionContext->shouldIncludeDeliveries()) {
150
            $shippingAddresses = AddressTransformer::transformCollection($cart->getDeliveries()->getAddresses(), true);
151 25
            $data['deliveries'] = DeliveryTransformer::transformCollection(
152 25
                $cart->getDeliveries(),
153 25
                $convertedLineItems,
154
                $this->stateMachineRegistry->getInitialState(OrderDeliveryStates::STATE_MACHINE, $context->getContext())->getId(),
155
                $context->getContext(),
156 25
                $shippingAddresses
157
            );
158
        }
159
160
        if ($conversionContext->shouldIncludeBillingAddress()) {
161
            $customerAddressId = $context->getCustomer()->getActiveBillingAddress()->getId();
162
163
            if (array_key_exists($customerAddressId, $shippingAddresses)) {
164
                $billingAddressId = $shippingAddresses[$customerAddressId]['id'];
165
            } else {
166 7
                $billingAddress = AddressTransformer::transform($context->getCustomer()->getActiveBillingAddress());
0 ignored issues
show
Bug introduced by
It seems like $context->getCustomer()-...tActiveBillingAddress() can also be of type null; however, parameter $address of Shopware\Core\Checkout\C...ransformer::transform() does only seem to accept Shopware\Core\Checkout\C...s\CustomerAddressEntity, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

166
                $billingAddress = AddressTransformer::transform(/** @scrutinizer ignore-type */ $context->getCustomer()->getActiveBillingAddress());
Loading history...
167
                $data['addresses'] = [$billingAddress];
168 7
                $billingAddressId = $billingAddress['id'];
169
            }
170
            $data['billingAddressId'] = $billingAddressId;
171
        }
172 7
173
        if ($conversionContext->shouldIncludeTransactions()) {
174
            $data['transactions'] = TransactionTransformer::transformCollection(
175
                $cart->getTransactions(),
176 7
                $this->stateMachineRegistry->getInitialState(OrderTransactionStates::STATE_MACHINE, $context->getContext())->getId(),
177 7
                $context->getContext()
178 7
            );
179
        }
180
181
        $data['lineItems'] = array_values($convertedLineItems);
182
183 7
        /** @var IdStruct|null $idStruct */
184 7
        $idStruct = $cart->getExtensionOfType(self::ORIGINAL_ID, IdStruct::class);
185
        $data['id'] = $idStruct ? $idStruct->getId() : Uuid::randomHex();
0 ignored issues
show
introduced by
$idStruct is of type Shopware\Core\Checkout\Cart\Order\IdStruct, thus it always evaluated to true.
Loading history...
186
187 7
        /** @var IdStruct|null $orderNumberStruct */
188 7
        $orderNumberStruct = $cart->getExtensionOfType(self::ORIGINAL_ORDER_NUMBER, IdStruct::class);
189 7
        if ($orderNumberStruct !== null) {
190
            $data['orderNumber'] = $orderNumberStruct->getId();
191
        } else {
192
            $data['orderNumber'] = $this->numberRangeValueGenerator->getValue(
193 7
                $this->orderDefinition->getEntityName(),
194
                $context->getContext(),
195 7
                $context->getSalesChannel()->getId()
196
            );
197 7
        }
198 7
199 7
        if (Feature::isActive('FEATURE_NEXT_9351')) {
200
            $data['ruleIds'] = $context->getRuleIds();
201
        }
202 7
203 6
        $event = new CartConvertedEvent($cart, $data, $context, $conversionContext);
204
        $this->eventDispatcher->dispatch($event);
205
206 7
        return $event->getConvertedCart();
207
    }
208
209 7
    /**
210 7
     * @throws InvalidPayloadException
211
     * @throws InvalidQuantityException
212 7
     * @throws LineItemNotStackableException
213 7
     * @throws MixedLineItemTypeException
214
     * @throws MissingOrderRelationException
215 7
     */
216
    public function convertToCart(OrderEntity $order, Context $context): Cart
217
    {
218
        if ($order->getLineItems() === null) {
219
            throw new MissingOrderRelationException('lineItems');
220
        }
221 6
222
        if ($order->getDeliveries() === null) {
223 6
            throw new MissingOrderRelationException('deliveries');
224 6
        }
225
226 6
        $cart = new Cart(self::CART_TYPE, Uuid::randomHex());
227
        $cart->setPrice($order->getPrice());
228 6
        $cart->addExtension(self::ORIGINAL_ID, new IdStruct($order->getId()));
229 6
        $orderNumber = $order->getOrderNumber();
230
        if ($orderNumber === null) {
0 ignored issues
show
introduced by
The condition $orderNumber === null is always false.
Loading history...
231
            throw new OrderInconsistentException($order->getId(), 'orderNumber is required');
232 6
        }
233 6
234 6
        $cart->addExtension(self::ORIGINAL_ORDER_NUMBER, new IdStruct($orderNumber));
235
        /* NEXT-708 support:
236 6
            - transactions
237 6
        */
238 6
239 6
        $lineItems = LineItemTransformer::transformFlatToNested($order->getLineItems());
240 6
241
        $cart->addLineItems($lineItems);
242
        $cart->setDeliveries(
243
            $this->convertDeliveries($order->getDeliveries(), $lineItems)
244
        );
245 7
246
        $event = new OrderConvertedEvent($order, $cart, $context);
247 7
        $this->eventDispatcher->dispatch($event);
248
249
        return $event->getConvertedCart();
250 7
    }
251 7
252 7
    /**
253 7
     * @throws InconsistentCriteriaIdsException
254
     */
255
    public function assembleSalesChannelContext(OrderEntity $order, Context $context): SalesChannelContext
256 7
    {
257
        if ($order->getTransactions() === null) {
258
            throw new MissingOrderRelationException('transactions');
259 7
        }
260 7
        if ($order->getOrderCustomer() === null) {
261
            throw new MissingOrderRelationException('orderCustomer');
262
        }
263 7
264
        $customerId = $order->getOrderCustomer()->getCustomerId();
265
        $customerGroupId = null;
266
267 7
        if ($customerId) {
268 7
            /** @var CustomerEntity|null $customer */
269 7
            $customer = $this->customerRepository->search(new Criteria([$customerId]), $context)->get($customerId);
270 7
            $customerGroupId = $customer->getGroupId() ?? null;
271 7
        }
272 7
273
        /** @var OrderAddressEntity|null $billingAddress */
274
        $billingAddress = $this->orderAddressRepository->search(new Criteria([$order->getBillingAddressId()]), $context)->get($order->getBillingAddressId());
275
276 7
        $options = [
277 7
            SalesChannelContextService::CURRENCY_ID => $order->getCurrencyId(),
278 7
            SalesChannelContextService::LANGUAGE_ID => $order->getLanguageId(),
279 7
            SalesChannelContextService::CUSTOMER_ID => $customerId,
280 7
            SalesChannelContextService::COUNTRY_STATE_ID => $billingAddress->getCountryStateId(),
281 7
            SalesChannelContextService::CUSTOMER_GROUP_ID => $customerGroupId,
282 7
            SalesChannelContextService::PERMISSIONS => self::ADMIN_EDIT_ORDER_PERMISSIONS,
283 7
            SalesChannelContextService::VERSION_ID => $context->getVersionId(),
284
        ];
285 7
286
        //get the first not paid transaction or, if all paid, the last transaction
287 7
        if ($order->getTransactions() !== null) {
288
            foreach ($order->getTransactions() as $transaction) {
289 7
                $options[SalesChannelContextService::PAYMENT_METHOD_ID] = $transaction->getPaymentMethodId();
290
                if (
291
                    $transaction->getStateMachineState() !== null
292 7
                    && $transaction->getStateMachineState()->getTechnicalName() !== OrderTransactionStates::STATE_PAID
293
                ) {
294
                    break;
295
                }
296
            }
297
        }
298
299
        $salesChannelContext = $this->salesChannelContextFactory->create(Uuid::randomHex(), $order->getSalesChannelId(), $options);
300 7
301
        $salesChannelContext->getContext()->addExtensions($context->getExtensions());
302 7
303 7
        return $salesChannelContext;
304 7
    }
305 7
306 7
    private function convertDeliveries(OrderDeliveryCollection $orderDeliveries, LineItemCollection $lineItems): DeliveryCollection
307 7
    {
308 7
        $cartDeliveries = new DeliveryCollection();
309 7
310 7
        foreach ($orderDeliveries as $orderDelivery) {
311 7
            $deliveryDate = new DeliveryDate(
312 7
                $orderDelivery->getShippingDateEarliest(),
313
                $orderDelivery->getShippingDateLatest()
314 7
            );
315 7
316
            $deliveryPositions = new DeliveryPositionCollection();
317
318 7
            foreach ($orderDelivery->getPositions() as $position) {
319 7
                $identifier = $position->getOrderLineItem()->getIdentifier();
320
321
                // line item has been removed and will not be added to delivery
322 7
                if ($lineItems->get($identifier) === null) {
323 7
                    continue;
324
                }
325 7
326
                $deliveryPosition = new DeliveryPosition(
327
                    $identifier,
328
                    $lineItems->get($identifier),
0 ignored issues
show
Bug introduced by
It seems like $lineItems->get($identifier) can also be of type null; however, parameter $lineItem of Shopware\Core\Checkout\C...Position::__construct() does only seem to accept Shopware\Core\Checkout\Cart\LineItem\LineItem, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

328
                    /** @scrutinizer ignore-type */ $lineItems->get($identifier),
Loading history...
329
                    $position->getPrice()->getQuantity(),
330
                    $position->getPrice(),
331
                    $deliveryDate
332
                );
333
                $deliveryPosition->addExtension(self::ORIGINAL_ID, new IdStruct($position->getId()));
334
335
                $deliveryPositions->add($deliveryPosition);
336
            }
337
338
            $cartDelivery = new Delivery(
339
                $deliveryPositions,
340
                $deliveryDate,
341
                $orderDelivery->getShippingMethod(),
342
                new ShippingLocation(
343
                    $orderDelivery->getShippingOrderAddress()->getCountry(),
344
                    $orderDelivery->getShippingOrderAddress()->getCountryState(),
345
                    null
346
                ),
347
                $orderDelivery->getShippingCosts()
348
            );
349
            $cartDelivery->addExtension(self::ORIGINAL_ID, new IdStruct($orderDelivery->getId()));
350
351
            $cartDeliveries->add($cartDelivery);
352
        }
353
354
        return $cartDeliveries;
355
    }
356
}
357