Completed
Push — master ( d81c19...f57266 )
by Kamil
20s
created

src/Sylius/Behat/Context/Setup/OrderContext.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/*
4
 * This file is part of the Sylius package.
5
 *
6
 * (c) Paweł Jędrzejewski
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Sylius\Behat\Context\Setup;
15
16
use Behat\Behat\Context\Context;
17
use Doctrine\Common\Persistence\ObjectManager;
18
use SM\Factory\FactoryInterface as StateMachineFactoryInterface;
19
use Sylius\Behat\Service\SharedStorageInterface;
20
use Sylius\Component\Core\Model\AddressInterface;
21
use Sylius\Component\Core\Model\ChannelInterface;
22
use Sylius\Component\Core\Model\ChannelPricingInterface;
23
use Sylius\Component\Core\Model\OrderInterface;
24
use Sylius\Component\Core\Model\OrderItemInterface;
25
use Sylius\Component\Core\Model\ProductInterface;
26
use Sylius\Component\Core\Model\ProductVariantInterface;
27
use Sylius\Component\Core\Model\PromotionCouponInterface;
28
use Sylius\Component\Core\Model\ShippingMethodInterface;
29
use Sylius\Component\Core\Model\ShopUserInterface;
30
use Sylius\Component\Core\OrderCheckoutTransitions;
31
use Sylius\Component\Core\OrderPaymentTransitions;
32
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
33
use Sylius\Component\Customer\Model\CustomerInterface;
34
use Sylius\Component\Order\Modifier\OrderItemQuantityModifierInterface;
35
use Sylius\Component\Order\OrderTransitions;
36
use Sylius\Component\Payment\Model\PaymentInterface;
37
use Sylius\Component\Payment\Model\PaymentMethodInterface;
38
use Sylius\Component\Payment\PaymentTransitions;
39
use Sylius\Component\Product\Resolver\ProductVariantResolverInterface;
40
use Sylius\Component\Resource\Factory\FactoryInterface;
41
use Sylius\Component\Resource\Repository\RepositoryInterface;
42
use Sylius\Component\Shipping\ShipmentTransitions;
43
44
final class OrderContext implements Context
45
{
46
    /**
47
     * @var SharedStorageInterface
48
     */
49
    private $sharedStorage;
50
51
    /**
52
     * @var OrderRepositoryInterface
53
     */
54
    private $orderRepository;
55
56
    /**
57
     * @var FactoryInterface
58
     */
59
    private $orderFactory;
60
61
    /**
62
     * @var FactoryInterface
63
     */
64
    private $orderItemFactory;
65
66
    /**
67
     * @var OrderItemQuantityModifierInterface
68
     */
69
    private $itemQuantityModifier;
70
71
    /**
72
     * @var FactoryInterface
73
     */
74
    private $customerFactory;
75
76
    /**
77
     * @var RepositoryInterface
78
     */
79
    private $customerRepository;
80
81
    /**
82
     * @var ObjectManager
83
     */
84
    private $objectManager;
85
86
    /**
87
     * @var StateMachineFactoryInterface
88
     */
89
    private $stateMachineFactory;
90
91
    /**
92
     * @var ProductVariantResolverInterface
93
     */
94
    private $variantResolver;
95
96
    /**
97
     * @param SharedStorageInterface $sharedStorage
98
     * @param OrderRepositoryInterface $orderRepository
99
     * @param FactoryInterface $orderFactory
100
     * @param FactoryInterface $orderItemFactory
101
     * @param OrderItemQuantityModifierInterface $itemQuantityModifier
102
     * @param FactoryInterface $customerFactory
103
     * @param RepositoryInterface $customerRepository
104
     * @param ObjectManager $objectManager
105
     * @param StateMachineFactoryInterface $stateMachineFactory
106
     * @param ProductVariantResolverInterface $variantResolver
107
     */
108
    public function __construct(
109
        SharedStorageInterface $sharedStorage,
110
        OrderRepositoryInterface $orderRepository,
111
        FactoryInterface $orderFactory,
112
        FactoryInterface $orderItemFactory,
113
        OrderItemQuantityModifierInterface $itemQuantityModifier,
114
        FactoryInterface $customerFactory,
115
        RepositoryInterface $customerRepository,
116
        ObjectManager $objectManager,
117
        StateMachineFactoryInterface $stateMachineFactory,
118
        ProductVariantResolverInterface $variantResolver
119
    ) {
120
        $this->sharedStorage = $sharedStorage;
121
        $this->orderRepository = $orderRepository;
122
        $this->orderFactory = $orderFactory;
123
        $this->orderItemFactory = $orderItemFactory;
124
        $this->itemQuantityModifier = $itemQuantityModifier;
125
        $this->customerFactory = $customerFactory;
126
        $this->customerRepository = $customerRepository;
127
        $this->objectManager = $objectManager;
128
        $this->stateMachineFactory = $stateMachineFactory;
129
        $this->variantResolver = $variantResolver;
130
    }
131
132
    /**
133
     * @Given /^there is (?:a|another) (customer "[^"]+") that placed an order$/
134
     * @Given /^there is (?:a|another) (customer "[^"]+") that placed (an order "[^"]+")$/
135
     * @Given a customer :customer placed an order :orderNumber
136
     * @Given the customer :customer has already placed an order :orderNumber
137
     */
138
    public function thereIsCustomerThatPlacedOrder(CustomerInterface $customer, $orderNumber = null)
139
    {
140
        $order = $this->createOrder($customer, $orderNumber);
141
142
        $this->sharedStorage->set('order', $order);
143
144
        $this->orderRepository->add($order);
145
    }
146
147
    /**
148
     * @Given /^the guest customer placed order with ("[^"]+" product) for "([^"]+)" and ("[^"]+" based shipping address) with ("[^"]+" shipping method) and ("[^"]+" payment)$/
149
     */
150
    public function theGuestCustomerPlacedOrderWithForAndBasedShippingAddress(
151
        ProductInterface $product,
152
        string $email,
153
        AddressInterface $address,
154
        ShippingMethodInterface $shippingMethod,
155
        PaymentMethodInterface $paymentMethod
156
    ) {
157
        /** @var CustomerInterface $customer */
158
        $customer = $this->customerFactory->createNew();
159
        $customer->setEmail($email);
160
        $customer->setFirstName('John');
161
        $customer->setLastName('Doe');
162
163
        $this->customerRepository->add($customer);
164
165
        $this->placeOrder($product, $shippingMethod, $address, $paymentMethod, $customer, 1);
166
        $this->objectManager->flush();
167
    }
168
169
    /**
170
     * @Given a customer :customer added something to cart
171
     */
172
    public function customerStartedCheckout(CustomerInterface $customer)
173
    {
174
        $cart = $this->createCart($customer);
175
176
        $this->sharedStorage->set('cart', $cart);
177
178
        $this->orderRepository->add($cart);
179
    }
180
181
    /**
182
     * @Given /^(I) placed (an order "[^"]+")$/
183
     */
184
    public function iPlacedAnOrder(ShopUserInterface $user, $orderNumber)
185
    {
186
        $customer = $user->getCustomer();
187
        $order = $this->createOrder($customer, $orderNumber);
188
189
        $this->sharedStorage->set('order', $order);
190
191
        $this->orderRepository->add($order);
192
    }
193
194
    /**
195
     * @Given /^the customer ("[^"]+" addressed it to "[^"]+", "[^"]+" "[^"]+" in the "[^"]+"(?:|, "[^"]+"))$/
196
     * @Given /^I (addressed it to "[^"]+", "[^"]+", "[^"]+" "[^"]+" in the "[^"]+"(?:|, "[^"]+"))$/
197
     */
198
    public function theCustomerAddressedItTo(AddressInterface $address)
199
    {
200
        /** @var OrderInterface $order */
201
        $order = $this->sharedStorage->get('order');
202
        $order->setShippingAddress($address);
203
204
        $this->objectManager->flush();
205
    }
206
207
    /**
208
     * @Given the customer changed shipping address' street to :street
209
     */
210
    public function theCustomerChangedShippingAddressStreetTo($street)
211
    {
212
        /** @var OrderInterface $order */
213
        $order = $this->sharedStorage->get('order');
214
215
        $shippingAddress = $order->getShippingAddress();
216
        $shippingAddress->setStreet($street);
217
218
        $this->objectManager->flush();
219
220
        $this->applyTransitionOnOrderCheckout($order, OrderCheckoutTransitions::TRANSITION_ADDRESS);
221
    }
222
223
    /**
224
     * @Given /^the customer set the billing (address as "([^"]+)", "([^"]+)", "([^"]+)", "([^"]+)", "([^"]+)")$/
225
     * @Given /^for the billing address (of "[^"]+" in the "[^"]+", "[^"]+" "[^"]+", "[^"]+")$/
226
     * @Given /^for the billing address (of "[^"]+" in the "[^"]+", "[^"]+" "([^"]+)", "[^"]+", "[^"]+")$/
227
     */
228
    public function forTheBillingAddressOf(AddressInterface $address)
229
    {
230
        /** @var OrderInterface $order */
231
        $order = $this->sharedStorage->get('order');
232
233
        $order->setBillingAddress($address);
234
235
        $this->applyTransitionOnOrderCheckout($order, OrderCheckoutTransitions::TRANSITION_ADDRESS);
236
237
        $this->objectManager->flush();
238
    }
239
240
    /**
241
     * @Given /^the customer ("[^"]+" addressed it to "[^"]+", "[^"]+" "[^"]+" in the "[^"]+") with identical billing address$/
242
     * @Given /^I (addressed it to "[^"]+", "[^"]+", "[^"]+" "[^"]+" in the "[^"]+") with identical billing address$/
243
     */
244
    public function theCustomerAddressedItToWithIdenticalBillingAddress(AddressInterface $address)
245
    {
246
        $this->theCustomerAddressedItTo($address);
247
        $this->forTheBillingAddressOf(clone $address);
248
    }
249
250
    /**
251
     * @Given /^the customer chose ("[^"]+" shipping method) (to "[^"]+") with ("[^"]+" payment)$/
252
     * @Given /^I chose ("[^"]+" shipping method) (to "[^"]+") with ("[^"]+" payment)$/
253
     */
254
    public function theCustomerChoseShippingToWithPayment(
255
        ShippingMethodInterface $shippingMethod,
256
        AddressInterface $address,
257
        PaymentMethodInterface $paymentMethod
258
    ) {
259
        /** @var OrderInterface $order */
260
        $order = $this->sharedStorage->get('order');
261
262
        $this->checkoutUsing($order, $shippingMethod, $address, $paymentMethod);
263
264
        $this->objectManager->flush();
265
    }
266
267
    /**
268
     * @Given /^the customer chose ("[^"]+" shipping method) with ("[^"]+" payment)$/
269
     * @Given /^I chose ("[^"]+" shipping method) with ("[^"]+" payment)$/
270
     */
271
    public function theCustomerChoseShippingWithPayment(
272
        ShippingMethodInterface $shippingMethod,
273
        PaymentMethodInterface $paymentMethod
274
    ) {
275
        /** @var OrderInterface $order */
276
        $order = $this->sharedStorage->get('order');
277
278
        $this->proceedSelectingShippingAndPaymentMethod($order, $shippingMethod, $paymentMethod);
279
280
        $this->objectManager->flush();
281
    }
282
283
    /**
284
     * @Given /^the customer chose ("[^"]+" shipping method)$/
285
     */
286
    public function theCustomerChoseShippingMethod(ShippingMethodInterface $shippingMethod)
287
    {
288
        /** @var OrderInterface $order */
289
        $order = $this->sharedStorage->get('order');
290
291
        foreach ($order->getShipments() as $shipment) {
292
            $shipment->setMethod($shippingMethod);
293
        }
294
295
        $this->applyTransitionOnOrderCheckout($order, OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING);
296
        $this->applyTransitionOnOrderCheckout($order, OrderCheckoutTransitions::TRANSITION_COMPLETE);
297
        if (!$order->getPayments()->isEmpty()) {
298
            $this->stateMachineFactory->get($order, OrderPaymentTransitions::GRAPH)->apply(OrderPaymentTransitions::TRANSITION_PAY);
299
        }
300
301
        $this->objectManager->flush();
302
    }
303
304
    /**
305
     * @Given /^the customer chose ("[^"]+" payment)$/
306
     */
307
    public function theCustomerChosePayment(PaymentMethodInterface $paymentMethod)
308
    {
309
        /** @var OrderInterface $order */
310
        $order = $this->sharedStorage->get('order');
311
312
        foreach ($order->getPayments() as $payment) {
313
            $payment->setMethod($paymentMethod);
314
        }
315
316
        $this->applyTransitionOnOrderCheckout($order, OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT);
317
        $this->applyTransitionOnOrderCheckout($order, OrderCheckoutTransitions::TRANSITION_COMPLETE);
318
319
        $this->objectManager->flush();
320
    }
321
322
    /**
323
     * @Given the customer bought a single :product
324
     * @Given I bought a single :product
325
     */
326
    public function theCustomerBoughtSingleProduct(ProductInterface $product)
327
    {
328
        $this->addProductVariantToOrder($this->variantResolver->getVariant($product), 1);
329
330
        $this->objectManager->flush();
331
    }
332
333
    /**
334
     * @Given /^the customer bought ((?:a|an) "[^"]+") and ((?:a|an) "[^"]+")$/
335
     * @Given /^I bought ((?:a|an) "[^"]+") and ((?:a|an) "[^"]+")$/
336
     */
337
    public function theCustomerBoughtProductAndProduct(ProductInterface $product, ProductInterface $secondProduct)
338
    {
339
        $this->theCustomerBoughtSingleProduct($product);
340
        $this->theCustomerBoughtSingleProduct($secondProduct);
341
    }
342
343
    /**
344
     * @Given /^the customer bought (\d+) ("[^"]+" products)$/
345
     */
346
    public function theCustomerBoughtSeveralProducts($quantity, ProductInterface $product)
347
    {
348
        $variant = $this->variantResolver->getVariant($product);
349
        $this->addProductVariantToOrder($variant, $quantity);
350
351
        $this->objectManager->flush();
352
    }
353
354
    /**
355
     * @Given /^the customer bought ([^"]+) units of ("[^"]+" variant of product "[^"]+")$/
356
     */
357
    public function theCustomerBoughtSeveralVariantsOfProduct($quantity, ProductVariantInterface $variant)
358
    {
359
        $this->addProductVariantToOrder($variant, $quantity);
360
361
        $this->objectManager->flush();
362
    }
363
364
    /**
365
     * @Given /^the customer bought a single ("[^"]+" variant of product "[^"]+")$/
366
     */
367
    public function theCustomerBoughtSingleProductVariant(ProductVariantInterface $productVariant)
368
    {
369
        $this->addProductVariantToOrder($productVariant);
370
371
        $this->objectManager->flush();
372
    }
373
374
    /**
375
     * @Given the customer bought a single :product using :coupon coupon
376
     * @Given I bought a single :product using :coupon coupon
377
     */
378
    public function theCustomerBoughtSingleUsing(ProductInterface $product, PromotionCouponInterface $coupon)
379
    {
380
        $order = $this->addProductVariantToOrder($this->variantResolver->getVariant($product));
381
        $order->setPromotionCoupon($coupon);
382
383
        $this->objectManager->flush();
384
    }
385
386
    /**
387
     * @Given I used :coupon coupon
388
     */
389
    public function iUsedCoupon(PromotionCouponInterface $coupon)
390
    {
391
        $order = $this->sharedStorage->get('order');
392
        $order->setPromotionCoupon($coupon);
393
394
        $this->objectManager->flush();
395
    }
396
397
    /**
398
     * @Given /^(I) have already placed (\d+) orders choosing ("[^"]+" product), ("[^"]+" shipping method) (to "[^"]+") with ("[^"]+" payment)$/
399
     */
400
    public function iHaveAlreadyPlacedOrderNthTimes(
401
        ShopUserInterface $user,
402
        $numberOfOrders,
403
        ProductInterface $product,
404
        ShippingMethodInterface $shippingMethod,
405
        AddressInterface $address,
406
        PaymentMethodInterface $paymentMethod
407
    ) {
408
        $customer = $user->getCustomer();
409
        for ($i = 0; $i < $numberOfOrders; ++$i) {
410
            $this->placeOrder($product, $shippingMethod, $address, $paymentMethod, $customer, $i);
411
        }
412
413
        $this->objectManager->flush();
414
    }
415
416
    /**
417
     * @Given /^(this customer) has(?:| also) placed (an order "[^"]+") at "([^"]+)"$/
418
     */
419
    public function thisCustomerHasPlacedAnOrderAtDate(CustomerInterface $customer, $number, $checkoutCompletedAt)
420
    {
421
        $order = $this->createOrder($customer, $number);
422
        $order->setCheckoutCompletedAt(new \DateTime($checkoutCompletedAt));
423
        $order->setState(OrderInterface::STATE_NEW);
424
425
        $this->orderRepository->add($order);
426
    }
427
428
    /**
429
     * @Given /^(this customer) has(?:| also) placed (an order "[^"]+") on a (channel "[^"]+")$/
430
     */
431
    public function thisCustomerHasPlacedAnOrderOnAChannel(CustomerInterface $customer, $number, $channel)
432
    {
433
        $order = $this->createOrder($customer, $number, $channel);
434
        $order->setState(OrderInterface::STATE_NEW);
435
436
        $this->orderRepository->add($order);
437
        $this->sharedStorage->set('order', $order);
438
    }
439
440
    /**
441
     * @Given /^(this customer) has(?:| also) started checkout on a (channel "[^"]+")$/
442
     */
443
    public function thisCustomerHasStartedCheckoutOnAChannel(CustomerInterface $customer, $channel)
444
    {
445
        $order = $this->createOrder($customer, null, $channel);
446
447
        $this->orderRepository->add($order);
448
        $this->sharedStorage->set('order', $order);
449
    }
450
451
    /**
452
     * @Given /^(customer "[^"]+"|this customer) has(?:| also) placed (\d+) orders on the ("[^"]+" channel) in each buying (\d+) ("[^"]+" products?)$/
453
     */
454
    public function thisCustomerPlacedOrdersOnChannelBuyingProducts(
455
        CustomerInterface $customer,
456
        int $orderCount,
457
        ChannelInterface $channel,
458
        int $productCount,
459
        ProductInterface $product
460
    ): void {
461
        $this->createOrdersForCustomer($customer, $orderCount, $channel, $productCount, $product);
462
    }
463
464
    /**
465
     * @Given /^(customer "[^"]+"|this customer) has(?:| also) fulfilled (\d+) orders placed on the ("[^"]+" channel) in each buying (\d+) ("[^"]+" products?)$/
466
     */
467
    public function thisCustomerFulfilledOrdersPlacedOnChannelBuyingProducts(
468
        CustomerInterface $customer,
469
        int $orderCount,
470
        ChannelInterface $channel,
471
        int $productCount,
472
        ProductInterface $product
473
    ): void {
474
        $this->createOrdersForCustomer($customer, $orderCount, $channel, $productCount, $product, true);
475
    }
476
477
    /**
478
     * @Given :numberOfCustomers customers have added products to the cart for total of :total
479
     */
480
    public function customersHaveAddedProductsToTheCartForTotalOf($numberOfCustomers, $total)
481
    {
482
        $customers = $this->generateCustomers($numberOfCustomers);
483
484
        $sampleProductVariant = $this->sharedStorage->get('variant');
485
        $total = $this->getPriceFromString($total);
486
487
        for ($i = 0; $i < $numberOfCustomers; ++$i) {
488
            $order = $this->createCart($customers[random_int(0, $numberOfCustomers - 1)]);
489
490
            $price = $i === ($numberOfCustomers - 1) ? $total : random_int(1, $total);
491
            $total -= $price;
492
493
            $this->addVariantWithPriceToOrder($order, $sampleProductVariant, $price);
494
495
            $this->objectManager->persist($order);
496
        }
497
498
        $this->objectManager->flush();
499
    }
500
501
    /**
502
     * @Given a single customer has placed an order for total of :total
503
     * @Given :numberOfCustomers customers have placed :numberOfOrders orders for total of :total
504
     * @Given then :numberOfCustomers more customers have placed :numberOfOrders orders for total of :total
505
     */
506
    public function customersHavePlacedOrdersForTotalOf(
507
        int $numberOfCustomers = 1,
508
        int $numberOfOrders = 1,
509
        string $total
510
    ): void {
511
        $this->createOrders($numberOfCustomers, $numberOfOrders, $total);
512
    }
513
514
    /**
515
     * @Given :numberOfCustomers customers have fulfilled :numberOfOrders orders placed for total of :total
516
     * @Given then :numberOfCustomers more customers have fulfilled :numberOfOrders orders placed for total of :total
517
     */
518
    public function customersHaveFulfilledOrdersPlacedForTotalOf(
519
        int $numberOfCustomers,
520
        int $numberOfOrders,
521
        string $total
522
    ): void {
523
        $this->createOrders($numberOfCustomers, $numberOfOrders, $total, true);
524
    }
525
526
    /**
527
     * @Given :numberOfCustomers customers have placed :numberOfOrders orders for total of :total mostly :product product
528
     * @Given then :numberOfCustomers more customers have placed :numberOfOrders orders for total of :total mostly :product product
529
     */
530
    public function customersHavePlacedOrdersForTotalOfMostlyProduct(
531
        int $numberOfCustomers,
532
        int $numberOfOrders,
533
        string $total,
534
        ProductInterface $product
535
    ): void {
536
        $this->createOrdersWithProduct($numberOfCustomers, $numberOfOrders, $total, $product);
537
    }
538
539
    /**
540
     * @Given :numberOfCustomers customers have fulfilled :numberOfOrders orders placed for total of :total mostly :product product
541
     * @Given then :numberOfCustomers more customers have fulfilled :numberOfOrders orders placed for total of :total mostly :product product
542
     */
543
    public function customersHaveFulfilledOrdersPlacedForTotalOfMostlyProduct(
544
        int $numberOfCustomers,
545
        int $numberOfOrders,
546
        string $total,
547
        ProductInterface $product
548
    ): void {
549
        $this->createOrdersWithProduct($numberOfCustomers, $numberOfOrders, $total, $product, true);
550
    }
551
552
    /**
553
     * @Given /^(this customer) has(?:| also) placed (an order "[^"]+") buying a single ("[^"]+" product) for ("[^"]+") on the ("[^"]+" channel)$/
554
     */
555
    public function customerHasPlacedAnOrderBuyingASingleProductForOnTheChannel(
556
        CustomerInterface $customer,
557
        $orderNumber,
558
        ProductInterface $product,
559
        $price,
560
        ChannelInterface $channel
561
    ) {
562
        $order = $this->createOrder($customer, $orderNumber, $channel);
563
        $order->setState(OrderInterface::STATE_NEW);
564
565
        $this->addVariantWithPriceToOrder($order, $product->getVariants()->first(), $price);
566
567
        $this->orderRepository->add($order);
568
        $this->sharedStorage->set('order', $order);
569
    }
570
571
    /**
572
     * @Given /^(this order) is already paid$/
573
     * @Given the order :order is already paid
574
     */
575
    public function thisOrderIsAlreadyPaid(OrderInterface $order)
576
    {
577
        $this->applyPaymentTransitionOnOrder($order, PaymentTransitions::TRANSITION_COMPLETE);
578
579
        $this->objectManager->flush();
580
    }
581
582
    /**
583
     * @Given /^(this order) has been refunded$/
584
     */
585
    public function thisOrderHasBeenRefunded(OrderInterface $order)
586
    {
587
        $this->applyPaymentTransitionOnOrder($order, PaymentTransitions::TRANSITION_REFUND);
588
589
        $this->objectManager->flush();
590
    }
591
592
    /**
593
     * @Given /^the customer cancelled (this order)$/
594
     * @Given /^(this order) was cancelled$/
595
     * @Given the order :order was cancelled
596
     * @Given /^I cancelled (this order)$/
597
     */
598
    public function theCustomerCancelledThisOrder(OrderInterface $order)
599
    {
600
        $this->stateMachineFactory->get($order, OrderTransitions::GRAPH)->apply(OrderTransitions::TRANSITION_CANCEL);
601
602
        $this->objectManager->flush();
603
    }
604
605
    /**
606
     * @Given /^I cancelled my last order$/
607
     */
608
    public function theCustomerCancelledMyLastOrder()
609
    {
610
        $order = $this->sharedStorage->get('order');
611
        $this->stateMachineFactory->get($order, OrderTransitions::GRAPH)->apply(OrderTransitions::TRANSITION_CANCEL);
612
613
        $this->objectManager->flush();
614
    }
615
616
    /**
617
     * @Given /^(this order) has already been shipped$/
618
     */
619
    public function thisOrderHasAlreadyBeenShipped(OrderInterface $order)
620
    {
621
        $this->applyShipmentTransitionOnOrder($order, ShipmentTransitions::TRANSITION_SHIP);
622
623
        $this->objectManager->flush();
624
    }
625
626
    /**
627
     * @When the customer used coupon :coupon
628
     */
629
    public function theCustomerUsedCoupon(PromotionCouponInterface $coupon)
630
    {
631
        /** @var OrderInterface $order */
632
        $order = $this->sharedStorage->get('order');
633
        $order->setPromotionCoupon($coupon);
634
635
        $this->objectManager->flush();
636
    }
637
638
    /**
639
     * @param OrderInterface $order
640
     * @param string $transition
641
     */
642
    private function applyShipmentTransitionOnOrder(OrderInterface $order, $transition)
643
    {
644
        foreach ($order->getShipments() as $shipment) {
645
            $this->stateMachineFactory->get($shipment, ShipmentTransitions::GRAPH)->apply($transition);
646
        }
647
    }
648
649
    /**
650
     * @param OrderInterface $order
651
     * @param string $transition
652
     */
653
    private function applyPaymentTransitionOnOrder(OrderInterface $order, $transition)
654
    {
655
        foreach ($order->getPayments() as $payment) {
656
            $this->stateMachineFactory->get($payment, PaymentTransitions::GRAPH)->apply($transition);
657
        }
658
    }
659
660
    /**
661
     * @param OrderInterface $order
662
     * @param string $transition
663
     */
664
    private function applyTransitionOnOrderCheckout(OrderInterface $order, $transition)
665
    {
666
        $this->stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH)->apply($transition);
667
    }
668
669
    /**
670
     * @param OrderInterface $order
671
     * @param string $transition
672
     */
673
    private function applyTransitionOnOrder(OrderInterface $order, string $transition): void
674
    {
675
        $this->stateMachineFactory->get($order, OrderTransitions::GRAPH)->apply($transition);
676
    }
677
678
    /**
679
     * @param ProductVariantInterface $productVariant
680
     * @param int $quantity
681
     *
682
     * @return OrderInterface
683
     */
684
    private function addProductVariantToOrder(ProductVariantInterface $productVariant, $quantity = 1)
685
    {
686
        $order = $this->sharedStorage->get('order');
687
688
        $this->addProductVariantsToOrderWithChannelPrice(
689
            $order,
690
            $this->sharedStorage->get('channel'),
691
            $productVariant,
692
            (int) $quantity
693
        );
694
695
        return $order;
696
    }
697
698
    /**
699
     * @param OrderInterface $order
700
     * @param ChannelInterface $channel
701
     * @param ProductVariantInterface $productVariant
702
     * @param int $quantity
703
     */
704
    private function addProductVariantsToOrderWithChannelPrice(
705
        OrderInterface $order,
706
        ChannelInterface $channel,
707
        ProductVariantInterface $productVariant,
708
        int $quantity = 1
709
    ) {
710
        /** @var OrderItemInterface $item */
711
        $item = $this->orderItemFactory->createNew();
712
        $item->setVariant($productVariant);
713
714
        /** @var ChannelPricingInterface $channelPricing */
715
        $channelPricing = $productVariant->getChannelPricingForChannel($channel);
716
        $item->setUnitPrice($channelPricing->getPrice());
717
718
        $this->itemQuantityModifier->modify($item, $quantity);
719
720
        $order->addItem($item);
721
    }
722
723
    /**
724
     * @param CustomerInterface $customer
725
     * @param string $number
726
     * @param ChannelInterface|null $channel
727
     * @param string|null $localeCode
728
     *
729
     * @return OrderInterface
730
     */
731
    private function createOrder(
732
        CustomerInterface $customer,
733
        $number = null,
734
        ChannelInterface $channel = null,
735
        $localeCode = null
736
    ) {
737
        $order = $this->createCart($customer, $channel, $localeCode);
738
739
        if (null !== $number) {
740
            $order->setNumber($number);
741
        }
742
743
        $order->completeCheckout();
744
745
        return $order;
746
    }
747
748
    /**
749
     * @param CustomerInterface $customer
750
     * @param ChannelInterface|null $channel
751
     * @param string|null $localeCode
752
     *
753
     * @return OrderInterface
754
     */
755
    private function createCart(
756
        CustomerInterface $customer,
757
        ChannelInterface $channel = null,
758
        $localeCode = null
759
    ) {
760
        /** @var OrderInterface $order */
761
        $order = $this->orderFactory->createNew();
762
763
        $order->setCustomer($customer);
764
        $order->setChannel($channel ?? $this->sharedStorage->get('channel'));
765
        $order->setLocaleCode($localeCode ?? $this->sharedStorage->get('locale')->getCode());
766
        $order->setCurrencyCode($order->getChannel()->getBaseCurrency()->getCode());
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Sylius\Component\Channel\Model\ChannelInterface as the method getBaseCurrency() does only exist in the following implementations of said interface: Sylius\Component\Core\Model\Channel.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
767
768
        return $order;
769
    }
770
771
    /**
772
     * @param int $count
773
     *
774
     * @return CustomerInterface[]
775
     */
776
    private function generateCustomers($count)
777
    {
778
        $customers = [];
779
780
        for ($i = 0; $i < $count; ++$i) {
781
            $customer = $this->customerFactory->createNew();
782
            $customer->setEmail(sprintf('john%[email protected]', uniqid()));
783
            $customer->setFirstname('John');
784
            $customer->setLastname('Doe' . $i);
785
786
            $customers[] = $customer;
787
788
            $this->customerRepository->add($customer);
789
        }
790
791
        return $customers;
792
    }
793
794
    /**
795
     * @param string $price
796
     *
797
     * @return int
798
     */
799
    private function getPriceFromString($price)
800
    {
801
        return (int) round(str_replace(['€', '£', '$'], '', $price) * 100, 2);
802
    }
803
804
    /**
805
     * @param OrderInterface $order
806
     * @param ShippingMethodInterface $shippingMethod
807
     * @param AddressInterface $address
808
     * @param PaymentMethodInterface $paymentMethod
809
     */
810
    private function checkoutUsing(
811
        OrderInterface $order,
812
        ShippingMethodInterface $shippingMethod,
813
        AddressInterface $address,
814
        PaymentMethodInterface $paymentMethod
815
    ) {
816
        $order->setShippingAddress($address);
817
        $order->setBillingAddress(clone $address);
818
819
        $this->applyTransitionOnOrderCheckout($order, OrderCheckoutTransitions::TRANSITION_ADDRESS);
820
821
        $this->proceedSelectingShippingAndPaymentMethod($order, $shippingMethod, $paymentMethod);
822
    }
823
824
    /**
825
     * @param OrderInterface $order
826
     * @param ShippingMethodInterface $shippingMethod
827
     * @param PaymentMethodInterface $paymentMethod
828
     */
829
    private function proceedSelectingShippingAndPaymentMethod(OrderInterface $order, ShippingMethodInterface $shippingMethod, PaymentMethodInterface $paymentMethod)
830
    {
831
        foreach ($order->getShipments() as $shipment) {
832
            $shipment->setMethod($shippingMethod);
833
        }
834
        $this->applyTransitionOnOrderCheckout($order, OrderCheckoutTransitions::TRANSITION_SELECT_SHIPPING);
835
836
        $payment = $order->getLastPayment(PaymentInterface::STATE_CART);
837
        $payment->setMethod($paymentMethod);
838
839
        $this->applyTransitionOnOrderCheckout($order, OrderCheckoutTransitions::TRANSITION_SELECT_PAYMENT);
840
        $this->applyTransitionOnOrderCheckout($order, OrderCheckoutTransitions::TRANSITION_COMPLETE);
841
    }
842
843
    /**
844
     * @param OrderInterface $order
845
     * @param ProductVariantInterface $variant
846
     * @param int $price
847
     */
848
    private function addVariantWithPriceToOrder(OrderInterface $order, ProductVariantInterface $variant, $price)
849
    {
850
        $item = $this->orderItemFactory->createNew();
851
        $item->setVariant($variant);
852
        $item->setUnitPrice($price);
853
854
        $this->itemQuantityModifier->modify($item, 1);
855
856
        $order->addItem($item);
857
    }
858
859
    /**
860
     * @param int $numberOfCustomers
861
     * @param int $numberOfOrders
862
     * @param string $total
863
     * @param bool $isFulfilled
864
     */
865
    private function createOrders(
866
        int $numberOfCustomers,
867
        int $numberOfOrders,
868
        string $total,
869
        bool $isFulfilled = false
870
    ): void {
871
        $customers = $this->generateCustomers($numberOfCustomers);
872
        $sampleProductVariant = $this->sharedStorage->get('variant');
873
        $total = $this->getPriceFromString($total);
874
875
        for ($i = 0; $i < $numberOfOrders; ++$i) {
876
            $order = $this->createOrder($customers[random_int(0, $numberOfCustomers - 1)], '#' . uniqid());
877
            $order->setState(OrderInterface::STATE_NEW); // Temporary, we should use checkout to place these orders.
878
            $this->applyPaymentTransitionOnOrder($order, PaymentTransitions::TRANSITION_COMPLETE);
879
880
            $price = $i === ($numberOfOrders - 1) ? $total : random_int(1, $total);
881
            $total -= $price;
882
883
            $this->addVariantWithPriceToOrder($order, $sampleProductVariant, $price);
884
885
            if ($isFulfilled) {
886
                $this->applyTransitionOnOrder($order, OrderTransitions::TRANSITION_FULFILL);
887
            }
888
889
            $this->objectManager->persist($order);
890
            $this->sharedStorage->set('order', $order);
891
        }
892
893
        $this->objectManager->flush();
894
    }
895
896
    /**
897
     * @param int $numberOfCustomers
898
     * @param int $numberOfOrders
899
     * @param string $total
900
     * @param ProductInterface $product
901
     * @param bool $isFulfilled
902
     */
903
    private function createOrdersWithProduct(
904
        int $numberOfCustomers,
905
        int $numberOfOrders,
906
        string $total,
907
        ProductInterface $product,
908
        bool $isFulfilled = false
909
    ): void {
910
        $customers = $this->generateCustomers($numberOfCustomers);
911
        $sampleProductVariant = $product->getVariants()->first();
912
        $total = $this->getPriceFromString($total);
913
914
        for ($i = 0; $i < $numberOfOrders; ++$i) {
915
            $order = $this->createOrder($customers[random_int(0, $numberOfCustomers - 1)], '#' . uniqid(), $product->getChannels()->first());
916
            $order->setState(OrderInterface::STATE_NEW);
917
            $this->applyPaymentTransitionOnOrder($order, PaymentTransitions::TRANSITION_COMPLETE);
918
919
            $price = $i === ($numberOfOrders - 1) ? $total : random_int(1, $total);
920
            $total -= $price;
921
922
            $this->addVariantWithPriceToOrder($order, $sampleProductVariant, $price);
923
924
            if ($isFulfilled) {
925
                $this->applyTransitionOnOrder($order, OrderTransitions::TRANSITION_FULFILL);
926
            }
927
928
            $this->objectManager->persist($order);
929
        }
930
931
        $this->objectManager->flush();
932
    }
933
934
    /**
935
     * @param CustomerInterface $customer
936
     * @param int $orderCount
937
     * @param ChannelInterface $channel
938
     * @param int $productCount
939
     * @param ProductInterface $product
940
     * @param bool $isFulfilled
941
     */
942
    private function createOrdersForCustomer(
943
        CustomerInterface $customer,
944
        int $orderCount,
945
        ChannelInterface $channel,
946
        int $productCount,
947
        ProductInterface $product,
948
        bool $isFulfilled = false
949
    ): void {
950
        for ($i = 0; $i < $orderCount; ++$i) {
951
            $order = $this->createOrder($customer, uniqid('#'), $channel);
952
953
            $this->addProductVariantsToOrderWithChannelPrice(
954
                $order,
955
                $channel,
956
                $this->variantResolver->getVariant($product),
957
                (int) $productCount
958
            );
959
960
            $order->setState($isFulfilled ? OrderInterface::STATE_FULFILLED : OrderInterface::STATE_NEW);
961
962
            $this->objectManager->persist($order);
963
        }
964
965
        $this->objectManager->flush();
966
    }
967
968
    /**
969
     * @param ProductInterface $product
970
     * @param ShippingMethodInterface $shippingMethod
971
     * @param AddressInterface $address
972
     * @param PaymentMethodInterface $paymentMethod
973
     * @param CustomerInterface $customer
974
     * @param int $number
975
     */
976
    private function placeOrder(
977
        ProductInterface $product,
978
        ShippingMethodInterface $shippingMethod,
979
        AddressInterface $address,
980
        PaymentMethodInterface $paymentMethod,
981
        CustomerInterface $customer,
982
        int $number
983
    ): void {
984
        /** @var ProductVariantInterface $variant */
985
        $variant = $this->variantResolver->getVariant($product);
986
987
        /** @var ChannelPricingInterface $channelPricing */
988
        $channelPricing = $variant->getChannelPricingForChannel($this->sharedStorage->get('channel'));
989
990
        /** @var OrderItemInterface $item */
991
        $item = $this->orderItemFactory->createNew();
992
        $item->setVariant($variant);
993
        $item->setUnitPrice($channelPricing->getPrice());
994
995
        $this->itemQuantityModifier->modify($item, 1);
996
997
        $order = $this->createOrder($customer, '#00000' . $number);
998
        $order->addItem($item);
999
1000
        $this->checkoutUsing($order, $shippingMethod, clone $address, $paymentMethod);
1001
        $this->applyPaymentTransitionOnOrder($order, PaymentTransitions::TRANSITION_COMPLETE);
1002
1003
        $this->objectManager->persist($order);
1004
        $this->sharedStorage->set('order', $order);
1005
    }
1006
}
1007