PromotionContext   F
last analyzed

Complexity

Total Complexity 64

Size/Duplication

Total Lines 831
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 14

Importance

Changes 0
Metric Value
wmc 64
lcom 1
cbo 14
dl 0
loc 831
rs 3.049
c 0
b 0
f 0

59 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 17 1
A thereIsPromotion() 0 13 2
A thereIsAPromotionWithPriority() 0 11 1
A thereIsAnExclusivePromotionWithPriority() 0 12 1
A thereIsPromotionLimitedToUsages() 0 9 1
A thereIsPromotionWithCoupon() 0 15 1
A thisPromotionHasCoupons() 0 11 2
A thisPromotionHasExpired() 0 6 1
A thisPromotionExpiresTomorrow() 0 6 1
A thisPromotionHasStartedYesterday() 0 6 1
A thisPromotionStartsTomorrow() 0 6 1
A thisCouponHasExpired() 0 6 1
A thisCouponExpiresTomorrow() 0 6 1
A thisCouponHasReachedItsUsageLimit() 0 7 1
A thisCouponCanBeUsedNTimes() 0 6 1
A thisCouponCanBeUsedTwicePerCustomer() 0 6 1
A itGivesFixedDiscountToEveryOrder() 0 4 1
A thisPromotionGivesDiscountToEveryOrderInTheChannelAndDiscountToEveryOrderInTheChannel() 0 17 1
A itGivesPercentageDiscountToEveryOrder() 0 4 1
A itGivesFixedDiscountToEveryOrderWithQuantityAtLeast() 0 9 1
A itGivesFixedDiscountToEveryOrderWithItemsTotalAtLeast() 0 10 1
A itGivesPercentageDiscountToEveryOrderWithItemsTotalAtLeast() 0 9 1
A itGivesOffOnEveryItemWhenItemTotalExceeds() 0 10 1
A itGivesPercentageDiscountOnShippingToEveryOrder() 0 7 1
A thePromotionGivesFreeShippingToEveryOrder() 0 4 1
A itGivesPercentageOffEveryProductClassifiedAs() 0 7 1
A itGivesFixedOffEveryProductClassifiedAs() 0 7 1
A thisPromotionGivesOffOnEveryProductWithMinimumPriceAt() 0 7 1
A thisPromotionGivesOffOnEveryProductPricedBetween() 0 12 1
A thisPromotionPercentageGivesOffOnEveryProductWithMinimumPriceAt() 0 7 1
A thisPromotionPercentageGivesOffOnEveryProductPricedBetween() 0 12 1
A thePromotionGivesOffIfOrderContainsProductsClassifiedAs() 0 9 1
A thePromotionGivesOffIfOrderContainsProductsClassifiedAsOr() 0 9 1
A thePromotionGivesOffIfOrderContainsProductsClassifiedAsAndPricedAt() 0 11 1
A itGivesFixedOffCustomersNthOrder() 0 6 1
A itGivesPercentageOffCustomersNthOrder() 0 6 1
A itGivesPercentageOffOnEveryProductClassifiedAsAndAmountDiscountOnOrder() 0 9 1
A itGivesOffOnEveryProductClassifiedAsAndAFreeShippingToEveryOrderWithItemsTotalEqualAtLeast() 0 14 1
A itGivesOffOnEveryProductClassifiedAsAndAFixedDiscountToEveryOrderWithItemsTotalEqualAtLeast() 0 20 1
A itGivesOffOnEveryProductClassifiedAsOrIfOrderContainsAnyProductClassifiedAsOr() 0 18 1
A itGivesOffOnEveryProductClassifiedAsIfOrderContainsAnyProductClassifiedAs() 0 15 1
A itIsCouponBasedPromotion() 0 6 1
A thePromotionWasDisabledForTheChannel() 0 6 1
A theCouponWasUsed() 0 6 1
A thePromotionGivesOffIfOrderContainsProducts() 0 6 1
A itGivesFixedDiscountOffOnAProduct() 0 4 1
A itGivesPercentageDiscountOffOnAProduct() 0 4 1
A thePromotionGivesOffTheOrderForCustomersFromGroup() 0 12 1
A itGivesDiscountOnShippingToEveryOrderOver() 0 11 1
A itGivesFreeShippingToEveryOrderOver() 0 4 1
A getTaxonFilterConfiguration() 0 4 1
A getProductsFilterConfiguration() 0 4 1
A getPriceRangeFilterConfiguration() 0 9 2
A createUnitFixedPromotion() 0 11 1
A createUnitPercentagePromotion() 0 11 1
A createFixedPromotion() 0 11 2
A createPercentagePromotion() 0 8 1
A persistPromotion() 0 12 2
A createCoupon() 0 9 1

How to fix   Complexity   

Complex Class

Complex classes like PromotionContext often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PromotionContext, and based on these observations, apply Extract Interface, too.

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 Sylius\Behat\Service\SharedStorageInterface;
19
use Sylius\Component\Core\Factory\PromotionActionFactoryInterface;
20
use Sylius\Component\Core\Factory\PromotionRuleFactoryInterface;
21
use Sylius\Component\Core\Model\ChannelInterface;
22
use Sylius\Component\Core\Model\ProductInterface;
23
use Sylius\Component\Core\Model\PromotionCouponInterface;
24
use Sylius\Component\Core\Model\PromotionInterface;
25
use Sylius\Component\Core\Model\TaxonInterface;
26
use Sylius\Component\Core\Promotion\Checker\Rule\CustomerGroupRuleChecker;
27
use Sylius\Component\Core\Test\Factory\TestPromotionFactoryInterface;
28
use Sylius\Component\Customer\Model\CustomerGroupInterface;
29
use Sylius\Component\Promotion\Factory\PromotionCouponFactoryInterface;
30
use Sylius\Component\Promotion\Model\PromotionActionInterface;
31
use Sylius\Component\Promotion\Model\PromotionRuleInterface;
32
use Sylius\Component\Promotion\Repository\PromotionRepositoryInterface;
33
34
final class PromotionContext implements Context
35
{
36
    /**
37
     * @var SharedStorageInterface
38
     */
39
    private $sharedStorage;
40
41
    /**
42
     * @var PromotionActionFactoryInterface
43
     */
44
    private $actionFactory;
45
46
    /**
47
     * @var PromotionCouponFactoryInterface
48
     */
49
    private $couponFactory;
50
51
    /**
52
     * @var PromotionRuleFactoryInterface
53
     */
54
    private $ruleFactory;
55
56
    /**
57
     * @var TestPromotionFactoryInterface
58
     */
59
    private $testPromotionFactory;
60
61
    /**
62
     * @var PromotionRepositoryInterface
63
     */
64
    private $promotionRepository;
65
66
    /**
67
     * @var ObjectManager
68
     */
69
    private $objectManager;
70
71
    /**
72
     * @param SharedStorageInterface $sharedStorage
73
     * @param PromotionActionFactoryInterface $actionFactory
74
     * @param PromotionCouponFactoryInterface $couponFactory
75
     * @param PromotionRuleFactoryInterface $ruleFactory
76
     * @param TestPromotionFactoryInterface $testPromotionFactory
77
     * @param PromotionRepositoryInterface $promotionRepository
78
     * @param ObjectManager $objectManager
79
     */
80
    public function __construct(
81
        SharedStorageInterface $sharedStorage,
82
        PromotionActionFactoryInterface $actionFactory,
83
        PromotionCouponFactoryInterface $couponFactory,
84
        PromotionRuleFactoryInterface $ruleFactory,
85
        TestPromotionFactoryInterface $testPromotionFactory,
86
        PromotionRepositoryInterface $promotionRepository,
87
        ObjectManager $objectManager
88
    ) {
89
        $this->sharedStorage = $sharedStorage;
90
        $this->actionFactory = $actionFactory;
91
        $this->couponFactory = $couponFactory;
92
        $this->ruleFactory = $ruleFactory;
93
        $this->testPromotionFactory = $testPromotionFactory;
94
        $this->promotionRepository = $promotionRepository;
95
        $this->objectManager = $objectManager;
96
    }
97
98
    /**
99
     * @Given there is (also) a promotion :promotionName
100
     * @Given there is a promotion :promotionName identified by :promotionCode code
101
     */
102
    public function thereIsPromotion(string $promotionName, ?string $promotionCode = null): void
103
    {
104
        $promotion = $this->testPromotionFactory
105
            ->createForChannel($promotionName, $this->sharedStorage->get('channel'))
106
        ;
107
108
        if (null !== $promotionCode) {
109
            $promotion->setCode($promotionCode);
110
        }
111
112
        $this->promotionRepository->add($promotion);
113
        $this->sharedStorage->set('promotion', $promotion);
114
    }
115
116
    /**
117
     * @Given /^there is a promotion "([^"]+)" with priority ([^"]+)$/
118
     */
119
    public function thereIsAPromotionWithPriority($promotionName, $priority)
120
    {
121
        $promotion = $this->testPromotionFactory
122
            ->createForChannel($promotionName, $this->sharedStorage->get('channel'))
123
        ;
124
125
        $promotion->setPriority((int) $priority);
126
127
        $this->promotionRepository->add($promotion);
128
        $this->sharedStorage->set('promotion', $promotion);
129
    }
130
131
    /**
132
     * @Given /^there is an exclusive promotion "([^"]+)"(?:| with priority ([^"]+))$/
133
     */
134
    public function thereIsAnExclusivePromotionWithPriority($promotionName, $priority = 0)
135
    {
136
        $promotion = $this->testPromotionFactory
137
            ->createForChannel($promotionName, $this->sharedStorage->get('channel'))
138
        ;
139
140
        $promotion->setExclusive(true);
141
        $promotion->setPriority((int) $priority);
142
143
        $this->promotionRepository->add($promotion);
144
        $this->sharedStorage->set('promotion', $promotion);
145
    }
146
147
    /**
148
     * @Given there is a promotion :promotionName limited to :usageLimit usages
149
     */
150
    public function thereIsPromotionLimitedToUsages($promotionName, $usageLimit)
151
    {
152
        $promotion = $this->testPromotionFactory->createForChannel($promotionName, $this->sharedStorage->get('channel'));
153
154
        $promotion->setUsageLimit((int) $usageLimit);
155
156
        $this->promotionRepository->add($promotion);
157
        $this->sharedStorage->set('promotion', $promotion);
158
    }
159
160
    /**
161
     * @Given the store has promotion :promotionName with coupon :couponCode
162
     * @Given the store has a promotion :promotionName with a coupon :couponCode that is limited to :usageLimit usages
163
     */
164
    public function thereIsPromotionWithCoupon(string $promotionName, string $couponCode, ?int $usageLimit = null): void
165
    {
166
        $coupon = $this->createCoupon($couponCode, $usageLimit);
167
168
        $promotion = $this->testPromotionFactory
169
            ->createForChannel($promotionName, $this->sharedStorage->get('channel'))
170
        ;
171
        $promotion->addCoupon($coupon);
172
        $promotion->setCouponBased(true);
173
174
        $this->promotionRepository->add($promotion);
175
176
        $this->sharedStorage->set('promotion', $promotion);
177
        $this->sharedStorage->set('coupon', $coupon);
178
    }
179
180
    /**
181
     * @Given /^(this promotion) has "([^"]+)", "([^"]+)" and "([^"]+)" coupons/
182
     */
183
    public function thisPromotionHasCoupons(PromotionInterface $promotion, string ...$couponCodes): void
184
    {
185
        foreach ($couponCodes as $couponCode) {
186
            $coupon = $this->createCoupon($couponCode);
187
            $promotion->addCoupon($coupon);
188
        }
189
190
        $promotion->setCouponBased(true);
191
192
        $this->objectManager->flush();
193
    }
194
195
    /**
196
     * @Given /^(this promotion) has already expired$/
197
     */
198
    public function thisPromotionHasExpired(PromotionInterface $promotion)
199
    {
200
        $promotion->setEndsAt(new \DateTime('1 day ago'));
201
202
        $this->objectManager->flush();
203
    }
204
205
    /**
206
     * @Given /^(this promotion) expires tomorrow$/
207
     */
208
    public function thisPromotionExpiresTomorrow(PromotionInterface $promotion)
209
    {
210
        $promotion->setEndsAt(new \DateTime('tomorrow'));
211
212
        $this->objectManager->flush();
213
    }
214
215
    /**
216
     * @Given /^(this promotion) has started yesterday$/
217
     */
218
    public function thisPromotionHasStartedYesterday(PromotionInterface $promotion)
219
    {
220
        $promotion->setStartsAt(new \DateTime('1 day ago'));
221
222
        $this->objectManager->flush();
223
    }
224
225
    /**
226
     * @Given /^(this promotion) starts tomorrow$/
227
     */
228
    public function thisPromotionStartsTomorrow(PromotionInterface $promotion)
229
    {
230
        $promotion->setStartsAt(new \DateTime('tomorrow'));
231
232
        $this->objectManager->flush();
233
    }
234
235
    /**
236
     * @Given /^(this coupon) has already expired$/
237
     */
238
    public function thisCouponHasExpired(PromotionCouponInterface $coupon)
239
    {
240
        $coupon->setExpiresAt(new \DateTime('1 day ago'));
241
242
        $this->objectManager->flush();
243
    }
244
245
    /**
246
     * @Given /^(this coupon) expires tomorrow$/
247
     */
248
    public function thisCouponExpiresTomorrow(PromotionCouponInterface $coupon)
249
    {
250
        $coupon->setExpiresAt(new \DateTime('tomorrow'));
251
252
        $this->objectManager->flush();
253
    }
254
255
    /**
256
     * @Given /^(this coupon) has already reached its usage limit$/
257
     */
258
    public function thisCouponHasReachedItsUsageLimit(PromotionCouponInterface $coupon)
259
    {
260
        $coupon->setUsed(42);
261
        $coupon->setUsageLimit(42);
262
263
        $this->objectManager->flush();
264
    }
265
266
    /**
267
     * @Given /^(this coupon) can be used (\d+) times?$/
268
     */
269
    public function thisCouponCanBeUsedNTimes(PromotionCouponInterface $coupon, $usageLimit)
270
    {
271
        $coupon->setUsageLimit((int) $usageLimit);
272
273
        $this->objectManager->flush();
274
    }
275
276
    /**
277
     * @Given /^(this coupon) can be used twice per customer$/
278
     */
279
    public function thisCouponCanBeUsedTwicePerCustomer(PromotionCouponInterface $coupon)
280
    {
281
        $coupon->setPerCustomerUsageLimit(2);
282
283
        $this->objectManager->flush();
284
    }
285
286
    /**
287
     * @Given /^([^"]+) gives ("(?:€|£|\$)[^"]+") discount to every order$/
288
     */
289
    public function itGivesFixedDiscountToEveryOrder(PromotionInterface $promotion, $discount)
290
    {
291
        $this->createFixedPromotion($promotion, $discount);
292
    }
293
294
    /**
295
     * @Given /^([^"]+) gives ("(?:€|£|\$)[^"]+") discount to every order in the ("[^"]+" channel) and ("(?:€|£|\$)[^"]+") discount to every order in the ("[^"]+" channel)$/
296
     */
297
    public function thisPromotionGivesDiscountToEveryOrderInTheChannelAndDiscountToEveryOrderInTheChannel(
298
        PromotionInterface $promotion,
299
        $firstChannelDiscount,
300
        ChannelInterface $firstChannel,
301
        $secondChannelDiscount,
302
        ChannelInterface $secondChannel
303
    ) {
304
        /** @var PromotionActionInterface $action */
305
        $action = $this->actionFactory->createFixedDiscount($firstChannelDiscount, $firstChannel->getCode());
306
        $action->setConfiguration(array_merge($action->getConfiguration(), [$secondChannel->getCode() => ['amount' => $secondChannelDiscount]]));
307
308
        $promotion->addChannel($firstChannel);
309
        $promotion->addChannel($secondChannel);
310
        $promotion->addAction($action);
311
312
        $this->objectManager->flush();
313
    }
314
315
    /**
316
     * @Given /^([^"]+) gives ("[^"]+%") discount to every order$/
317
     */
318
    public function itGivesPercentageDiscountToEveryOrder(PromotionInterface $promotion, $discount)
319
    {
320
        $this->createPercentagePromotion($promotion, $discount);
321
    }
322
323
    /**
324
     * @Given /^([^"]+) gives ("(?:€|£|\$)[^"]+") discount to every order with quantity at least ([^"]+)$/
325
     */
326
    public function itGivesFixedDiscountToEveryOrderWithQuantityAtLeast(
327
        PromotionInterface $promotion,
328
        $discount,
329
        $quantity
330
    ) {
331
        $rule = $this->ruleFactory->createCartQuantity((int) $quantity);
332
333
        $this->createFixedPromotion($promotion, $discount, [], $rule);
334
    }
335
336
    /**
337
     * @Given /^([^"]+) gives ("(?:€|£|\$)[^"]+") discount to every order with items total at least ("[^"]+")$/
338
     */
339
    public function itGivesFixedDiscountToEveryOrderWithItemsTotalAtLeast(
340
        PromotionInterface $promotion,
341
        $discount,
342
        $targetAmount
343
    ) {
344
        $channelCode = $this->sharedStorage->get('channel')->getCode();
345
        $rule = $this->ruleFactory->createItemTotal($channelCode, $targetAmount);
346
347
        $this->createFixedPromotion($promotion, $discount, [], $rule);
348
    }
349
350
    /**
351
     * @Given /^([^"]+) gives ("[^"]+%") discount to every order with items total at least ("[^"]+")$/
352
     */
353
    public function itGivesPercentageDiscountToEveryOrderWithItemsTotalAtLeast(
354
        PromotionInterface $promotion,
355
        $discount,
356
        $targetAmount
357
    ) {
358
        $channelCode = $this->sharedStorage->get('channel')->getCode();
359
        $rule = $this->ruleFactory->createItemTotal($channelCode, $targetAmount);
360
        $this->createPercentagePromotion($promotion, $discount, [], $rule);
361
    }
362
363
    /**
364
     * @Given /^([^"]+) gives ("[^"]+%") off on every product when the item total is at least ("(?:€|£|\$)[^"]+")$/
365
     */
366
    public function itGivesOffOnEveryItemWhenItemTotalExceeds(
367
        PromotionInterface $promotion,
368
        $discount,
369
        $targetAmount
370
    ) {
371
        $channelCode = $this->sharedStorage->get('channel')->getCode();
372
        $rule = $this->ruleFactory->createItemTotal($channelCode, $targetAmount);
373
374
        $this->createUnitPercentagePromotion($promotion, $discount, [], $rule);
375
    }
376
377
    /**
378
     * @Given /^([^"]+) gives ("[^"]+%") discount on shipping to every order$/
379
     */
380
    public function itGivesPercentageDiscountOnShippingToEveryOrder(PromotionInterface $promotion, $discount)
381
    {
382
        $action = $this->actionFactory->createShippingPercentageDiscount($discount);
383
        $promotion->addAction($action);
384
385
        $this->objectManager->flush();
386
    }
387
388
    /**
389
     * @Given /^([^"]+) gives free shipping to every order$/
390
     */
391
    public function thePromotionGivesFreeShippingToEveryOrder(PromotionInterface $promotion)
392
    {
393
        $this->itGivesPercentageDiscountOnShippingToEveryOrder($promotion, 1);
394
    }
395
396
    /**
397
     * @Given /^([^"]+) gives(?:| another) ("[^"]+%") off every product (classified as "[^"]+")$/
398
     */
399
    public function itGivesPercentageOffEveryProductClassifiedAs(
400
        PromotionInterface $promotion,
401
        $discount,
402
        TaxonInterface $taxon
403
    ) {
404
        $this->createUnitPercentagePromotion($promotion, $discount, $this->getTaxonFilterConfiguration([$taxon->getCode()]));
405
    }
406
407
    /**
408
     * @Given /^([^"]+) gives(?:| another) ("(?:€|£|\$)[^"]+") off on every product (classified as "[^"]+")$/
409
     */
410
    public function itGivesFixedOffEveryProductClassifiedAs(
411
        PromotionInterface $promotion,
412
        $discount,
413
        TaxonInterface $taxon
414
    ) {
415
        $this->createUnitFixedPromotion($promotion, $discount, $this->getTaxonFilterConfiguration([$taxon->getCode()]));
416
    }
417
418
    /**
419
     * @Given /^([^"]+) gives ("(?:€|£|\$)[^"]+") off on every product with minimum price at ("(?:€|£|\$)[^"]+")$/
420
     */
421
    public function thisPromotionGivesOffOnEveryProductWithMinimumPriceAt(
422
        PromotionInterface $promotion,
423
        $discount,
424
        $amount
425
    ) {
426
        $this->createUnitFixedPromotion($promotion, $discount, $this->getPriceRangeFilterConfiguration($amount));
427
    }
428
429
    /**
430
     * @Given /^([^"]+) gives ("(?:€|£|\$)[^"]+") off on every product priced between ("(?:€|£|\$)[^"]+") and ("(?:€|£|\$)[^"]+")$/
431
     */
432
    public function thisPromotionGivesOffOnEveryProductPricedBetween(
433
        PromotionInterface $promotion,
434
        $discount,
435
        $minAmount,
436
        $maxAmount
437
    ) {
438
        $this->createUnitFixedPromotion(
439
            $promotion,
440
            $discount,
441
            $this->getPriceRangeFilterConfiguration($minAmount, $maxAmount)
442
        );
443
    }
444
445
    /**
446
     * @Given /^([^"]+) gives ("[^"]+%") off on every product with minimum price at ("(?:€|£|\$)[^"]+")$/
447
     */
448
    public function thisPromotionPercentageGivesOffOnEveryProductWithMinimumPriceAt(
449
        PromotionInterface $promotion,
450
        $discount,
451
        $amount
452
    ) {
453
        $this->createUnitPercentagePromotion($promotion, $discount, $this->getPriceRangeFilterConfiguration($amount));
454
    }
455
456
    /**
457
     * @Given /^([^"]+) gives ("[^"]+%") off on every product priced between ("(?:€|£|\$)[^"]+") and ("(?:€|£|\$)[^"]+")$/
458
     */
459
    public function thisPromotionPercentageGivesOffOnEveryProductPricedBetween(
460
        PromotionInterface $promotion,
461
        $discount,
462
        $minAmount,
463
        $maxAmount
464
    ) {
465
        $this->createUnitPercentagePromotion(
466
            $promotion,
467
            $discount,
468
            $this->getPriceRangeFilterConfiguration($minAmount, $maxAmount)
469
        );
470
    }
471
472
    /**
473
     * @Given /^([^"]+) gives ("(?:€|£|\$)[^"]+") off if order contains products (classified as "[^"]+")$/
474
     */
475
    public function thePromotionGivesOffIfOrderContainsProductsClassifiedAs(
476
        PromotionInterface $promotion,
477
        $discount,
478
        TaxonInterface $taxon
479
    ) {
480
        $rule = $this->ruleFactory->createHasTaxon([$taxon->getCode()]);
481
482
        $this->createFixedPromotion($promotion, $discount, [], $rule);
483
    }
484
485
    /**
486
     * @Given /^([^"]+) gives ("(?:€|£|\$)[^"]+") off if order contains products (classified as "[^"]+" or "[^"]+")$/
487
     */
488
    public function thePromotionGivesOffIfOrderContainsProductsClassifiedAsOr(
489
        PromotionInterface $promotion,
490
        $discount,
491
        array $taxons
492
    ) {
493
        $rule = $this->ruleFactory->createHasTaxon([$taxons[0]->getCode(), $taxons[1]->getCode()]);
494
495
        $this->createFixedPromotion($promotion, $discount, [], $rule);
496
    }
497
498
    /**
499
     * @Given /^([^"]+) gives ("(?:€|£|\$)[^"]+") off if order contains products (classified as "[^"]+") with a minimum value of ("(?:€|£|\$)[^"]+")$/
500
     */
501
    public function thePromotionGivesOffIfOrderContainsProductsClassifiedAsAndPricedAt(
502
        PromotionInterface $promotion,
503
        $discount,
504
        TaxonInterface $taxon,
505
        $amount
506
    ) {
507
        $channelCode = $this->sharedStorage->get('channel')->getCode();
508
        $rule = $this->ruleFactory->createItemsFromTaxonTotal($channelCode, $taxon->getCode(), $amount);
509
510
        $this->createFixedPromotion($promotion, $discount, [], $rule);
511
    }
512
513
    /**
514
     * @Given /^([^"]+) gives ("(?:€|£|\$)[^"]+") off customer's (\d)(?:st|nd|rd|th) order$/
515
     */
516
    public function itGivesFixedOffCustomersNthOrder(PromotionInterface $promotion, $discount, $nth)
517
    {
518
        $rule = $this->ruleFactory->createNthOrder((int) $nth);
519
520
        $this->createFixedPromotion($promotion, $discount, [], $rule);
521
    }
522
523
    /**
524
     * @Given /^([^"]+) gives ("[^"]+%") off on the customer's (\d)(?:st|nd|rd|th) order$/
525
     */
526
    public function itGivesPercentageOffCustomersNthOrder(PromotionInterface $promotion, $discount, $nth)
527
    {
528
        $rule = $this->ruleFactory->createNthOrder((int) $nth);
529
530
        $this->createPercentagePromotion($promotion, $discount, [], $rule);
531
    }
532
533
    /**
534
     * @Given /^([^"]+) gives ("[^"]+%") off on every product (classified as "[^"]+") and ("(?:€|£|\$)[^"]+") discount on every order$/
535
     */
536
    public function itGivesPercentageOffOnEveryProductClassifiedAsAndAmountDiscountOnOrder(
537
        PromotionInterface $promotion,
538
        $productDiscount,
539
        TaxonInterface $discountTaxon,
540
        $orderDiscount
541
    ) {
542
        $this->createUnitPercentagePromotion($promotion, $productDiscount, $this->getTaxonFilterConfiguration([$discountTaxon->getCode()]));
543
        $this->createFixedPromotion($promotion, $orderDiscount);
544
    }
545
546
    /**
547
     * @Given /^([^"]+) gives ("(?:€|£|\$)[^"]+") off on every product (classified as "[^"]+") and a free shipping to every order with items total equal at least ("[^"]+")$/
548
     */
549
    public function itGivesOffOnEveryProductClassifiedAsAndAFreeShippingToEveryOrderWithItemsTotalEqualAtLeast(
550
        PromotionInterface $promotion,
551
        $discount,
552
        TaxonInterface $taxon,
553
        $targetAmount
554
    ) {
555
        $freeShippingAction = $this->actionFactory->createShippingPercentageDiscount(1);
556
        $promotion->addAction($freeShippingAction);
557
558
        $channelCode = $this->sharedStorage->get('channel')->getCode();
559
        $rule = $this->ruleFactory->createItemTotal($channelCode, $targetAmount);
560
561
        $this->createUnitFixedPromotion($promotion, $discount, [], $rule);
562
    }
563
564
    /**
565
     * @Given /^([^"]+) gives ("[^"]+%") off on every product (classified as "[^"]+") and a ("(?:€|£|\$)[^"]+") discount to every order with items total equal at least ("(?:€|£|\$)[^"]+")$/
566
     */
567
    public function itGivesOffOnEveryProductClassifiedAsAndAFixedDiscountToEveryOrderWithItemsTotalEqualAtLeast(
568
        PromotionInterface $promotion,
569
        $taxonDiscount,
570
        TaxonInterface $taxon,
571
        $orderDiscount,
572
        $targetAmount
573
    ) {
574
        $orderDiscountAction = $this->actionFactory->createFixedDiscount($orderDiscount, $this->sharedStorage->get('channel')->getCode());
575
        $promotion->addAction($orderDiscountAction);
576
577
        $channelCode = $this->sharedStorage->get('channel')->getCode();
578
        $rule = $this->ruleFactory->createItemTotal($channelCode, $targetAmount);
579
580
        $this->createUnitPercentagePromotion(
581
            $promotion,
582
            $taxonDiscount,
583
            $this->getTaxonFilterConfiguration([$taxon->getCode()]),
584
            $rule
585
        );
586
    }
587
588
    /**
589
     * @Given /^([^"]+) gives ("[^"]+%") off on every product (classified as "[^"]+" or "[^"]+") if order contains any product (classified as "[^"]+" or "[^"]+")$/
590
     */
591
    public function itGivesOffOnEveryProductClassifiedAsOrIfOrderContainsAnyProductClassifiedAsOr(
592
        PromotionInterface $promotion,
593
        $discount,
594
        array $discountTaxons,
595
        array $targetTaxons
596
    ) {
597
        $discountTaxonsCodes = [$discountTaxons[0]->getCode(), $discountTaxons[1]->getCode()];
598
        $targetTaxonsCodes = [$targetTaxons[0]->getCode(), $targetTaxons[1]->getCode()];
599
600
        $rule = $this->ruleFactory->createHasTaxon($targetTaxonsCodes);
601
602
        $this->createUnitPercentagePromotion(
603
            $promotion,
604
            $discount,
605
            $this->getTaxonFilterConfiguration($discountTaxonsCodes),
606
            $rule
607
        );
608
    }
609
610
    /**
611
     * @Given /^([^"]+) gives ("[^"]+%") off on every product (classified as "[^"]+") if order contains any product (classified as "[^"]+")$/
612
     */
613
    public function itGivesOffOnEveryProductClassifiedAsIfOrderContainsAnyProductClassifiedAs(
614
        PromotionInterface $promotion,
615
        $discount,
616
        $discountTaxon,
617
        $targetTaxon
618
    ) {
619
        $rule = $this->ruleFactory->createHasTaxon([$targetTaxon->getCode()]);
620
621
        $this->createUnitPercentagePromotion(
622
            $promotion,
623
            $discount,
624
            $this->getTaxonFilterConfiguration([$discountTaxon->getCode()]),
625
            $rule
626
        );
627
    }
628
629
    /**
630
     * @Given /^(it) is coupon based promotion$/
631
     */
632
    public function itIsCouponBasedPromotion(PromotionInterface $promotion)
633
    {
634
        $promotion->setCouponBased(true);
635
636
        $this->objectManager->flush();
637
    }
638
639
    /**
640
     * @Given /^(the promotion) was disabled for the (channel "[^"]+")$/
641
     */
642
    public function thePromotionWasDisabledForTheChannel(PromotionInterface $promotion, ChannelInterface $channel)
643
    {
644
        $promotion->removeChannel($channel);
645
646
        $this->objectManager->flush();
647
    }
648
649
    /**
650
     * @Given /^the (coupon "[^"]+") was used up to its usage limit$/
651
     */
652
    public function theCouponWasUsed(PromotionCouponInterface $coupon)
653
    {
654
        $coupon->setUsed($coupon->getUsageLimit());
655
656
        $this->objectManager->flush();
657
    }
658
659
    /**
660
     * @Given /^([^"]+) gives ("(?:€|£|\$)[^"]+") off if order contains (?:a|an) ("[^"]+" product)$/
661
     */
662
    public function thePromotionGivesOffIfOrderContainsProducts(PromotionInterface $promotion, $discount, ProductInterface $product)
663
    {
664
        $rule = $this->ruleFactory->createContainsProduct($product->getCode());
665
666
        $this->createFixedPromotion($promotion, $discount, [], $rule);
667
    }
668
669
    /**
670
     * @Given /^([^"]+) gives ("(?:€|£|\$)[^"]+") off on a ("[^"]*" product)$/
671
     */
672
    public function itGivesFixedDiscountOffOnAProduct(PromotionInterface $promotion, $discount, ProductInterface $product)
673
    {
674
        $this->createUnitFixedPromotion($promotion, $discount, $this->getProductsFilterConfiguration([$product->getCode()]));
675
    }
676
677
    /**
678
     * @Given /^([^"]+) gives ("[^"]+%") off on a ("[^"]*" product)$/
679
     */
680
    public function itGivesPercentageDiscountOffOnAProduct(PromotionInterface $promotion, $discount, ProductInterface $product)
681
    {
682
        $this->createUnitPercentagePromotion($promotion, $discount, $this->getProductsFilterConfiguration([$product->getCode()]));
683
    }
684
685
    /**
686
     * @Given /^([^"]+) gives ("[^"]+%") off the order for customers from ("[^"]*" group)$/
687
     */
688
    public function thePromotionGivesOffTheOrderForCustomersFromGroup(
689
        PromotionInterface $promotion,
690
        $discount,
691
        CustomerGroupInterface $customerGroup
692
    ) {
693
        /** @var PromotionRuleInterface $rule */
694
        $rule = $this->ruleFactory->createNew();
695
        $rule->setType(CustomerGroupRuleChecker::TYPE);
696
        $rule->setConfiguration(['group_code' => $customerGroup->getCode()]);
697
698
        $this->createPercentagePromotion($promotion, $discount, [], $rule);
699
    }
700
701
    /**
702
     * @Given /^([^"]+) gives ("[^"]+%") discount on shipping to every order over ("(?:€|£|\$)[^"]+")$/
703
     */
704
    public function itGivesDiscountOnShippingToEveryOrderOver(
705
        PromotionInterface $promotion,
706
        $discount,
707
        $itemTotal
708
    ) {
709
        $channelCode = $this->sharedStorage->get('channel')->getCode();
710
        $rule = $this->ruleFactory->createItemTotal($channelCode, $itemTotal);
711
        $action = $this->actionFactory->createShippingPercentageDiscount($discount);
712
713
        $this->persistPromotion($promotion, $action, [], $rule);
714
    }
715
716
    /**
717
     * @Given /^([^"]+) gives free shipping to every order over ("(?:€|£|\$)[^"]+")$/
718
     */
719
    public function itGivesFreeShippingToEveryOrderOver(PromotionInterface $promotion, $itemTotal)
720
    {
721
        $this->itGivesDiscountOnShippingToEveryOrderOver($promotion, 1, $itemTotal);
722
    }
723
724
    /**
725
     * @param array $taxonCodes
726
     *
727
     * @return array
728
     */
729
    private function getTaxonFilterConfiguration(array $taxonCodes)
730
    {
731
        return ['filters' => ['taxons_filter' => ['taxons' => $taxonCodes]]];
732
    }
733
734
    /**
735
     * @param array $productCodes
736
     *
737
     * @return array
738
     */
739
    private function getProductsFilterConfiguration(array $productCodes)
740
    {
741
        return ['filters' => ['products_filter' => ['products' => $productCodes]]];
742
    }
743
744
    /**
745
     * @param int $minAmount
746
     * @param int $maxAmount
747
     *
748
     * @return array
749
     */
750
    private function getPriceRangeFilterConfiguration($minAmount, $maxAmount = null)
751
    {
752
        $configuration = ['filters' => ['price_range_filter' => ['min' => $minAmount]]];
753
        if (null !== $maxAmount) {
754
            $configuration['filters']['price_range_filter']['max'] = $maxAmount;
755
        }
756
757
        return $configuration;
758
    }
759
760
    /**
761
     * @param PromotionInterface $promotion
762
     * @param int $discount
763
     * @param array $configuration
764
     * @param PromotionRuleInterface|null $rule
765
     */
766
    private function createUnitFixedPromotion(PromotionInterface $promotion, $discount, array $configuration = [], PromotionRuleInterface $rule = null)
767
    {
768
        $channelCode = $this->sharedStorage->get('channel')->getCode();
769
770
        $this->persistPromotion(
771
            $promotion,
772
            $this->actionFactory->createUnitFixedDiscount($discount, $channelCode),
773
            [$channelCode => $configuration],
774
            $rule
775
        );
776
    }
777
778
    /**
779
     * @param PromotionInterface $promotion
780
     * @param int $discount
781
     * @param array $configuration
782
     * @param PromotionRuleInterface|null $rule
783
     */
784
    private function createUnitPercentagePromotion(PromotionInterface $promotion, $discount, array $configuration = [], PromotionRuleInterface $rule = null)
785
    {
786
        $channelCode = $this->sharedStorage->get('channel')->getCode();
787
788
        $this->persistPromotion(
789
            $promotion,
790
            $this->actionFactory->createUnitPercentageDiscount($discount, $channelCode),
791
            [$channelCode => $configuration],
792
            $rule
793
        );
794
    }
795
796
    /**
797
     * @param PromotionInterface $promotion
798
     * @param int $discount
799
     * @param array $configuration
800
     * @param PromotionRuleInterface|null $rule
801
     * @param ChannelInterface|null $channel
802
     */
803
    private function createFixedPromotion(
804
        PromotionInterface $promotion,
805
        $discount,
806
        array $configuration = [],
807
        PromotionRuleInterface $rule = null,
808
        ChannelInterface $channel = null
809
    ) {
810
        $channelCode = (null !== $channel) ? $channel->getCode() : $this->sharedStorage->get('channel')->getCode();
811
812
        $this->persistPromotion($promotion, $this->actionFactory->createFixedDiscount($discount, $channelCode), $configuration, $rule);
813
    }
814
815
    /**
816
     * @param PromotionInterface $promotion
817
     * @param float $discount
818
     * @param array $configuration
819
     * @param PromotionRuleInterface $rule
820
     */
821
    private function createPercentagePromotion(
822
        PromotionInterface $promotion,
823
        $discount,
824
        array $configuration = [],
825
        PromotionRuleInterface $rule = null
826
    ) {
827
        $this->persistPromotion($promotion, $this->actionFactory->createPercentageDiscount($discount), $configuration, $rule);
828
    }
829
830
    /**
831
     * @param PromotionInterface $promotion
832
     * @param PromotionActionInterface $action
833
     * @param array $configuration
834
     * @param PromotionRuleInterface|null $rule
835
     */
836
    private function persistPromotion(PromotionInterface $promotion, PromotionActionInterface $action, array $configuration, PromotionRuleInterface $rule = null)
837
    {
838
        $configuration = array_merge_recursive($action->getConfiguration(), $configuration);
839
        $action->setConfiguration($configuration);
840
841
        $promotion->addAction($action);
842
        if (null !== $rule) {
843
            $promotion->addRule($rule);
844
        }
845
846
        $this->objectManager->flush();
847
    }
848
849
    /**
850
     * @param string $couponCode
851
     * @param int|null $usageLimit
852
     *
853
     * @return PromotionCouponInterface
854
     */
855
    private function createCoupon(string $couponCode, ?int $usageLimit = null): PromotionCouponInterface
856
    {
857
        /** @var PromotionCouponInterface $coupon */
858
        $coupon = $this->couponFactory->createNew();
859
        $coupon->setCode($couponCode);
860
        $coupon->setUsageLimit($usageLimit);
861
862
        return $coupon;
863
    }
864
}
865