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

OrderConverter::convertToOrder()   F

Complexity

Conditions 12
Paths 385

Size

Total Lines 82
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 38
CRAP Score 12.018

Importance

Changes 0
Metric Value
cc 12
eloc 51
c 0
b 0
f 0
nc 385
nop 3
dl 0
loc 82
rs 3.8208
ccs 38
cts 40
cp 0.95
crap 12.018

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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