Skip to content

Commit

Permalink
[API][Admin] Validating coupons
Browse files Browse the repository at this point in the history
  • Loading branch information
NoResponseMate committed Nov 17, 2023
1 parent 62ad80d commit 2a19a82
Show file tree
Hide file tree
Showing 9 changed files with 347 additions and 13 deletions.
35 changes: 29 additions & 6 deletions features/promotion/managing_coupons/coupon_validation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Feature: Coupon validation
And it is coupon based promotion
And I am logged in as an administrator

@ui
@ui @api
Scenario: Trying to add a new coupon without specifying its code
When I want to create a new coupon for this promotion
And I do not specify its code
Expand All @@ -19,9 +19,9 @@ Feature: Coupon validation
And I make it valid until "26.03.2017"
And I try to add it
Then I should be notified that code is required
And there should be 0 coupon related to this promotion
And there should be 0 coupons related to this promotion

@ui
@ui @api
Scenario: Trying to add a new coupon with usage limit below one
When I want to create a new coupon for this promotion
And I specify its code as "SANTA2016"
Expand All @@ -30,9 +30,9 @@ Feature: Coupon validation
And I make it valid until "26.03.2017"
And I try to add it
Then I should be notified that coupon usage limit must be at least one
And there should be 0 coupon related to this promotion
And there should be 0 coupons related to this promotion

@ui
@ui @api
Scenario: Trying to add a new coupon with per customer usage limit below one
When I want to create a new coupon for this promotion
And I specify its code as "SANTA2016"
Expand All @@ -41,4 +41,27 @@ Feature: Coupon validation
And I make it valid until "26.03.2017"
And I try to add it
Then I should be notified that coupon usage limit per customer must be at least one
And there should be 0 coupon related to this promotion
And there should be 0 coupons related to this promotion

@api @no-ui
Scenario: Trying to add a new coupon with no promotion
Given I want to create a new coupon
And I specify its code as "RANDOM"
And I limit its usage to 30 times
And I limit its per customer usage to 3 times
And I make it valid until "26.03.2017"
And I try to add it
Then I should be notified that promotion is required
And there should be no coupon with code "RANDOM"

@api @no-ui
Scenario: Trying to add a new coupon for a non-coupon based promotion
Given there is a promotion "Flash sale"
When I want to create a new coupon for this promotion
And I specify its code as "FAST-50"
And I limit its usage to 50 times
And I limit its per customer usage to 1 time
And I make it valid until "26.03.2017"
And I try to add it
Then I should be notified that only coupon based promotions can have coupons
And there should be 0 coupons related to this promotion
113 changes: 109 additions & 4 deletions src/Sylius/Behat/Context/Api/Admin/ManagingPromotionCouponsContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ public function iWantToViewAllCouponsOfThisPromotion(PromotionInterface $promoti
$this->client->filter();
}

/**
* @When I want to create a new coupon
*/
public function iWantToCreateANewCoupon(): void
{
$this->client->buildCreateRequest(Resources::PROMOTION_COUPONS);
}

/**
* @When /^I want to create a new coupon for (this promotion)$/
*/
Expand Down Expand Up @@ -83,7 +91,7 @@ public function iSpecifyItsCodeAs(string $code): void
}

/**
* @When I limit its usage to :times times
* @When I limit its usage to :times time(s)
* @When I change its usage limit to :times
*/
public function iLimitItsUsageToTimes(int $times): void
Expand All @@ -92,7 +100,7 @@ public function iLimitItsUsageToTimes(int $times): void
}

/**
* @When I limit its per customer usage to :times times
* @When I limit its per customer usage to :times time(s)
* @When I change its per customer usage limit to :times
*/
public function iLimitItsPerCustomerUsageToTimes(int $times): void
Expand All @@ -118,7 +126,15 @@ public function iMakeItNotReusableFromCancelledOrders(): void
}

/**
* @When I add it
* @When I do not specify its :field
*/
public function iDoNotSpecifyIts(): void
{
// Intentionally left blank
}

/**
* @When I (try to) add it
*/
public function iAddIt(): void
{
Expand Down Expand Up @@ -171,7 +187,7 @@ public function iSortCouponsByExpirationDate(string $order): void
public function thereShouldBeCountCouponsRelatedToThisPromotion(int $count, PromotionInterface $promotion): void
{
$coupons = $this->responseChecker->getCollectionItemsWithValue(
$this->client->getLastResponse(),
$this->client->index(Resources::PROMOTION_COUPONS),
'promotion',
$this->sectionAwareIriConverter->getIriFromResourceInSection($promotion, 'admin'),
);
Expand All @@ -191,6 +207,18 @@ public function thereShouldBeACouponWithCode(string $code): void
));
}

/**
* @Then there should be no coupon with code :code
*/
public function thereShouldBeNoCouponWithCode(string $code): void
{
Assert::false($this->responseChecker->hasItemWithValue(
$this->client->index(Resources::PROMOTION_COUPONS),
'code',
$code,
));
}

/**
* @Then I should see :count coupons on the list
*/
Expand Down Expand Up @@ -318,6 +346,83 @@ public function iShouldBeNotifiedThatItIsInUseAndCannotBeDeleted(): void
);
}

/**
* @Then I should be notified that code is required
*/
public function iShouldBeNotifiedThatCodeIsRequired(): void
{
$response = $this->client->getLastResponse();
Assert::false(
$this->responseChecker->isCreationSuccessful($response),
'Coupon has been created successfully, but it should not',
);
Assert::same($this->responseChecker->getError($response), 'code: Please enter coupon code.');
}

/**
* @Then I should be notified that coupon usage limit must be at least one
*/
public function iShouldBeNotifiedThatCouponUsageLimitMustBeAtLeastOne(): void
{
$response = $this->client->getLastResponse();
Assert::false(
$this->responseChecker->isCreationSuccessful($response),
'Coupon has been created successfully, but it should not',
);
Assert::same(
$this->responseChecker->getError($response),
'usageLimit: Coupon usage limit must be at least 1.',
);
}

/**
* @Then I should be notified that coupon usage limit per customer must be at least one
*/
public function iShouldBeNotifiedThatCouponUsageLimitPerCustomerMustBeAtLeastOne(): void
{
$response = $this->client->getLastResponse();
Assert::false(
$this->responseChecker->isCreationSuccessful($response),
'Coupon has been created successfully, but it should not',
);
Assert::same(
$this->responseChecker->getError($response),
'perCustomerUsageLimit: Coupon usage limit per customer must be at least 1.',
);
}

/**
* @Then I should be notified that promotion is required
*/
public function iShouldBeNotifiedThatPromotionIsRequired(): void
{
$response = $this->client->getLastResponse();
Assert::false(
$this->responseChecker->isCreationSuccessful($response),
'Coupon has been created successfully, but it should not',
);
Assert::same(
$this->responseChecker->getError($response),
'promotion: Please provide a promotion for this coupon.',
);
}

/**
* @Then I should be notified that only coupon based promotions can have coupons
*/
public function iShouldBeNotifiedThatOnlyCouponBasedPromotionsCanHaveCoupons(): void
{
$response = $this->client->getLastResponse();
Assert::false(
$this->responseChecker->isCreationSuccessful($response),
'Coupon has been created successfully, but it should not',
);
Assert::same(
$this->responseChecker->getError($response),
'promotion: Only coupon based promotions can have coupons.',
);
}

private function sortBy(string $order, string $field): void
{
$this->client->sort([$field => str_starts_with($order, 'de') ? 'desc' : 'asc']);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public function specifySuffixAs(string $suffix): void
}

/**
* @When /^I limit generated coupons usage to (\d+) times$/
* @When /^I limit generated coupons usage to (\d+) times?$/
*/
public function iSetGeneratedCouponsUsageLimitTo(int $limit)
{
Expand All @@ -123,7 +123,7 @@ public function iSpecifyItsCodeAs($code = null)
}

/**
* @When I limit its usage to :limit times
* @When I limit its usage to :limit time(s)
*/
public function iLimitItsUsageLimitTo(int $limit)
{
Expand All @@ -149,7 +149,7 @@ public function iSpecifyItsAmountAs(?int $amount = null): void
}

/**
* @When /^I limit its per customer usage to ([^"]+) times$/
* @When /^I limit its per customer usage to ([^"]+) times?$/
*/
public function iLimitItsPerCustomerUsageLimitTo(int $limit)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,9 @@
<argument type="tagged_iterator" tag="sylius.catalog_promotion.scope_validator" index-by="key" />
<tag name="validator.constraint_validator" alias="sylius_catalog_promotion_scope" />
</service>

<service id="Sylius\Bundle\PromotionBundle\Validator\PromotionNotCouponBasedValidator">
<tag name="validator.constraint_validator" alias="sylius_promotion_not_coupon_based_validator" />
</service>
</services>
</container>
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@
<option name="message">sylius.promotion_coupon.code.unique</option>
<option name="groups">sylius</option>
</constraint>
<constraint name="Sylius\Bundle\PromotionBundle\Validator\Constraints\PromotionNotCouponBased">
<option name="groups">sylius</option>
</constraint>

<property name="promotion">
<constraint name="NotBlank">
<option name="message">sylius.promotion_coupon.promotion.not_blank</option>
<option name="groups">sylius</option>
</constraint>
</property>

<property name="code">
<constraint name="NotBlank">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ sylius:
regex: Coupon code can only be comprised of letters, numbers, dashes and underscores.
unique: This coupon already exists.
is_invalid: Coupon code is invalid.
promotion:
not_blank: Please provide a promotion for this coupon.
not_coupon_based: Only coupon based promotions can have coupons.
usage_limit:
min: Coupon usage limit must be at least {{ limit }}.
promotion_coupon_generator_instruction:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Bundle\PromotionBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;

final class PromotionNotCouponBased extends Constraint
{
public string $message = 'sylius.promotion_coupon.promotion.not_coupon_based';

public function getTargets(): string
{
return self::CLASS_CONSTRAINT;
}

public function validatedBy(): string
{
return 'sylius_promotion_not_coupon_based_validator';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Bundle\PromotionBundle\Validator;

use Sylius\Bundle\PromotionBundle\Validator\Constraints\PromotionNotCouponBased;
use Sylius\Component\Promotion\Model\PromotionCouponInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Webmozart\Assert\Assert;

final class PromotionNotCouponBasedValidator extends ConstraintValidator
{
public function validate(mixed $value, Constraint $constraint): void
{
if (null === $value) {
return;
}

/** @var PromotionNotCouponBased $constraint */
Assert::isInstanceOf($constraint, PromotionNotCouponBased::class);

/** @var PromotionCouponInterface $value */
Assert::isInstanceOf($value, PromotionCouponInterface::class);

$promotion = $value->getPromotion();
if (null === $promotion) {
return;
}

if (!$promotion->isCouponBased()) {
$this->context
->buildViolation($constraint->message)
->atPath('promotion')
->addViolation()
;
}
}
}

0 comments on commit 2a19a82

Please sign in to comment.