Completed
Push — master ( 23d5f0...daf9a4 )
by Kamil
05:58
created

PromotionContext::generateCoupons()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

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

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
777
     */
778
    private function getTaxonFilterConfiguration(array $taxonCodes)
779
    {
780
        return ['filters' => ['taxons_filter' => ['taxons' => $taxonCodes]]];
781
    }
782
783
    /**
784
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,array<string,array<string,array>>>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
785
     */
786
    private function getProductsFilterConfiguration(array $productCodes)
787
    {
788
        return ['filters' => ['products_filter' => ['products' => $productCodes]]];
789
    }
790
791
    /**
792
     * @param int $minAmount
793
     * @param int $maxAmount
0 ignored issues
show
Documentation introduced by
Should the type for parameter $maxAmount not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
794
     *
795
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,array<string,array<string,integer>>>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
796
     */
797
    private function getPriceRangeFilterConfiguration($minAmount, $maxAmount = null)
798
    {
799
        $configuration = ['filters' => ['price_range_filter' => ['min' => $minAmount]]];
800
        if (null !== $maxAmount) {
801
            $configuration['filters']['price_range_filter']['max'] = $maxAmount;
802
        }
803
804
        return $configuration;
805
    }
806
807
    private function createPromotion(string $name, ?string $code = null): PromotionInterface
808
    {
809
        $promotion = $this->testPromotionFactory->createForChannel($name, $this->sharedStorage->get('channel'));
810
811
        if (null !== $code) {
812
            $promotion->setCode($code);
813
        }
814
815
        $this->promotionRepository->add($promotion);
816
        $this->sharedStorage->set('promotion', $promotion);
817
818
        return $promotion;
819
    }
820
821
    /**
822
     * @param int $discount
823
     */
824
    private function createUnitFixedPromotion(PromotionInterface $promotion, $discount, array $configuration = [], PromotionRuleInterface $rule = null)
825
    {
826
        $channelCode = $this->sharedStorage->get('channel')->getCode();
827
828
        $this->persistPromotion(
829
            $promotion,
830
            $this->actionFactory->createUnitFixedDiscount($discount, $channelCode),
831
            [$channelCode => $configuration],
832
            $rule
833
        );
834
    }
835
836
    /**
837
     * @param int $discount
838
     */
839
    private function createUnitPercentagePromotion(PromotionInterface $promotion, $discount, array $configuration = [], PromotionRuleInterface $rule = null)
840
    {
841
        $channelCode = $this->sharedStorage->get('channel')->getCode();
842
843
        $this->persistPromotion(
844
            $promotion,
845
            $this->actionFactory->createUnitPercentageDiscount($discount, $channelCode),
846
            [$channelCode => $configuration],
847
            $rule
848
        );
849
    }
850
851
    /**
852
     * @param int $discount
853
     */
854
    private function createFixedPromotion(
855
        PromotionInterface $promotion,
856
        $discount,
857
        array $configuration = [],
858
        PromotionRuleInterface $rule = null,
859
        ChannelInterface $channel = null
860
    ) {
861
        $channelCode = (null !== $channel) ? $channel->getCode() : $this->sharedStorage->get('channel')->getCode();
862
863
        $this->persistPromotion($promotion, $this->actionFactory->createFixedDiscount($discount, $channelCode), $configuration, $rule);
864
    }
865
866
    /**
867
     * @param float $discount
868
     * @param PromotionRuleInterface $rule
0 ignored issues
show
Documentation introduced by
Should the type for parameter $rule not be null|PromotionRuleInterface?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
869
     */
870
    private function createPercentagePromotion(
871
        PromotionInterface $promotion,
872
        $discount,
873
        array $configuration = [],
874
        PromotionRuleInterface $rule = null
875
    ) {
876
        $this->persistPromotion($promotion, $this->actionFactory->createPercentageDiscount($discount), $configuration, $rule);
877
    }
878
879
    private function persistPromotion(PromotionInterface $promotion, PromotionActionInterface $action, array $configuration, PromotionRuleInterface $rule = null)
880
    {
881
        $configuration = array_merge_recursive($action->getConfiguration(), $configuration);
882
        $action->setConfiguration($configuration);
883
884
        $promotion->addAction($action);
885
        if (null !== $rule) {
886
            $promotion->addRule($rule);
887
        }
888
889
        $this->objectManager->flush();
890
    }
891
892
    private function createCoupon(string $couponCode, ?int $usageLimit = null): PromotionCouponInterface
893
    {
894
        /** @var PromotionCouponInterface $coupon */
895
        $coupon = $this->couponFactory->createNew();
896
        $coupon->setCode($couponCode);
897
        $coupon->setUsageLimit($usageLimit);
898
899
        return $coupon;
900
    }
901
902
    private function generateCoupons(
903
        int $amount,
904
        PromotionInterface $promotion,
905
        int $codeLength,
906
        ?string $prefix = null,
907
        ?string $suffix = null
908
    ): void {
909
        $instruction = new PromotionCouponGeneratorInstruction();
910
        $instruction->setAmount($amount);
911
        $instruction->setCodeLength($codeLength);
912
        $instruction->setPrefix($prefix);
913
        $instruction->setSuffix($suffix);
914
915
        $this->couponGenerator->generate($promotion, $instruction);
916
    }
917
}
918