Passed
Push — master ( b21036...8d84d5 )
by Christian
11:52 queued 12s
created

OrderConverter::convertToOrder()   C

Complexity

Conditions 11
Paths 193

Size

Total Lines 80
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 37
CRAP Score 11.0163

Importance

Changes 0
Metric Value
cc 11
eloc 50
nc 193
nop 3
dl 0
loc 80
rs 6.5416
c 0
b 0
f 0
ccs 37
cts 39
cp 0.9487
crap 11.0163

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\Uuid\Uuid;
42
use Shopware\Core\System\NumberRange\ValueGenerator\NumberRangeValueGeneratorInterface;
43
use Shopware\Core\System\SalesChannel\Context\SalesChannelContextFactory;
44
use Shopware\Core\System\SalesChannel\Context\SalesChannelContextService;
45
use Shopware\Core\System\SalesChannel\SalesChannelContext;
46
use Shopware\Core\System\StateMachine\StateMachineRegistry;
47
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
48
49
class OrderConverter
50
{
51
    public const CART_CONVERTED_TO_ORDER_EVENT = 'cart.convertedToOrder.event';
52
53
    public const CART_TYPE = 'recalculation';
54
55
    public const ORIGINAL_ID = 'originalId';
56
57
    public const ORIGINAL_ORDER_NUMBER = 'originalOrderNumber';
58
59
    private const ADMIN_EDIT_ORDER_PERMISSIONS = [
60
        ProductCartProcessor::ALLOW_PRODUCT_PRICE_OVERWRITES => true,
61
        ProductCartProcessor::SKIP_PRODUCT_RECALCULATION => true,
62
        DeliveryProcessor::SKIP_DELIVERY_PRICE_RECALCULATION => true,
63
        DeliveryProcessor::SKIP_DELIVERY_TAX_RECALCULATION => true,
64
        PromotionCollector::SKIP_PROMOTION => true,
65
        ProductCartProcessor::SKIP_PRODUCT_STOCK_VALIDATION => true,
66
    ];
67
68
    /**
69
     * @var EntityRepositoryInterface
70
     */
71
    protected $customerRepository;
72
73
    /**
74 2
     * @var SalesChannelContextFactory
75
     */
76
    protected $salesChannelContextFactory;
77
78
    /**
79
     * @var EventDispatcherInterface
80 2
     */
81 2
    protected $eventDispatcher;
82 2
83 2
    /**
84 2
     * @var StateMachineRegistry
85
     */
86
    private $stateMachineRegistry;
87
88
    /**
89 25
     * @var NumberRangeValueGeneratorInterface
90
     */
91
    private $numberRangeValueGenerator;
92 25
93 22
    /**
94 22
     * @var OrderDefinition
95
     */
96
    private $orderDefinition;
97
98 25
    /**
99 25
     * @var EntityRepositoryInterface
100 25
     */
101
    private $orderAddressRepository;
102
103 25
    public function __construct(
104 25
        EntityRepositoryInterface $customerRepository,
105
        SalesChannelContextFactory $salesChannelContextFactory,
106
        StateMachineRegistry $stateMachineRegistry,
107 25
        EventDispatcherInterface $eventDispatcher,
108
        NumberRangeValueGeneratorInterface $numberRangeValueGenerator,
109 25
        OrderDefinition $orderDefinition,
110
        EntityRepositoryInterface $orderAddressRepository
111 25
    ) {
112 25
        $this->customerRepository = $customerRepository;
113 25
        $this->salesChannelContextFactory = $salesChannelContextFactory;
114 25
        $this->stateMachineRegistry = $stateMachineRegistry;
115 25
        $this->eventDispatcher = $eventDispatcher;
116 25
        $this->numberRangeValueGenerator = $numberRangeValueGenerator;
117 25
        $this->orderDefinition = $orderDefinition;
118 25
        $this->orderAddressRepository = $orderAddressRepository;
119
    }
120
121
    /**
122 25
     * @throws DeliveryWithoutAddressException
123 25
     */
124
    public function convertToOrder(Cart $cart, SalesChannelContext $context, OrderConversionContext $conversionContext): array
125 25
    {
126 22
        foreach ($cart->getDeliveries() as $delivery) {
127
            if ($delivery->getLocation()->getAddress() !== null || $delivery->hasExtensionOfType(self::ORIGINAL_ID, IdStruct::class)) {
128 3
                continue;
129 3
            }
130 3
131
            throw new DeliveryWithoutAddressException();
132 25
        }
133
        $data = CartTransformer::transform(
134
            $cart,
135 25
            $context,
136 25
            $this->stateMachineRegistry->getInitialState(OrderStates::STATE_MACHINE, $context->getContext())->getId()
137 25
        );
138 25
139
        if ($conversionContext->shouldIncludeCustomer()) {
140
            $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

140
            $data['orderCustomer'] = CustomerTransformer::transform(/** @scrutinizer ignore-type */ $context->getCustomer());
Loading history...
141 25
        }
142
143
        $data['languageId'] = $context->getSalesChannel()->getLanguageId();
144 25
145 25
        $convertedLineItems = LineItemTransformer::transformCollection($cart->getLineItems());
146 5
        $shippingAddresses = [];
147
148
        if ($conversionContext->shouldIncludeDeliveries()) {
149 25
            $shippingAddresses = AddressTransformer::transformCollection($cart->getDeliveries()->getAddresses(), true);
150
            $data['deliveries'] = DeliveryTransformer::transformCollection(
151 25
                $cart->getDeliveries(),
152 25
                $convertedLineItems,
153 25
                $this->stateMachineRegistry->getInitialState(OrderDeliveryStates::STATE_MACHINE, $context->getContext())->getId(),
154
                $context->getContext(),
155
                $shippingAddresses
156 25
            );
157
        }
158
159
        if ($conversionContext->shouldIncludeBillingAddress()) {
160
            $customerAddressId = $context->getCustomer()->getActiveBillingAddress()->getId();
161
162
            if (array_key_exists($customerAddressId, $shippingAddresses)) {
163
                $billingAddressId = $shippingAddresses[$customerAddressId]['id'];
164
            } else {
165
                $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

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

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