Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2b7b075

Browse files
authored
feature #12025 [API]adding promotions with expire date (arti0090)
This PR was merged into the 1.9-dev branch. Discussion ---------- | Q | A | --------------- | ----- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Related tickets | #12013 like this but better | License | MIT Commits ------- 071b43e changed way how it is validated 1d5617e fixed issues and changed validator logic e5e252f Fixed minor issues
2 parents 4cb44a4 + e5e252f commit 2b7b075

8 files changed

Lines changed: 319 additions & 107 deletions

File tree

src/Sylius/Bundle/ApiBundle/CommandHandler/Cart/ApplyCouponToCartHandler.php

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
namespace Sylius\Bundle\ApiBundle\CommandHandler\Cart;
1515

16-
use DateTime;
1716
use Sylius\Bundle\ApiBundle\Command\Cart\ApplyCouponToCart;
1817
use Sylius\Component\Core\Model\OrderInterface;
1918
use Sylius\Component\Promotion\Model\PromotionCouponInterface;
@@ -55,29 +54,7 @@ public function __invoke(ApplyCouponToCart $command): void
5554
/** @var PromotionCouponInterface $promotionCoupon */
5655
$promotionCoupon = $this->promotionCouponRepository->findOneBy(['code' => $command->couponCode]);
5756

58-
Assert::notNull($promotionCoupon, 'Could not find promotion coupon with given code');
59-
60-
$couponUsageLimit = $promotionCoupon->getUsageLimit();
61-
62-
if (null !== $couponUsageLimit) {
63-
Assert::greaterThan($couponUsageLimit, 0, 'Promotion coupon usage has expired');
64-
}
65-
66-
$couponExpireDate = $promotionCoupon->getExpiresAt();
67-
68-
if (null !== $couponExpireDate) {
69-
Assert::greaterThan($couponExpireDate, new DateTime(), 'Promotion coupon has expired');
70-
}
71-
72-
$promotion = $promotionCoupon->getPromotion();
73-
74-
Assert::notNull($promotion, 'Could not find promotion linked with this coupon');
75-
76-
$promotionEndDate = $promotion->getEndsAt();
77-
78-
if (null !== $promotionEndDate) {
79-
Assert::greaterThan($promotionEndDate, new DateTime(), 'Promotion has expired');
80-
}
57+
Assert::notNull($promotionCoupon);
8158

8259
$cart->setPromotionCoupon($promotionCoupon);
8360

src/Sylius/Bundle/ApiBundle/Resources/config/services/validator.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,13 @@
4646
<service id="sylius.api.validator.correct_change_shop_user_confirm_password" class="Sylius\Bundle\ApiBundle\Validator\Constraints\CorrectChangeShopUserConfirmPasswordValidator">
4747
<tag name="validator.constraint_validator" alias="sylius_api_correct_change_shop_user_confirm_password" />
4848
</service>
49+
50+
<service id="sylius.api.validator.promotion_coupon_eligibility" class="Sylius\Bundle\ApiBundle\Validator\Constraints\PromotionCouponEligibilityValidator">
51+
<argument type="service" id="sylius.repository.promotion_coupon" />
52+
<argument type="service" id="sylius.repository.order" />
53+
<argument type="service" id="sylius.promotion_eligibility_checker" />
54+
<argument type="service" id="sylius.promotion_coupon_eligibility_checker" />
55+
<tag name="validator.constraint_validator" alias="sylius_api_promotion_coupon_eligibility" />
56+
</service>
4957
</services>
5058
</container>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<!--
4+
5+
This file is part of the Sylius package.
6+
7+
(c) Paweł Jędrzejewski
8+
9+
For the full copyright and license information, please view the LICENSE
10+
file that was distributed with this source code.
11+
12+
-->
13+
14+
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/services/constraint-mapping-1.0.xsd">
15+
<class name="Sylius\Bundle\ApiBundle\Command\Cart\ApplyCouponToCart">
16+
<constraint name="Sylius\Bundle\ApiBundle\Validator\Constraints\PromotionCouponEligibility" />
17+
<property name="couponCode">
18+
<constraint name="NotNull" >
19+
<option name="message">sylius.promotion_coupon.code.not_blank</option>
20+
</constraint>
21+
</property>
22+
</class>
23+
</constraint-mapping>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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\Bundle\ApiBundle\Validator\Constraints;
15+
16+
use Symfony\Component\Validator\Constraint;
17+
18+
/** @experimental */
19+
final class PromotionCouponEligibility extends Constraint
20+
{
21+
public function validatedBy(): string
22+
{
23+
return 'sylius_api_promotion_coupon_eligibility';
24+
}
25+
26+
public function getTargets(): string
27+
{
28+
return self::CLASS_CONSTRAINT;
29+
}
30+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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\Bundle\ApiBundle\Validator\Constraints;
15+
16+
use Sylius\Bundle\ApiBundle\Command\Cart\ApplyCouponToCart;
17+
use Sylius\Component\Core\Model\OrderInterface;
18+
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
19+
use Sylius\Component\Promotion\Checker\Eligibility\PromotionCouponEligibilityCheckerInterface;
20+
use Sylius\Component\Promotion\Checker\Eligibility\PromotionEligibilityCheckerInterface;
21+
use Sylius\Component\Promotion\Model\PromotionCouponInterface;
22+
use Sylius\Component\Promotion\Repository\PromotionCouponRepositoryInterface;
23+
use Symfony\Component\Validator\Constraint;
24+
use Symfony\Component\Validator\ConstraintValidator;
25+
use Webmozart\Assert\Assert;
26+
27+
/** @experimental */
28+
final class PromotionCouponEligibilityValidator extends ConstraintValidator
29+
{
30+
/** @var PromotionCouponRepositoryInterface */
31+
private $promotionCouponRepository;
32+
33+
/** @var OrderRepositoryInterface */
34+
private $orderRepository;
35+
36+
/** @var PromotionEligibilityCheckerInterface */
37+
private $promotionChecker;
38+
39+
/** @var PromotionCouponEligibilityCheckerInterface */
40+
private $promotionCouponChecker;
41+
42+
43+
public function __construct(
44+
PromotionCouponRepositoryInterface $promotionCouponRepository,
45+
OrderRepositoryInterface $orderRepository,
46+
PromotionEligibilityCheckerInterface $promotionChecker,
47+
PromotionCouponEligibilityCheckerInterface $promotionCouponChecker
48+
) {
49+
$this->promotionCouponRepository = $promotionCouponRepository;
50+
$this->orderRepository = $orderRepository;
51+
$this->promotionChecker = $promotionChecker;
52+
$this->promotionCouponChecker = $promotionCouponChecker;
53+
}
54+
55+
public function validate($value, Constraint $constraint): void
56+
{
57+
Assert::isInstanceOf($value, ApplyCouponToCart::class);
58+
59+
/** @var PromotionCouponInterface $promotionCoupon */
60+
$promotionCoupon = $this->promotionCouponRepository->findOneBy(['code' => $value->couponCode]);
61+
62+
/** @var OrderInterface $cart*/
63+
$cart = $this->orderRepository->findCartByTokenValue($value->getOrderTokenValue());
64+
65+
$cart->setPromotionCoupon($promotionCoupon);
66+
67+
if (!$this->promotionCouponChecker->isEligible($cart, $promotionCoupon)) {
68+
$this->context->buildViolation('sylius.promotion_coupon.is_invalid')
69+
->atPath('couponCode')
70+
->addViolation()
71+
;
72+
return;
73+
}
74+
75+
if (!$this->promotionChecker->isEligible($cart, $promotionCoupon->getPromotion())) {
76+
$this->context->buildViolation('sylius.promotion.is_invalid')
77+
->atPath('couponCode')
78+
->addViolation()
79+
;
80+
}
81+
}
82+
}

src/Sylius/Bundle/ApiBundle/spec/CommandHandler/Cart/ApplyCouponToCartHandlerSpec.php

Lines changed: 15 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -43,114 +43,46 @@ function it_applies_coupon_to_cart(
4343
PromotionCouponRepositoryInterface $promotionCouponRepository,
4444
OrderProcessorInterface $orderProcessor,
4545
OrderInterface $cart,
46-
PromotionCouponInterface $promotionCoupon,
47-
PromotionInterface $promotion
46+
PromotionCouponInterface $promotionCoupon
4847
): void {
4948
$orderRepository->findCartByTokenValue('cart')->willReturn($cart);
5049

5150
$promotionCouponRepository->findOneBy(['code' => 'couponCode'])->willReturn($promotionCoupon);
52-
$promotionCoupon->getUsageLimit()->willReturn(null);
53-
$promotionCoupon->getExpiresAt()->willReturn(null);
54-
55-
$promotionCoupon->getPromotion()->willReturn($promotion);
56-
$promotion->getEndsAt()->willReturn(null);
57-
58-
$cart->setPromotionCoupon($promotionCoupon)->shouldBeCalled();
5951

6052
$orderProcessor->process($cart)->shouldBeCalled();
6153

6254
$this(ApplyCouponToCart::createFromData('cart', 'couponCode'));
6355
}
6456

65-
function it_throws_exception_when_coupon_code_is_invalid(
57+
function it_throws_exception_if_cart_is_not_found(
6658
OrderRepositoryInterface $orderRepository,
6759
PromotionCouponRepositoryInterface $promotionCouponRepository,
60+
OrderProcessorInterface $orderProcessor,
6861
OrderInterface $cart
6962
): void {
70-
$command = new ApplyCouponToCart('couponCode');
71-
$command->setOrderTokenValue('cart');
63+
$orderRepository->findCartByTokenValue('cart')->willReturn(null);
7264

73-
$orderRepository->findCartByTokenValue('cart')->willReturn($cart);
65+
$promotionCouponRepository->findOneBy(['code' => 'couponCode'])->shouldNotBeCalled();
7466

75-
$promotionCouponRepository->findOneBy(['code' => 'couponCode'])->willReturn(null);
67+
$orderProcessor->process($cart)->shouldNotBeCalled();
7668

77-
$this->shouldThrow(\InvalidArgumentException::class)->during('__invoke', [$command]);
69+
$this->shouldThrow(\InvalidArgumentException::class)
70+
->during('__invoke', [ApplyCouponToCart::createFromData('cart', 'couponCode')]);
7871
}
7972

80-
function it_throws_exception_when_coupon_usage_limit_has_expired(
73+
function it_throws_exception_if_promotion_coupon_is_not_found(
8174
OrderRepositoryInterface $orderRepository,
8275
PromotionCouponRepositoryInterface $promotionCouponRepository,
83-
OrderInterface $cart,
84-
PromotionCouponInterface $promotionCoupon
85-
): void {
86-
$command = new ApplyCouponToCart('couponCode');
87-
$command->setOrderTokenValue('cart');
88-
89-
$orderRepository->findCartByTokenValue('cart')->willReturn($cart);
90-
91-
$promotionCouponRepository->findOneBy(['code' => 'couponCode'])->willReturn($promotionCoupon);
92-
$promotionCoupon->getUsageLimit()->willReturn(0);
93-
94-
$this->shouldThrow(\InvalidArgumentException::class)->during('__invoke', [$command]);
95-
}
96-
97-
function it_throws_exception_when_coupon_date_has_expired(
98-
OrderRepositoryInterface $orderRepository,
99-
PromotionCouponRepositoryInterface $promotionCouponRepository,
100-
OrderInterface $cart,
101-
PromotionCouponInterface $promotionCoupon
102-
): void {
103-
$command = new ApplyCouponToCart('couponCode');
104-
$command->setOrderTokenValue('cart');
105-
106-
$orderRepository->findCartByTokenValue('cart')->willReturn($cart);
107-
108-
$promotionCouponRepository->findOneBy(['code' => 'couponCode'])->willReturn($promotionCoupon);
109-
$promotionCoupon->getUsageLimit()->willReturn(1);
110-
$promotionCoupon->getExpiresAt()->willReturn(new \DateTime('now -1 year'));
111-
112-
$this->shouldThrow(\InvalidArgumentException::class)->during('__invoke', [$command]);
113-
}
114-
115-
function it_throws_exception_when_promotion_date_has_expired(
116-
OrderRepositoryInterface $orderRepository,
117-
PromotionCouponRepositoryInterface $promotionCouponRepository,
118-
OrderInterface $cart,
119-
PromotionCouponInterface $promotionCoupon,
120-
PromotionInterface $promotion
121-
): void {
122-
$command = new ApplyCouponToCart('couponCode');
123-
$command->setOrderTokenValue('cart');
124-
125-
$orderRepository->findCartByTokenValue('cart')->willReturn($cart);
126-
127-
$promotionCouponRepository->findOneBy(['code' => 'couponCode'])->willReturn($promotionCoupon);
128-
$promotionCoupon->getUsageLimit()->willReturn(1);
129-
$promotionCoupon->getExpiresAt()->willReturn(new \DateTime('now +1 year'));
130-
131-
$promotionCoupon->getPromotion()->willReturn($promotion);
132-
$promotion->getEndsAt()->willReturn(new \DateTime('now -1 year'));
133-
134-
$this->shouldThrow(\InvalidArgumentException::class)->during('__invoke', [$command]);
135-
}
136-
137-
function it_throws_exception_when_coupon_does_not_have_promotion(
138-
OrderRepositoryInterface $orderRepository,
139-
PromotionCouponRepositoryInterface $promotionCouponRepository,
140-
OrderInterface $cart,
141-
PromotionCouponInterface $promotionCoupon
76+
OrderProcessorInterface $orderProcessor,
77+
OrderInterface $cart
14278
): void {
143-
$command = new ApplyCouponToCart('couponCode');
144-
$command->setOrderTokenValue('cart');
145-
14679
$orderRepository->findCartByTokenValue('cart')->willReturn($cart);
14780

148-
$promotionCouponRepository->findOneBy(['code' => 'couponCode'])->willReturn($promotionCoupon);
149-
$promotionCoupon->getUsageLimit()->willReturn(1);
150-
$promotionCoupon->getExpiresAt()->willReturn(new \DateTime('now +1 year'));
81+
$promotionCouponRepository->findOneBy(['code' => 'couponCode'])->willReturn(null);
15182

152-
$promotionCoupon->getPromotion()->willReturn(null);
83+
$orderProcessor->process($cart)->shouldNotBeCalled();
15384

154-
$this->shouldThrow(\InvalidArgumentException::class)->during('__invoke', [$command]);
85+
$this->shouldThrow(\InvalidArgumentException::class)
86+
->during('__invoke', [ApplyCouponToCart::createFromData('cart', 'couponCode')]);
15587
}
15688
}

0 commit comments

Comments
 (0)