From 1c4685dbcc28eebbb6b473f4c871a6c0d87c615a Mon Sep 17 00:00:00 2001 From: Grzegorz Sadowski Date: Thu, 18 Nov 2021 10:44:45 +0100 Subject: [PATCH 01/11] [CatalogPromotion][Behat] Add scenarios for new fixed discount action --- ...available_types_catalog_promotions.feature | 23 -------------- ...ilable_types_of_catalog_promotions.feature | 23 ++++++++++++++ ...g_promotion_with_proper_priorities.feature | 10 +++++-- ...motions_with_fixed_discount_action.feature | 28 +++++++++++++++++ ...alog_promotions_with_minimum_price.feature | 23 -------------- ..._taking_minimum_price_into_account.feature | 30 +++++++++++++++++++ .../creating_catalog_promotion.feature | 12 ++++++++ .../editing_catalog_promotion.feature | 6 ++++ ...idating_catalog_promotion_creation.feature | 14 +++++++++ 9 files changed, 120 insertions(+), 49 deletions(-) delete mode 100644 features/promotion/applying_catalog_promotions/applying_all_available_types_catalog_promotions.feature create mode 100644 features/promotion/applying_catalog_promotions/applying_all_available_types_of_catalog_promotions.feature create mode 100644 features/promotion/applying_catalog_promotions/applying_catalog_promotions_with_fixed_discount_action.feature delete mode 100644 features/promotion/applying_catalog_promotions/applying_catalog_promotions_with_minimum_price.feature create mode 100644 features/promotion/applying_catalog_promotions/applying_catalog_promotions_with_taking_minimum_price_into_account.feature diff --git a/features/promotion/applying_catalog_promotions/applying_all_available_types_catalog_promotions.feature b/features/promotion/applying_catalog_promotions/applying_all_available_types_catalog_promotions.feature deleted file mode 100644 index 8ba6b680a2f..00000000000 --- a/features/promotion/applying_catalog_promotions/applying_all_available_types_catalog_promotions.feature +++ /dev/null @@ -1,23 +0,0 @@ -@applying_catalog_promotions -Feature: Applying all available catalog promotions - In order to see products in best prices - As a Customer - I want to see discounted products in the catalog - - Background: - Given the store operates on a channel named "Web-US" - And the store classifies its products as "Clothes" and "Dishes" - And the store has a "T-Shirt" configurable product - And this product belongs to "Clothes" - And this product has "PHP T-Shirt" variant priced at "$20.00" in "Web-US" channel - And the store has a "Mug" configurable product - And this product belongs to "Dishes" - And this product has "Coffee Mug" variant priced at "$5.00" in "Web-US" channel - And there is a catalog promotion "Clothes sale" that reduces price by "30%" and applies on "Clothes" taxon - And there is a catalog promotion "T-Shirt sale" that reduces price by "30%" and applies on "T-Shirt" product - And there is a catalog promotion "Winter sale" that reduces price by "30%" and applies on "PHP T-Shirt" variant - - @api @ui - Scenario: Applying multiple catalog promotions - When I view "PHP T-Shirt" variant of the "T-Shirt" product - Then I should see this variant is discounted from "$20.00" to "$6.86" with "Clothes sale", "T-Shirt sale" and "Winter sale" promotions diff --git a/features/promotion/applying_catalog_promotions/applying_all_available_types_of_catalog_promotions.feature b/features/promotion/applying_catalog_promotions/applying_all_available_types_of_catalog_promotions.feature new file mode 100644 index 00000000000..ea0b27f8bf4 --- /dev/null +++ b/features/promotion/applying_catalog_promotions/applying_all_available_types_of_catalog_promotions.feature @@ -0,0 +1,23 @@ +@applying_catalog_promotions +Feature: Applying all available catalog promotions + In order to see products in best prices + As a Customer + I want to see discounted products in the catalog + + Background: + Given the store operates on a channel named "Web-US" + And the store classifies its products as "Clothes" + And the store has a "T-Shirt" configurable product + And this product belongs to "Clothes" + And this product has "PHP T-Shirt" variant priced at "$20.00" in "Web-US" channel + And there is a catalog promotion "PHP sale" that reduces price by "10%" and applies on "PHP T-Shirt" variant + And there is a catalog promotion "T-Shirt sale" that reduces price by "10%" and applies on "T-Shirt" product + And there is a catalog promotion "Clothes sale" that reduces price by "10%" and applies on "Clothes" taxon + And there is a catalog promotion "Fixed PHP sale" that reduces price by fixed "$5.00" and applies on "PHP T-Shirt" variant + And there is a catalog promotion "Fixed T-Shirt sale" that reduces price by fixed "$5.00" and applies on "T-Shirt" product + And there is a catalog promotion "Fixed Clothes sale" that reduces price by fixed "$5.00" and applies on "Clothes" taxon + + @api @ui + Scenario: Applying multiple catalog promotions + When I view "PHP T-Shirt" variant of the "T-Shirt" product + Then I should see this variant is discounted from "$100.00" to "$57.90" with 6 promotions diff --git a/features/promotion/applying_catalog_promotions/applying_catalog_promotion_with_proper_priorities.feature b/features/promotion/applying_catalog_promotions/applying_catalog_promotion_with_proper_priorities.feature index 4fb476ad73d..34964a71e53 100644 --- a/features/promotion/applying_catalog_promotions/applying_catalog_promotion_with_proper_priorities.feature +++ b/features/promotion/applying_catalog_promotions/applying_catalog_promotion_with_proper_priorities.feature @@ -6,12 +6,16 @@ Feature: Applying catalog promotion with proper priorities Background: Given the store operates on a single channel in "United States" + And the store classifies its products as "Clothes" And the store has a "T-Shirt" configurable product + And this product belongs to "Clothes" And this product has "PHP T-Shirt" variant priced at "$100.00" - And there is a catalog promotion "Clothes sale" with priority 10 that reduces price by "30%" and applies on "PHP T-Shirt" variant - And there is a catalog promotion "Winter sale" with priority 100 that reduces price by "50%" and applies on "PHP T-Shirt" variant + And there is a catalog promotion "Autumn sale" with priority 10 that reduces price by "25%" and applies on "PHP T-Shirt" variant + And there is a catalog promotion "Winter sale" with priority 40 that reduces price by fixed "$10.00" and applies on "T-Shirt" product + And there is a catalog promotion "Spring sale" with priority 20 that reduces price by fixed "$5.00" and applies on "Clothes" taxon + And there is a catalog promotion "Summer sale" with priority 30 that reduces price by "50%" and applies on "PHP T-Shirt" variant @api @ui Scenario: Applying catalog promotion with descending order by their priority When I view "PHP T-Shirt" variant of the "T-Shirt" product - Then I should see this variant is discounted from "$100.00" to "$35.00" with "Winter sale" and "Clothes sale" promotions + Then I should see this variant is discounted from "$100.00" to "$30.00" with "Winter sale", "Summer sale", "Spring sale" and "Autumn sale" promotions diff --git a/features/promotion/applying_catalog_promotions/applying_catalog_promotions_with_fixed_discount_action.feature b/features/promotion/applying_catalog_promotions/applying_catalog_promotions_with_fixed_discount_action.feature new file mode 100644 index 00000000000..a059887fb35 --- /dev/null +++ b/features/promotion/applying_catalog_promotions/applying_catalog_promotions_with_fixed_discount_action.feature @@ -0,0 +1,28 @@ +@applying_catalog_promotions +Feature: Applying catalog promotions for product + In order to be attracted to specific products + As a Visitor + I want to see discounted products in the catalog + + Background: + Given the store operates on a single channel in "United States" + And the store has a "T-Shirt" configurable product + And this product has "PHP T-Shirt" variant priced at "$20.00" + And this product has "Java T-Shirt" variant priced at "$30.00" + And there is a catalog promotion "Winter sale" that reduces price by fixed "$10.00" and applies on "T-Shirt" product + + @api @ui + Scenario: Applying simple catalog promotion + When I view "PHP T-Shirt" variant of the "T-Shirt" product + Then I should see this variant is discounted from "$20.00" to "$10.00" with "Winter sale" promotion + + @api @ui @javascript + Scenario: Applying simple catalog promotion on another variant + When I view "Java T-Shirt" variant of the "T-Shirt" product + Then I should see this variant is discounted from "$30.00" to "$20.00" with "Winter sale" promotion + + @api @ui + Scenario: Applying multiple catalog promotions + Given there is a catalog promotion "Christmas sale" that reduces price by fixed "$5.00" and applies on "PHP T-Shirt" variant + When I view "PHP T-Shirt" variant of the "T-Shirt" product + Then I should see this variant is discounted from "$20.00" to "$5.00" with "Winter sale" and "Christmas sale" promotions diff --git a/features/promotion/applying_catalog_promotions/applying_catalog_promotions_with_minimum_price.feature b/features/promotion/applying_catalog_promotions/applying_catalog_promotions_with_minimum_price.feature deleted file mode 100644 index 9634c408c0a..00000000000 --- a/features/promotion/applying_catalog_promotions/applying_catalog_promotions_with_minimum_price.feature +++ /dev/null @@ -1,23 +0,0 @@ -@applying_catalog_promotions -Feature: Applying catalog promotions with minimum price - In order to avoid too many promotions applied on product - As a Visitor - I want to see discounted products in the catalog up to minimum price - - Background: - Given the store operates on a single channel in "United States" - And the store has a "T-Shirt" configurable product - And this product has "PHP T-Shirt" variant priced at "$20.00" - And the "PHP T-Shirt" variant has minimum price "$15.00" in "United States" channel - And there is a catalog promotion "Winter sale" that reduces price by "80%" and applies on "PHP T-Shirt" variant - - @api @ui - Scenario: Applying promotion up to minimum price - When I view "PHP T-Shirt" variant of the "T-Shirt" product - Then I should see this variant is discounted from "$20.00" to "$15.00" with "Winter sale" promotion - - @api @ui - Scenario: Not applying promotion if product is priced on same price as its minimum price - Given there is a catalog promotion "T-shirt Sale" that reduces price by "40%" and applies on "PHP T-Shirt" variant - When I view "PHP T-Shirt" variant of the "T-Shirt" product - Then I should see this variant is discounted from "$20.00" to "$15.00" with only "Winter sale" promotion diff --git a/features/promotion/applying_catalog_promotions/applying_catalog_promotions_with_taking_minimum_price_into_account.feature b/features/promotion/applying_catalog_promotions/applying_catalog_promotions_with_taking_minimum_price_into_account.feature new file mode 100644 index 00000000000..209e533ad1a --- /dev/null +++ b/features/promotion/applying_catalog_promotions/applying_catalog_promotions_with_taking_minimum_price_into_account.feature @@ -0,0 +1,30 @@ +@applying_catalog_promotions +Feature: Applying catalog promotions with taking minimum price into account + In order to avoid too much discount applied on product + As a Visitor + I want to see discounted products in the catalog up to minimum price + + Background: + Given the store operates on a single channel in "United States" + And the store has a "T-Shirt" configurable product + And this product has "PHP T-Shirt" variant priced at "$20.00" + And the "PHP T-Shirt" variant has minimum price "$15.00" in "United States" channel + + @api @ui + Scenario: Applying percentage discount up to minimum price + Given there is a catalog promotion "Winter sale" that reduces price by "80%" and applies on "PHP T-Shirt" variant + When I view "PHP T-Shirt" variant of the "T-Shirt" product + Then I should see this variant is discounted from "$20.00" to "$15.00" with "Winter sale" promotion + + @api @ui + Scenario: Applying fixed discount up to minimum price + Given there is a catalog promotion "Winter sale" that reduces price by fixed "$10.00" and applies on "PHP T-Shirt" variant + When I view "PHP T-Shirt" variant of the "T-Shirt" product + Then I should see this variant is discounted from "$20.00" to "$15.00" with "Winter sale" promotion + + @api @ui + Scenario: Not applying promotion if product is priced on same price as its minimum price + Given there is a catalog promotion "Winter sale" that reduces price by "80%" and applies on "PHP T-Shirt" variant + And there is a catalog promotion "T-shirt Sale" that reduces price by "40%" and applies on "PHP T-Shirt" variant + When I view "PHP T-Shirt" variant of the "T-Shirt" product + Then I should see this variant is discounted from "$20.00" to "$15.00" with only "Winter sale" promotion diff --git a/features/promotion/managing_catalog_promotions/creating_catalog_promotion.feature b/features/promotion/managing_catalog_promotions/creating_catalog_promotion.feature index 886e7e2093c..a8be4211eba 100644 --- a/features/promotion/managing_catalog_promotions/creating_catalog_promotion.feature +++ b/features/promotion/managing_catalog_promotions/creating_catalog_promotion.feature @@ -67,6 +67,18 @@ Feature: Creating a catalog promotion And it should have "winter_sale" code and "Winter sale" name And the catalog promotion "Winter sale" should be available in channel "United States" + @api @ui @javascript + Scenario: Creating a catalog promotion with fixed discount action + When I want to create a new catalog promotion + And I specify its code as "winter_sale" + And I name it "Winter sale" + And I add scope that applies on "Clothes" taxon + And I add action that gives "$10.00" fixed discount + And I add it + Then there should be 1 new catalog promotion on the list + And it should have "winter_sale" code and "Winter sale" name + And it should have "$10.00" fixed discount + @api @ui @javascript Scenario: Creating a catalog promotion for taxon When I want to create a new catalog promotion diff --git a/features/promotion/managing_catalog_promotions/editing_catalog_promotion.feature b/features/promotion/managing_catalog_promotions/editing_catalog_promotion.feature index ca31eb80ddd..40d651e5098 100644 --- a/features/promotion/managing_catalog_promotions/editing_catalog_promotion.feature +++ b/features/promotion/managing_catalog_promotions/editing_catalog_promotion.feature @@ -76,6 +76,12 @@ Feature: Editing catalog promotion Then I should be notified that it has been successfully edited And this catalog promotion should have "40%" percentage discount + @api @ui @javascript + Scenario: Editing catalog promotion action to be a fixed discount + When I edit "Christmas sale" catalog promotion to have "$10.00" fixed discount + Then I should be notified that it has been successfully edited + And this catalog promotion should have "$10.00" fixed discount + @api @ui Scenario: Being unable to edit catalog promotion if it is currently being processed Given the catalog promotion "Christmas sale" is currently being processed diff --git a/features/promotion/managing_catalog_promotions/validating_catalog_promotion_creation.feature b/features/promotion/managing_catalog_promotions/validating_catalog_promotion_creation.feature index a60e01b89d1..ede3928fff9 100644 --- a/features/promotion/managing_catalog_promotions/validating_catalog_promotion_creation.feature +++ b/features/promotion/managing_catalog_promotions/validating_catalog_promotion_creation.feature @@ -114,6 +114,20 @@ Feature: Validating a catalog promotion creation Then I should be notified that a discount amount should be a number and cannot be empty And there should be an empty list of catalog promotions + @api @ui @javascript + Scenario: Trying to create a catalog promotion with not configured fixed discount action + When I want to create a new catalog promotion + And I specify its code as "winter_sale" + And I name it "Winter sale" + And I make it available in channel "United States" + And I specify its label as "Winter -50%" in "English (United States)" + And I describe it as "This promotion gives a $10.00 discount on every product" in "English (United States)" + And I add scope that applies on variants "PHP T-Shirt" variant and "Kotlin T-Shirt" variant + And I add fixed discount action without amount configured + And I try to add it + Then I should be notified that a discount amount should be a number and cannot be empty + And there should be an empty list of catalog promotions + @api @ui @javascript Scenario: Trying to create a catalog promotion with taxon type without taxons When I want to create a new catalog promotion From 71ce649fa32fd7066f67434bfb992fc1f3e292b5 Mon Sep 17 00:00:00 2001 From: Grzegorz Sadowski Date: Thu, 18 Nov 2021 13:15:21 +0100 Subject: [PATCH 02/11] [CatalogPromotion][API] Creating and editing catalog promotions with new fixed discount action --- .../creating_catalog_promotion.feature | 3 +- .../editing_catalog_promotion.feature | 3 +- .../ManagingCatalogPromotionsContext.php | 53 +++++++++++++++++++ .../Resources/config/services/validators.xml | 4 ++ .../CatalogPromotionActionValidator.php | 16 ++++-- .../CatalogPromotionActionValidatorSpec.php | 5 ++ .../Model/CatalogPromotionActionInterface.php | 2 + 7 files changed, 81 insertions(+), 5 deletions(-) diff --git a/features/promotion/managing_catalog_promotions/creating_catalog_promotion.feature b/features/promotion/managing_catalog_promotions/creating_catalog_promotion.feature index a8be4211eba..2c2659dee22 100644 --- a/features/promotion/managing_catalog_promotions/creating_catalog_promotion.feature +++ b/features/promotion/managing_catalog_promotions/creating_catalog_promotion.feature @@ -67,7 +67,8 @@ Feature: Creating a catalog promotion And it should have "winter_sale" code and "Winter sale" name And the catalog promotion "Winter sale" should be available in channel "United States" - @api @ui @javascript + @api +# @ui @javascript Scenario: Creating a catalog promotion with fixed discount action When I want to create a new catalog promotion And I specify its code as "winter_sale" diff --git a/features/promotion/managing_catalog_promotions/editing_catalog_promotion.feature b/features/promotion/managing_catalog_promotions/editing_catalog_promotion.feature index 40d651e5098..4c63310f27b 100644 --- a/features/promotion/managing_catalog_promotions/editing_catalog_promotion.feature +++ b/features/promotion/managing_catalog_promotions/editing_catalog_promotion.feature @@ -76,7 +76,8 @@ Feature: Editing catalog promotion Then I should be notified that it has been successfully edited And this catalog promotion should have "40%" percentage discount - @api @ui @javascript + @api +# @ui @javascript Scenario: Editing catalog promotion action to be a fixed discount When I edit "Christmas sale" catalog promotion to have "$10.00" fixed discount Then I should be notified that it has been successfully edited diff --git a/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php b/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php index b7e953d02d7..fe0f301900e 100644 --- a/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php +++ b/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php @@ -206,6 +206,21 @@ public function iAddActionThatGivesPercentageDiscount(float $amount): void $this->client->addRequestData('actions', $actions); } + /** + * @When /^I add action that gives ("[^"]+") fixed discount$/ + */ + public function iAddActionThatGivesFixedDiscount(int $amount): void + { + $actions = [[ + 'type' => CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT, + 'configuration' => [ + 'amount' => $amount + ], + ]]; + + $this->client->addRequestData('actions', $actions); + } + /** * @When /^I add another action that gives ("[^"]+") percentage discount$/ */ @@ -535,6 +550,23 @@ public function iEditCatalogPromotionToHaveDiscount(CatalogPromotionInterface $c $this->client->update(); } + /** + * @When /^I edit ("[^"]+" catalog promotion) to have ("[^"]+") fixed discount$/ + */ + public function iEditCatalogPromotionToHaveFixedDiscount(CatalogPromotionInterface $catalogPromotion, int $amount): void + { + $this->client->buildUpdateRequest($catalogPromotion->getCode()); + $scopes = [[ + 'type' => CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT, + 'configuration' => [ + 'amount' => $amount, + ], + ]]; + + $this->client->updateRequestData(['actions' => $scopes]); + $this->client->update(); + } + /** * @When I add catalog promotion scope with nonexistent type */ @@ -703,6 +735,27 @@ public function itShouldHaveDiscount(float $amount): void Assert::same($catalogPromotion['actions'][0]['configuration']['amount'], $amount); } + /** + * @Then /^it should have ("[^"]+") fixed discount$/ + */ + public function itShouldHaveFixedDiscount(int $amount): void + { + $catalogPromotion = $this->responseChecker->getCollection($this->client->getLastResponse())[0]; + + Assert::same($catalogPromotion['actions'][0]['configuration']['amount'], $amount); + } + + /** + * @Then /^this catalog promotion should have ("[^"]+") fixed discount$/ + */ + public function thisCatalogPromotionShouldHaveFixedDiscount(int $amount): void + { + $catalogPromotionActions = $this->responseChecker->getValue($this->client->getLastResponse(), 'actions'); + + Assert::same($catalogPromotionActions[0]['type'], CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT); + Assert::same($catalogPromotionActions[0]['configuration']['amount'], $amount); + } + /** * @Then /^this catalog promotion should have ("[^"]+") percentage discount$/ * @Then /^it should reduce price by ("[^"]+")$/ diff --git a/src/Sylius/Bundle/PromotionBundle/Resources/config/services/validators.xml b/src/Sylius/Bundle/PromotionBundle/Resources/config/services/validators.xml index 6d3f1207149..c6f10b2e481 100644 --- a/src/Sylius/Bundle/PromotionBundle/Resources/config/services/validators.xml +++ b/src/Sylius/Bundle/PromotionBundle/Resources/config/services/validators.xml @@ -30,6 +30,10 @@ + + Sylius\Component\Promotion\Model\CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT + Sylius\Component\Promotion\Model\CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT + diff --git a/src/Sylius/Bundle/PromotionBundle/Validator/CatalogPromotionActionValidator.php b/src/Sylius/Bundle/PromotionBundle/Validator/CatalogPromotionActionValidator.php index a74c33ad863..16c5d19c069 100644 --- a/src/Sylius/Bundle/PromotionBundle/Validator/CatalogPromotionActionValidator.php +++ b/src/Sylius/Bundle/PromotionBundle/Validator/CatalogPromotionActionValidator.php @@ -14,6 +14,7 @@ namespace Sylius\Bundle\PromotionBundle\Validator; use Sylius\Bundle\PromotionBundle\Validator\Constraints\CatalogPromotionAction; +use Sylius\Component\Core\Model\CatalogPromotionScopeInterface; use Sylius\Component\Promotion\Model\CatalogPromotionActionInterface; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; @@ -21,13 +22,20 @@ final class CatalogPromotionActionValidator extends ConstraintValidator { + private array $actionTypes; + + public function __construct(array $actionTypes) + { + $this->actionTypes = $actionTypes; + } + public function validate($value, Constraint $constraint): void { /** @var CatalogPromotionAction $constraint */ Assert::isInstanceOf($constraint, CatalogPromotionAction::class); /** @var CatalogPromotionActionInterface $value */ - if ($value->getType() !== CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT) { + if (!in_array($value->getType(), $this->actionTypes, true)) { $this->context->buildViolation($constraint->invalidType)->atPath('type')->addViolation(); return; @@ -47,8 +55,10 @@ public function validate($value, Constraint $constraint): void return; } - if ($configuration['amount'] < 0 || $configuration['amount'] > 1) { - $this->context->buildViolation($constraint->notInRangeDiscount)->atPath('configuration.amount')->addViolation(); + if ($value->getType() === CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT) { + if ($configuration['amount'] < 0 || $configuration['amount'] > 1) { + $this->context->buildViolation($constraint->notInRangeDiscount)->atPath('configuration.amount')->addViolation(); + } } } } diff --git a/src/Sylius/Bundle/PromotionBundle/spec/Validator/CatalogPromotionActionValidatorSpec.php b/src/Sylius/Bundle/PromotionBundle/spec/Validator/CatalogPromotionActionValidatorSpec.php index f2b0a4fb704..18e0389dc21 100644 --- a/src/Sylius/Bundle/PromotionBundle/spec/Validator/CatalogPromotionActionValidatorSpec.php +++ b/src/Sylius/Bundle/PromotionBundle/spec/Validator/CatalogPromotionActionValidatorSpec.php @@ -24,6 +24,11 @@ final class CatalogPromotionActionValidatorSpec extends ObjectBehavior { function let(ExecutionContextInterface $executionContext): void { + $this->beConstructedWith([ + CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT, + CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT, + ]); + $this->initialize($executionContext); } diff --git a/src/Sylius/Component/Promotion/Model/CatalogPromotionActionInterface.php b/src/Sylius/Component/Promotion/Model/CatalogPromotionActionInterface.php index f5c93a63dfd..33efb43317c 100644 --- a/src/Sylius/Component/Promotion/Model/CatalogPromotionActionInterface.php +++ b/src/Sylius/Component/Promotion/Model/CatalogPromotionActionInterface.php @@ -17,6 +17,8 @@ interface CatalogPromotionActionInterface extends ResourceInterface { + public const TYPE_FIXED_DISCOUNT = 'fixed_discount'; + public const TYPE_PERCENTAGE_DISCOUNT = 'percentage_discount'; public function setType(?string $type): void; From 45dc8e39f977bb2786ff7ca04a647587dfc5ff73 Mon Sep 17 00:00:00 2001 From: Grzegorz Sadowski Date: Tue, 23 Nov 2021 00:11:06 +0100 Subject: [PATCH 03/11] [CatalogPromotion][UI] Creating and editing catalog promotions with new fixed discount action --- .../creating_catalog_promotion.feature | 7 +-- .../editing_catalog_promotion.feature | 7 +-- .../ManagingCatalogPromotionsContext.php | 32 ++++++---- .../ManagingCatalogPromotionsContext.php | 41 +++++++++++++ .../Admin/CatalogPromotion/FormElement.php | 24 +++++++- .../CatalogPromotion/FormElementInterface.php | 8 +++ .../CatalogPromotionActionTypeExtension.php | 58 +++++++++++++++++++ .../Resources/config/services/form.xml | 6 ++ .../AdminBundle/Resources/private/js/app.js | 8 +++ .../js/sylius-catalog-promotion-actions.js | 37 ++++++++++++ .../views/CatalogPromotion/_action.html.twig | 3 + .../Applicator/CatalogPromotionApplicator.php | 8 +++ ...edFixedDiscountActionConfigurationType.php | 32 ++++++++++ .../Resources/config/services/form.xml | 5 ++ .../FixedDiscountActionConfigurationType.php | 42 ++++++++++++++ ...centageDiscountActionConfigurationType.php | 2 +- .../Form/Type/CatalogPromotionActionType.php | 57 ++++++++++++++++-- .../Resources/config/services/forms.xml | 11 +++- .../Resources/translations/messages.en.yml | 1 + .../CatalogPromotionActionValidator.php | 10 ++-- 20 files changed, 364 insertions(+), 35 deletions(-) create mode 100644 src/Sylius/Bundle/AdminBundle/Form/Extension/CatalogPromotionActionTypeExtension.php create mode 100644 src/Sylius/Bundle/AdminBundle/Resources/private/js/sylius-catalog-promotion-actions.js create mode 100644 src/Sylius/Bundle/AdminBundle/Resources/views/CatalogPromotion/_action.html.twig create mode 100644 src/Sylius/Bundle/CoreBundle/Form/Type/CatalogPromotionAction/ChannelBasedFixedDiscountActionConfigurationType.php create mode 100644 src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionAction/FixedDiscountActionConfigurationType.php rename src/Sylius/Bundle/PromotionBundle/Form/Type/{CatalogAction => CatalogPromotionAction}/PercentageDiscountActionConfigurationType.php (91%) diff --git a/features/promotion/managing_catalog_promotions/creating_catalog_promotion.feature b/features/promotion/managing_catalog_promotions/creating_catalog_promotion.feature index 2c2659dee22..e16e86a82cc 100644 --- a/features/promotion/managing_catalog_promotions/creating_catalog_promotion.feature +++ b/features/promotion/managing_catalog_promotions/creating_catalog_promotion.feature @@ -67,18 +67,17 @@ Feature: Creating a catalog promotion And it should have "winter_sale" code and "Winter sale" name And the catalog promotion "Winter sale" should be available in channel "United States" - @api -# @ui @javascript + @api @ui @javascript Scenario: Creating a catalog promotion with fixed discount action When I want to create a new catalog promotion And I specify its code as "winter_sale" And I name it "Winter sale" And I add scope that applies on "Clothes" taxon - And I add action that gives "$10.00" fixed discount + And I add action that gives "$10.00" fixed discount in the "United States" channel And I add it Then there should be 1 new catalog promotion on the list And it should have "winter_sale" code and "Winter sale" name - And it should have "$10.00" fixed discount + And the "Winter sale" catalog promotion should have "$10.00" fixed discount in the "United States" channel @api @ui @javascript Scenario: Creating a catalog promotion for taxon diff --git a/features/promotion/managing_catalog_promotions/editing_catalog_promotion.feature b/features/promotion/managing_catalog_promotions/editing_catalog_promotion.feature index 4c63310f27b..195007241fc 100644 --- a/features/promotion/managing_catalog_promotions/editing_catalog_promotion.feature +++ b/features/promotion/managing_catalog_promotions/editing_catalog_promotion.feature @@ -76,12 +76,11 @@ Feature: Editing catalog promotion Then I should be notified that it has been successfully edited And this catalog promotion should have "40%" percentage discount - @api -# @ui @javascript + @api @ui @javascript Scenario: Editing catalog promotion action to be a fixed discount - When I edit "Christmas sale" catalog promotion to have "$10.00" fixed discount + When I edit "Christmas sale" catalog promotion to have "$10.00" fixed discount in the "United States" channel Then I should be notified that it has been successfully edited - And this catalog promotion should have "$10.00" fixed discount + And this catalog promotion should have "$10.00" fixed discount in the "United States" channel @api @ui Scenario: Being unable to edit catalog promotion if it is currently being processed diff --git a/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php b/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php index fe0f301900e..e42dc677ad9 100644 --- a/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php +++ b/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php @@ -207,14 +207,16 @@ public function iAddActionThatGivesPercentageDiscount(float $amount): void } /** - * @When /^I add action that gives ("[^"]+") fixed discount$/ + * @When /^I add action that gives ("[^"]+") fixed discount in the ("[^"]+" channel)$/ */ - public function iAddActionThatGivesFixedDiscount(int $amount): void + public function iAddActionThatGivesFixedDiscount(int $amount, ChannelInterface $channel): void { $actions = [[ 'type' => CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT, 'configuration' => [ - 'amount' => $amount + $channel->getCode() => [ + 'amount' => $amount + ], ], ]]; @@ -551,10 +553,13 @@ public function iEditCatalogPromotionToHaveDiscount(CatalogPromotionInterface $c } /** - * @When /^I edit ("[^"]+" catalog promotion) to have ("[^"]+") fixed discount$/ + * @When /^I edit ("[^"]+" catalog promotion) to have ("[^"]+") fixed discount in the ("[^"]+" channel)$/ */ - public function iEditCatalogPromotionToHaveFixedDiscount(CatalogPromotionInterface $catalogPromotion, int $amount): void - { + public function iEditCatalogPromotionToHaveFixedDiscountInTheChannel( + CatalogPromotionInterface $catalogPromotion, + int $amount, + ChannelInterface $channel + ): void { $this->client->buildUpdateRequest($catalogPromotion->getCode()); $scopes = [[ 'type' => CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT, @@ -736,19 +741,22 @@ public function itShouldHaveDiscount(float $amount): void } /** - * @Then /^it should have ("[^"]+") fixed discount$/ + * @Then /^the ("[^"]+" catalog promotion) should have ("[^"]+") fixed discount in the ("[^"]+" channel)$/ */ - public function itShouldHaveFixedDiscount(int $amount): void - { + public function theCatalogPromotionShouldHaveFixedDiscountInTheChannel( + CatalogPromotionInterface $catalogPromotion, + int $amount, + ChannelInterface $channel + ): void { $catalogPromotion = $this->responseChecker->getCollection($this->client->getLastResponse())[0]; - Assert::same($catalogPromotion['actions'][0]['configuration']['amount'], $amount); + Assert::same($catalogPromotion['actions'][0]['configuration'][$channel->getCode()]['amount'], $amount); } /** - * @Then /^this catalog promotion should have ("[^"]+") fixed discount$/ + * @Then /^this catalog promotion should have ("[^"]+") fixed discount in the ("[^"]+" channel)$/ */ - public function thisCatalogPromotionShouldHaveFixedDiscount(int $amount): void + public function thisCatalogPromotionShouldHaveFixedDiscountInTheChannel(int $amount, ChannelInterface $channel): void { $catalogPromotionActions = $this->responseChecker->getValue($this->client->getLastResponse(), 'actions'); diff --git a/src/Sylius/Behat/Context/Ui/Admin/ManagingCatalogPromotionsContext.php b/src/Sylius/Behat/Context/Ui/Admin/ManagingCatalogPromotionsContext.php index 85f4e676fee..0aaa33b272d 100644 --- a/src/Sylius/Behat/Context/Ui/Admin/ManagingCatalogPromotionsContext.php +++ b/src/Sylius/Behat/Context/Ui/Admin/ManagingCatalogPromotionsContext.php @@ -260,9 +260,20 @@ public function iRemoveItsEveryAction(): void public function iAddActionThatGivesPercentageDiscount(string $discount): void { $this->formElement->addAction(); + $this->formElement->chooseActionType('Percentage discount'); $this->formElement->specifyLastActionDiscount($discount); } + /** + * @When /^I add action that gives "(?:€|£|\$)([^"]+)" fixed discount in the ("[^"]+" channel)$/ + */ + public function iAddActionThatGivesFixedDiscount(string $discount, ChannelInterface $channel): void + { + $this->formElement->addAction(); + $this->formElement->chooseActionType('Fixed discount'); + $this->formElement->specifyLastActionDiscountForChannel($discount, $channel); + } + /** * @When I (try to) add it */ @@ -357,6 +368,22 @@ public function iEditCatalogPromotionToHaveDiscount(CatalogPromotionInterface $c $this->updatePage->saveChanges(); } + /** + * @When /^I edit ("[^"]+" catalog promotion) to have "(?:€|£|\$)([^"]+)" fixed discount in the ("[^"]+" channel)$/ + */ + public function iEditCatalogPromotionToHaveFixedDiscountInTheChannel( + CatalogPromotionInterface $catalogPromotion, + string $discount, + ChannelInterface $channel + ): void { + $this->updatePage->open(['id' => $catalogPromotion->getId()]); + $this->formElement->chooseActionType('Fixed discount'); + $this->formElement->specifyLastActionDiscountForChannel($discount, $channel); + $this->updatePage->saveChanges(); + + $this->sharedStorage->set('catalog_promotion', $catalogPromotion); + } + /** * @When I disable :catalogPromotion catalog promotion */ @@ -662,6 +689,20 @@ public function itShouldHaveDiscount(string $amount): void Assert::same($this->formElement->getLastActionDiscount(), $amount); } + /** + * @Then /^the ("[^"]+" catalog promotion) should have "(?:€|£|\$)([^"]+)" fixed discount in the ("[^"]+" channel)$/ + * @Then /^(this catalog promotion) should have "(?:€|£|\$)([^"]+)" fixed discount in the ("[^"]+" channel)$/ + */ + public function theCatalogPromotionShouldHaveFixedDiscountInTheChannel( + CatalogPromotionInterface $catalogPromotion, + string $amount, + ChannelInterface $channel + ): void { + $this->updatePage->open(['id' => $catalogPromotion->getId()]); + + Assert::same($this->formElement->getLastActionFixedDiscount($channel), $amount); + } + /** * @Then there should still be only one catalog promotion with code :code */ diff --git a/src/Sylius/Behat/Element/Admin/CatalogPromotion/FormElement.php b/src/Sylius/Behat/Element/Admin/CatalogPromotion/FormElement.php index c0dd5211798..07c1766595d 100644 --- a/src/Sylius/Behat/Element/Admin/CatalogPromotion/FormElement.php +++ b/src/Sylius/Behat/Element/Admin/CatalogPromotion/FormElement.php @@ -16,6 +16,7 @@ use Behat\Mink\Element\NodeElement; use Behat\Mink\Exception\ElementNotFoundException; use FriendsOfBehat\PageObjectExtension\Element\Element; +use Sylius\Component\Core\Model\ChannelInterface; use Webmozart\Assert\Assert; final class FormElement extends Element implements FormElementInterface @@ -86,6 +87,13 @@ public function chooseScopeType(string $type): void $lastScope->selectFieldOption('Type', $type); } + public function chooseActionType(string $type): void + { + $lastScope = $this->getElement('last_action'); + + $lastScope->selectFieldOption('Type', $type); + } + public function chooseLastScopeCodes(array $codes): void { $lastScope = $this->getElement('last_scope'); @@ -96,10 +104,17 @@ public function chooseLastScopeCodes(array $codes): void public function specifyLastActionDiscount(string $discount): void { $lastAction = $this->getElement('last_action'); - +// $lastAction->find('css', 'input')->setValue($discount); } + public function specifyLastActionDiscountForChannel(string $discount, ChannelInterface $channel): void + { + $lastAction = $this->getElement('last_action'); + + $lastAction->find('css', sprintf('.field:contains("%s") input', $channel->getName()))->setValue($discount); + } + public function getFieldValueInLocale(string $field, string $localeCode): string { return $this->getElement($field, ['%localeCode%' => $localeCode])->getValue(); @@ -119,6 +134,13 @@ public function getLastActionDiscount(): string return $lastAction->find('css', 'input')->getValue(); } + public function getLastActionFixedDiscount(ChannelInterface $channel): string + { + $lastAction = $this->getElement('last_action'); + + return $lastAction->find('css', 'input')->getValue(); + } + public function getValidationMessage(): string { $foundElement = $this->getDocument()->find('css', '.sylius-validation-error'); diff --git a/src/Sylius/Behat/Element/Admin/CatalogPromotion/FormElementInterface.php b/src/Sylius/Behat/Element/Admin/CatalogPromotion/FormElementInterface.php index 728f35ac709..ec2aa0be703 100644 --- a/src/Sylius/Behat/Element/Admin/CatalogPromotion/FormElementInterface.php +++ b/src/Sylius/Behat/Element/Admin/CatalogPromotion/FormElementInterface.php @@ -13,6 +13,8 @@ namespace Sylius\Behat\Element\Admin\CatalogPromotion; +use Sylius\Component\Core\Model\ChannelInterface; + interface FormElementInterface { public function nameIt(string $name): void; @@ -39,16 +41,22 @@ public function addAction(): void; public function chooseScopeType(string $type): void; + public function chooseActionType(string $type): void; + public function chooseLastScopeCodes(array $codes): void; public function specifyLastActionDiscount(string $discount): void; + public function specifyLastActionDiscountForChannel(string $discount, ChannelInterface $channel): void; + public function getFieldValueInLocale(string $field, string $localeCode): string; public function getLastScopeCodes(): array; public function getLastActionDiscount(): string; + public function getLastActionFixedDiscount(ChannelInterface $channel): string; + public function getValidationMessage(): string; public function removeAllActions(): void; diff --git a/src/Sylius/Bundle/AdminBundle/Form/Extension/CatalogPromotionActionTypeExtension.php b/src/Sylius/Bundle/AdminBundle/Form/Extension/CatalogPromotionActionTypeExtension.php new file mode 100644 index 00000000000..909081f48b9 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Form/Extension/CatalogPromotionActionTypeExtension.php @@ -0,0 +1,58 @@ + $formType) { + $this->actionConfigurationTypes[$type] = get_class($formType); + $this->actionTypes['sylius.form.catalog_promotion.action.' . $type] = $type; + } + ksort($this->actionTypes); + + $this->twig = $twig; + } + + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder + ->add('type', ChoiceType::class, [ + 'label' => 'sylius.ui.type', + 'choices' => $this->actionTypes, + 'choice_attr' => function(?string $type) use ($builder): array { + return [ + 'data-configuration' => $this->twig->render( + '@SyliusAdmin/CatalogPromotion/_action.html.twig', + ['field' => $builder->create( + 'configuration', + $this->actionConfigurationTypes[$type], + ['label' => false, 'csrf_protection' => false] + )->getForm()->createView()] + ) + ]; + } + ]) + ; + } + + public static function getExtendedTypes(): iterable + { + return [CatalogPromotionActionType::class]; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Resources/config/services/form.xml b/src/Sylius/Bundle/AdminBundle/Resources/config/services/form.xml index 818f37778c2..9564e09e4e3 100644 --- a/src/Sylius/Bundle/AdminBundle/Resources/config/services/form.xml +++ b/src/Sylius/Bundle/AdminBundle/Resources/config/services/form.xml @@ -18,6 +18,12 @@ + + + + + + diff --git a/src/Sylius/Bundle/AdminBundle/Resources/private/js/app.js b/src/Sylius/Bundle/AdminBundle/Resources/private/js/app.js index 058b99a054e..042d6379589 100644 --- a/src/Sylius/Bundle/AdminBundle/Resources/private/js/app.js +++ b/src/Sylius/Bundle/AdminBundle/Resources/private/js/app.js @@ -17,6 +17,7 @@ import 'sylius/ui/sylius-product-attributes'; import 'sylius/ui/sylius-product-auto-complete'; import 'sylius/ui/sylius-prototype-handler'; +import './sylius-catalog-promotion-actions'; import './sylius-catalog-promotion-scopes'; import './sylius-compound-form-errors'; import './sylius-lazy-choice-tree'; @@ -80,6 +81,10 @@ $(document).ready(() => { } }); + $(document).loadCatalogPromotionActionConfiguration( + document.querySelector('#sylius_catalog_promotion_actions [data-form-collection="item"]:last-child') + ); + $(document).loadCatalogPromotionScopeConfiguration( document.querySelector('#sylius_catalog_promotion_scopes [data-form-collection="item"]:last-child') ); @@ -101,6 +106,9 @@ $(document).ready(() => { $(document).taxonSlugGenerator(); $(document).previewUploadedImage('#sylius_product_images'); $(document).previewUploadedImage('#sylius_taxon_images'); + if ($('#sylius_catalog_promotion_actions').length > 0) { + $(document).loadCatalogPromotionActionConfiguration(document.querySelector('#sylius_catalog_promotion_actions')); + } if ($('#sylius_catalog_promotion_scopes').length > 0) { $(document).loadCatalogPromotionScopeConfiguration(document.querySelector('#sylius_catalog_promotion_scopes')); } diff --git a/src/Sylius/Bundle/AdminBundle/Resources/private/js/sylius-catalog-promotion-actions.js b/src/Sylius/Bundle/AdminBundle/Resources/private/js/sylius-catalog-promotion-actions.js new file mode 100644 index 00000000000..4d46b47df2b --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/private/js/sylius-catalog-promotion-actions.js @@ -0,0 +1,37 @@ +/* + * This file is part of the Sylius package. + * + * (c) Paweł Jędrzejewski + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import $ from 'jquery'; + +$.fn.extend({ + loadCatalogPromotionActionConfiguration(target) { + target.querySelector('#sylius_catalog_promotion_actions select[name*="type"]').onchange = function () { + const parent = this.parentElement; + const newConfig = document.createElement('div'); + newConfig.innerHTML = this.selectedOptions[0].getAttribute('data-configuration'); + const oldConfig = parent.nextElementSibling; + + parent.parentElement.replaceChild(newConfig, oldConfig); + + const oldConfigInputName = oldConfig.querySelector('input').getAttribute('name'); + let newConfigInputs = newConfig.querySelectorAll('input'); + + newConfigInputs.forEach(element => { + let newConfigInputName = element.getAttribute('name'); + + newConfigInputName = oldConfigInputName.replace( + oldConfigInputName.substring(oldConfigInputName.indexOf('[configuration]') + 15), + newConfigInputName.substring(newConfigInputName.indexOf('configuration') + 13), + ); + + $(element).attr('name', newConfigInputName); + }); + }; + }, +}); diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/CatalogPromotion/_action.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/CatalogPromotion/_action.html.twig new file mode 100644 index 00000000000..4e5841dd903 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/CatalogPromotion/_action.html.twig @@ -0,0 +1,3 @@ +{% form_theme field '@SyliusAdmin/Form/theme.html.twig' %} + +{{ form_row(field) }} diff --git a/src/Sylius/Bundle/CoreBundle/Applicator/CatalogPromotionApplicator.php b/src/Sylius/Bundle/CoreBundle/Applicator/CatalogPromotionApplicator.php index 1a7eb59c6ee..7ef7782f6d1 100644 --- a/src/Sylius/Bundle/CoreBundle/Applicator/CatalogPromotionApplicator.php +++ b/src/Sylius/Bundle/CoreBundle/Applicator/CatalogPromotionApplicator.php @@ -34,6 +34,10 @@ public function applyOnVariant( CatalogPromotionInterface $catalogPromotion ): void { foreach ($catalogPromotion->getActions() as $action) { + if ($action->getType() === CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT) { + continue; + } + $this->applyDiscountFromAction($catalogPromotion, $action, $variant); } } @@ -47,6 +51,10 @@ public function applyOnChannelPricing( } foreach ($catalogPromotion->getActions() as $action) { + if ($action->getType() === CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT) { + continue; + } + $this->applyDiscountFromActionOnChannelPricing( $catalogPromotion, $action->getConfiguration()['amount'], diff --git a/src/Sylius/Bundle/CoreBundle/Form/Type/CatalogPromotionAction/ChannelBasedFixedDiscountActionConfigurationType.php b/src/Sylius/Bundle/CoreBundle/Form/Type/CatalogPromotionAction/ChannelBasedFixedDiscountActionConfigurationType.php new file mode 100644 index 00000000000..3c0ef7f50cf --- /dev/null +++ b/src/Sylius/Bundle/CoreBundle/Form/Type/CatalogPromotionAction/ChannelBasedFixedDiscountActionConfigurationType.php @@ -0,0 +1,32 @@ +setDefaults([ + 'entry_type' => FixedDiscountActionConfigurationType::class, + 'entry_options' => function (ChannelInterface $channel) { + return [ + 'label' => $channel->getName(), + 'currency' => $channel->getBaseCurrency()->getCode(), + ]; + }, + ]); + } + + public function getParent(): string + { + return ChannelCollectionType::class; + } +} diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/services/form.xml b/src/Sylius/Bundle/CoreBundle/Resources/config/services/form.xml index 0af33c5369f..9f5932c188d 100644 --- a/src/Sylius/Bundle/CoreBundle/Resources/config/services/form.xml +++ b/src/Sylius/Bundle/CoreBundle/Resources/config/services/form.xml @@ -160,6 +160,11 @@ + + + + + diff --git a/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionAction/FixedDiscountActionConfigurationType.php b/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionAction/FixedDiscountActionConfigurationType.php new file mode 100644 index 00000000000..b15e8c27775 --- /dev/null +++ b/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionAction/FixedDiscountActionConfigurationType.php @@ -0,0 +1,42 @@ +add('amount', MoneyType::class, [ + 'label' => 'sylius.ui.amount', + 'constraints' => [ + new NotBlank(['groups' => ['sylius']]), + new Type(['type' => 'numeric', 'groups' => ['sylius']]), + ], + 'currency' => $options['currency'], + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver + ->setRequired('currency') + ->setAllowedTypes('currency', 'string') + ; + } + + public function getBlockPrefix(): string + { + return 'sylius_catalog_promotion_action_fixed_discount_configuration'; + } +} diff --git a/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogAction/PercentageDiscountActionConfigurationType.php b/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionAction/PercentageDiscountActionConfigurationType.php similarity index 91% rename from src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogAction/PercentageDiscountActionConfigurationType.php rename to src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionAction/PercentageDiscountActionConfigurationType.php index 0fb36cf8e0a..060c25c1ed2 100644 --- a/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogAction/PercentageDiscountActionConfigurationType.php +++ b/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionAction/PercentageDiscountActionConfigurationType.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Sylius\Bundle\PromotionBundle\Form\Type\CatalogAction; +namespace Sylius\Bundle\PromotionBundle\Form\Type\CatalogPromotionAction; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\PercentType; diff --git a/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionActionType.php b/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionActionType.php index db7a9baf59d..81f0a8d553e 100644 --- a/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionActionType.php +++ b/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionActionType.php @@ -4,33 +4,78 @@ namespace Sylius\Bundle\PromotionBundle\Form\Type; -use Sylius\Bundle\PromotionBundle\Form\Type\CatalogAction\PercentageDiscountActionConfigurationType; use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType; +use Sylius\Component\Promotion\Model\CatalogPromotionActionInterface; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\FormEvents; final class CatalogPromotionActionType extends AbstractResourceType { - private array $actionTypes; + private array $actionTypes = []; - public function __construct(string $dataClass, array $validationGroups = [], array $actionTypes = []) - { + private array $actionConfigurationTypes; + + public function __construct( + string $dataClass, + array $validationGroups, + iterable $actionConfigurationTypes + ) { parent::__construct($dataClass, $validationGroups); - $this->actionTypes = $actionTypes; + foreach ($actionConfigurationTypes as $type => $formType) { + $this->actionConfigurationTypes[$type] = get_class($formType); + $this->actionTypes['sylius.form.catalog_promotion.action.' . $type] = $type; + } + ksort($this->actionTypes); } public function buildForm(FormBuilderInterface $builder, array $options) { + $defaultActionType = current($this->actionTypes); + $defaultActionConfigurationType = $this->actionConfigurationTypes[$defaultActionType]; + $builder ->add('type', ChoiceType::class, [ 'label' => 'sylius.ui.type', 'choices' => $this->actionTypes, ]) - ->add('configuration', PercentageDiscountActionConfigurationType::class, [ + ->add('configuration', $defaultActionConfigurationType, [ 'label' => false, ]) ; + + $builder + ->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event): void { + /** @var CatalogPromotionActionInterface|null $data */ + $data = $event->getData(); + $form = $event->getForm(); + + if ($data === null) { + return; + } + + $actionConfigurationType = $this->actionConfigurationTypes[$data->getType()]; + $form->add('configuration', $actionConfigurationType, [ + 'label' => false, + ]); + }) + ->addEventListener(FormEvents::PRE_SUBMIT, function(FormEvent $event): void { + /** @var array|null $data */ + $data = $event->getData(); + $form = $event->getForm(); + + if ($data === null) { + return; + } + + $actionConfigurationType = $this->actionConfigurationTypes[$data['type']]; + $form->add('configuration', $actionConfigurationType, [ + 'label' => false, + ]); + }) + ; } public function getBlockPrefix(): string diff --git a/src/Sylius/Bundle/PromotionBundle/Resources/config/services/forms.xml b/src/Sylius/Bundle/PromotionBundle/Resources/config/services/forms.xml index 91e20984190..05e3bb8fe82 100644 --- a/src/Sylius/Bundle/PromotionBundle/Resources/config/services/forms.xml +++ b/src/Sylius/Bundle/PromotionBundle/Resources/config/services/forms.xml @@ -31,6 +31,8 @@ sylius + Sylius\Component\Promotion\Model\CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT + Sylius\Component\Promotion\Model\CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT @@ -50,12 +52,15 @@ + + + + + %sylius.model.catalog_promotion_action.class% %sylius.form.type.catalog_promotion.validation_groups% - - Sylius\Component\Promotion\Model\CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT - + diff --git a/src/Sylius/Bundle/PromotionBundle/Resources/translations/messages.en.yml b/src/Sylius/Bundle/PromotionBundle/Resources/translations/messages.en.yml index fe370330331..87bf80c62dd 100644 --- a/src/Sylius/Bundle/PromotionBundle/Resources/translations/messages.en.yml +++ b/src/Sylius/Bundle/PromotionBundle/Resources/translations/messages.en.yml @@ -4,6 +4,7 @@ sylius: form: catalog_promotion: action: + fixed_discount: Fixed discount percentage_discount: Percentage discount description: Description enabled: Enabled diff --git a/src/Sylius/Bundle/PromotionBundle/Validator/CatalogPromotionActionValidator.php b/src/Sylius/Bundle/PromotionBundle/Validator/CatalogPromotionActionValidator.php index 16c5d19c069..e11d55d0b58 100644 --- a/src/Sylius/Bundle/PromotionBundle/Validator/CatalogPromotionActionValidator.php +++ b/src/Sylius/Bundle/PromotionBundle/Validator/CatalogPromotionActionValidator.php @@ -41,6 +41,10 @@ public function validate($value, Constraint $constraint): void return; } + if ($value->getType() !== CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT) { + return; + } + $configuration = $value->getConfiguration(); if (!array_key_exists('amount', $configuration)) { @@ -55,10 +59,8 @@ public function validate($value, Constraint $constraint): void return; } - if ($value->getType() === CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT) { - if ($configuration['amount'] < 0 || $configuration['amount'] > 1) { - $this->context->buildViolation($constraint->notInRangeDiscount)->atPath('configuration.amount')->addViolation(); - } + if ($configuration['amount'] < 0 || $configuration['amount'] > 1) { + $this->context->buildViolation($constraint->notInRangeDiscount)->atPath('configuration.amount')->addViolation(); } } } From b5458fa31aadaf699e39b0cc106d5efd25144823 Mon Sep 17 00:00:00 2001 From: Grzegorz Sadowski Date: Tue, 23 Nov 2021 12:23:27 +0100 Subject: [PATCH 04/11] [CatalogPromotion][API][UI] Add validation for new fixed discount action --- ...idating_catalog_promotion_creation.feature | 30 ++++- .../ManagingCatalogPromotionsContext.php | 62 +++++++++++ .../ManagingCatalogPromotionsContext.php | 25 ++++- .../Admin/CatalogPromotion/FormElement.php | 13 +++ .../CatalogPromotion/FormElementInterface.php | 2 + .../Resources/config/services/validators.xml | 5 + .../Resources/translations/validators.en.yml | 4 + .../FixedDiscountActionValidator.php | 57 ++++++++++ .../FixedDiscountActionValidatorSpec.php | 104 ++++++++++++++++++ .../Resources/config/services/validators.xml | 5 + .../ActionValidatorInterface.php | 22 ++++ .../PercentageDiscountActionValidator.php | 44 ++++++++ .../CatalogPromotionActionValidator.php | 28 ++--- .../PercentageDiscountActionValidatorSpec.php | 84 ++++++++++++++ .../CatalogPromotionActionValidatorSpec.php | 80 ++++---------- tests/Api/Admin/CatalogPromotionsTest.php | 26 +++++ ...omotion_with_invalid_actions_response.json | 22 +++- 17 files changed, 535 insertions(+), 78 deletions(-) create mode 100644 src/Sylius/Bundle/CoreBundle/Validator/CatalogPromotionAction/FixedDiscountActionValidator.php create mode 100644 src/Sylius/Bundle/CoreBundle/spec/Validator/CatalogPromotionAction/FixedDiscountActionValidatorSpec.php create mode 100644 src/Sylius/Bundle/PromotionBundle/Validator/CatalogPromotionAction/ActionValidatorInterface.php create mode 100644 src/Sylius/Bundle/PromotionBundle/Validator/CatalogPromotionAction/PercentageDiscountActionValidator.php create mode 100644 src/Sylius/Bundle/PromotionBundle/spec/Validator/CatalogPromotionAction/PercentageDiscountActionValidatorSpec.php diff --git a/features/promotion/managing_catalog_promotions/validating_catalog_promotion_creation.feature b/features/promotion/managing_catalog_promotions/validating_catalog_promotion_creation.feature index ede3928fff9..ee7d5971327 100644 --- a/features/promotion/managing_catalog_promotions/validating_catalog_promotion_creation.feature +++ b/features/promotion/managing_catalog_promotions/validating_catalog_promotion_creation.feature @@ -125,7 +125,35 @@ Feature: Validating a catalog promotion creation And I add scope that applies on variants "PHP T-Shirt" variant and "Kotlin T-Shirt" variant And I add fixed discount action without amount configured And I try to add it - Then I should be notified that a discount amount should be a number and cannot be empty + Then I should be notified that a discount amount should be configured for at least one channel + And there should be an empty list of catalog promotions + + @api @ui @javascript + Scenario: Trying to create a catalog promotion with wrong value of fixed discount action + When I want to create a new catalog promotion + And I specify its code as "winter_sale" + And I name it "Winter sale" + And I make it available in channel "United States" + And I specify its label as "Winter -50%" in "English (United States)" + And I describe it as "This promotion gives a $10.00 discount on every product" in "English (United States)" + And I add scope that applies on variants "PHP T-Shirt" variant and "Kotlin T-Shirt" variant + And I add invalid fixed discount action with non number in amount for the "United States" channel + And I try to add it + Then I should be notified that a discount amount should be configured for at least one channel + And there should be an empty list of catalog promotions + + @api + Scenario: Trying to create a catalog promotion with fixed discount action with invalid channel + When I want to create a new catalog promotion + And I specify its code as "winter_sale" + And I name it "Winter sale" + And I make it available in channel "United States" + And I specify its label as "Winter -50%" in "English (United States)" + And I describe it as "This promotion gives a $10.00 discount on every product" in "English (United States)" + And I add scope that applies on variants "PHP T-Shirt" variant and "Kotlin T-Shirt" variant + And I add invalid fixed discount action configured for nonexistent channel + And I try to add it + Then I should be notified that at least one of the provided channel codes does not exist And there should be an empty list of catalog promotions @api @ui @javascript diff --git a/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php b/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php index e42dc677ad9..61d284caedf 100644 --- a/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php +++ b/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php @@ -630,6 +630,46 @@ public function iAddPercentageDiscountActionWithoutAmountConfigured(): void $this->client->addRequestData('actions', $actions); } + /** + * @When I add fixed discount action without amount configured + */ + public function iAddFixedDiscountActionWithoutAmountConfigured(): void + { + $actions = [[ + 'type' => CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT, + 'configuration' => [], + ]]; + + $this->client->addRequestData('actions', $actions); + } + + /** + * @When I add invalid fixed discount action with non number in amount for the :channel channel + */ + public function iAddInvalidFixedDiscountActionWithNonNumberInAmountForTheChannel( + ChannelInterface $channel + ): void { + $actions = [[ + 'type' => CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT, + 'configuration' => [$channel->getCode() => ['amount' => 'wrong value']], + ]]; + + $this->client->addRequestData('actions', $actions); + } + + /** + * @When I add invalid fixed discount action configured for nonexistent channel + */ + public function iAddInvalidFixedDiscountActionConfiguredForNonexistentChannel(): void + { + $actions = [[ + 'type' => CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT, + 'configuration' => ['nonexistent_action' => ['amount' => 1000]], + ]]; + + $this->client->addRequestData('actions', $actions); + } + /** * @When I add catalog promotion action with nonexistent type */ @@ -1196,6 +1236,28 @@ public function iShouldBeNotifiedThatDiscountAmountShouldBeNumber(): void ); } + /** + * @Then I should be notified that a discount amount should be configured for at least one channel + */ + public function iShouldBeNotifiedThatADiscountAmountShouldBeConfiguredForAtLeasOneChannel(): void + { + Assert::contains( + $this->responseChecker->getError($this->client->getLastResponse()), + 'Provided configuration contains errors. Please add the fixed discount amount greater than 0 for at least 1 channel.' + ); + } + + /** + * @Then I should be notified that at least one of the provided channel codes does not exist + */ + public function iShouldBeNotifiedThatAtLeastOneOfTheProvidedChannelCodesDoesNotExist(): void + { + Assert::contains( + $this->responseChecker->getError($this->client->getLastResponse()), + 'Provided configuration contains errors. At least one of the provided channel codes does not exist.' + ); + } + /** * @Then I should be notified that scope configuration is invalid */ diff --git a/src/Sylius/Behat/Context/Ui/Admin/ManagingCatalogPromotionsContext.php b/src/Sylius/Behat/Context/Ui/Admin/ManagingCatalogPromotionsContext.php index 0aaa33b272d..494436993b3 100644 --- a/src/Sylius/Behat/Context/Ui/Admin/ManagingCatalogPromotionsContext.php +++ b/src/Sylius/Behat/Context/Ui/Admin/ManagingCatalogPromotionsContext.php @@ -443,8 +443,9 @@ public function iAddCatalogPromotionScopeForProductWithoutProducts(): void /** * @When I add percentage discount action without amount configured + * @When I add fixed discount action without amount configured */ - public function iAddPercentageDiscountActionWithoutAmountConfigured(): void + public function iAddDiscountActionWithoutAmountConfigured(): void { $this->formElement->addAction(); } @@ -458,6 +459,18 @@ public function iAddInvalidPercentageDiscountActionWithNonNumberInAmount(): void $this->formElement->specifyLastActionDiscount('alot'); } + + /** + * @When I add invalid fixed discount action with non number in amount for the :channel channel + */ + public function iAddInvalidFixedDiscountActionWithNonNumberInAmountForTheChannel( + ChannelInterface $channel + ): void { + $this->formElement->addAction(); + $this->formElement->chooseActionType('Fixed discount'); + $this->formElement->specifyLastActionDiscountForChannel('wrong value', $channel); + } + /** * @When /^I make (this catalog promotion) unavailable in the ("[^"]+" channel)$/ * @When /^I make the ("[^"]+" catalog promotion) unavailable in the ("[^"]+" channel)$/ @@ -529,6 +542,16 @@ public function iShouldBeNotifiedThatADiscountAmountShouldBeANumber(): void Assert::same($this->formElement->getValidationMessage(), 'The percentage discount amount must be a number and can not be empty.'); } + /** + * @Then I should be notified that a discount amount should be configured for at least one channel + */ + public function iShouldBeNotifiedThatADiscountAmountShouldBeConfiguredForAtLeasOneChannel(): void + { + Assert::true($this->formElement->hasValidationMessage( + 'Provided configuration contains errors. Please add the fixed discount amount greater than 0 for at least 1 channel.' + )); + } + /** * @Then there should be :amount catalog promotions on the list * @Then there should be :amount new catalog promotion on the list diff --git a/src/Sylius/Behat/Element/Admin/CatalogPromotion/FormElement.php b/src/Sylius/Behat/Element/Admin/CatalogPromotion/FormElement.php index 07c1766595d..c025638313e 100644 --- a/src/Sylius/Behat/Element/Admin/CatalogPromotion/FormElement.php +++ b/src/Sylius/Behat/Element/Admin/CatalogPromotion/FormElement.php @@ -152,6 +152,19 @@ public function getValidationMessage(): string return $foundElement->getText(); } + public function hasValidationMessage(string $message): bool + { + $validationElements = $this->getDocument()->findAll('css', '.sylius-validation-error'); + + foreach ($validationElements as $validationElement) { + if ($validationElement->getText() === $message) { + return true; + } + } + + return false; + } + public function removeAllActions(): void { $deleteButtons = $this->getDocument()->findAll('css', '#actions [data-form-collection="delete"]'); diff --git a/src/Sylius/Behat/Element/Admin/CatalogPromotion/FormElementInterface.php b/src/Sylius/Behat/Element/Admin/CatalogPromotion/FormElementInterface.php index ec2aa0be703..da745e266b2 100644 --- a/src/Sylius/Behat/Element/Admin/CatalogPromotion/FormElementInterface.php +++ b/src/Sylius/Behat/Element/Admin/CatalogPromotion/FormElementInterface.php @@ -59,6 +59,8 @@ public function getLastActionFixedDiscount(ChannelInterface $channel): string; public function getValidationMessage(): string; + public function hasValidationMessage(string $message): bool; + public function removeAllActions(): void; public function removeAllScopes(): void; diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/services/validators.xml b/src/Sylius/Bundle/CoreBundle/Resources/config/services/validators.xml index 82608a858bc..981d0d9c6db 100644 --- a/src/Sylius/Bundle/CoreBundle/Resources/config/services/validators.xml +++ b/src/Sylius/Bundle/CoreBundle/Resources/config/services/validators.xml @@ -74,6 +74,11 @@ + + + + + diff --git a/src/Sylius/Bundle/CoreBundle/Resources/translations/validators.en.yml b/src/Sylius/Bundle/CoreBundle/Resources/translations/validators.en.yml index 015a6967217..d5c898f3278 100644 --- a/src/Sylius/Bundle/CoreBundle/Resources/translations/validators.en.yml +++ b/src/Sylius/Bundle/CoreBundle/Resources/translations/validators.en.yml @@ -3,6 +3,10 @@ sylius: file: max_size: The image is too big - {{ size }}{{ suffix }}. Maximum allowed size is {{ limit }}{{ suffix }}. upload_ini_size: The image is too big. Maximum allowed size is {{ limit }}{{ suffix }}. + catalog_promotion_action: + fixed_discount: + invalid_channel: Provided configuration contains errors. At least one of the provided channel codes does not exist. + not_valid: Provided configuration contains errors. Please add the fixed discount amount greater than 0 for at least 1 channel. catalog_promotion_scope: invalid_type: Catalog promotion scope type is invalid. Please choose a valid type. for_products: diff --git a/src/Sylius/Bundle/CoreBundle/Validator/CatalogPromotionAction/FixedDiscountActionValidator.php b/src/Sylius/Bundle/CoreBundle/Validator/CatalogPromotionAction/FixedDiscountActionValidator.php new file mode 100644 index 00000000000..c081a3f2f73 --- /dev/null +++ b/src/Sylius/Bundle/CoreBundle/Validator/CatalogPromotionAction/FixedDiscountActionValidator.php @@ -0,0 +1,57 @@ +channelRepository = $channelRepository; + } + + public function validate(array $configuration, Constraint $constraint, ExecutionContextInterface $context): void + { + /** @var CatalogPromotionAction $constraint */ + Assert::isInstanceOf($constraint, CatalogPromotionAction::class); + + if (empty($configuration)) { + $context->buildViolation('sylius.catalog_promotion_action.fixed_discount.not_valid')->atPath('configuration')->addViolation(); + + return; + } + + foreach ($configuration as $channelCode => $channelConfiguration) { + if (null === $this->channelRepository->findOneBy(['code' => $channelCode])) { + $context->buildViolation('sylius.catalog_promotion_action.fixed_discount.invalid_channel')->atPath('configuration')->addViolation(); + + return; + } + + if (!array_key_exists('amount', $channelConfiguration) || !is_integer($channelConfiguration['amount']) || $channelConfiguration['amount'] < 0) { + $context->buildViolation('sylius.catalog_promotion_action.fixed_discount.not_valid')->atPath('configuration')->addViolation(); + + return; + } + } + } +} diff --git a/src/Sylius/Bundle/CoreBundle/spec/Validator/CatalogPromotionAction/FixedDiscountActionValidatorSpec.php b/src/Sylius/Bundle/CoreBundle/spec/Validator/CatalogPromotionAction/FixedDiscountActionValidatorSpec.php new file mode 100644 index 00000000000..5342f4cd03e --- /dev/null +++ b/src/Sylius/Bundle/CoreBundle/spec/Validator/CatalogPromotionAction/FixedDiscountActionValidatorSpec.php @@ -0,0 +1,104 @@ +beConstructedWith($channelRepository); + } + + function it_is_an_action_validator(): void + { + $this->shouldHaveType(ActionValidatorInterface::class); + } + + function it_adds_violation_if_catalog_promotion_action_has_an_empty_configuration( + ExecutionContextInterface $executionContext, + ConstraintViolationBuilderInterface $constraintViolationBuilder + ): void { + $executionContext->buildViolation('sylius.catalog_promotion_action.fixed_discount.not_empty')->willReturn($constraintViolationBuilder); + $constraintViolationBuilder->atPath('configuration')->willReturn($constraintViolationBuilder); + $constraintViolationBuilder->addViolation()->shouldBeCalled(); + + $this->validate([], new CatalogPromotionAction(), $executionContext); + } + + function it_adds_violation_if_catalog_promotion_action_has_configured_channel_that_does_not_exist( + ChannelRepositoryInterface $channelRepository, + ExecutionContextInterface $executionContext, + ConstraintViolationBuilderInterface $constraintViolationBuilder + ): void { + $channelRepository->findOneBy(['code' => 'nonexistent_channel'])->willReturn(null); + + $executionContext->buildViolation('sylius.catalog_promotion_action.fixed_discount.invalid_channel')->willReturn($constraintViolationBuilder); + $constraintViolationBuilder->atPath('configuration')->willReturn($constraintViolationBuilder); + $constraintViolationBuilder->addViolation()->shouldBeCalled(); + + $this->validate(['nonexistent_channel' => ['amount' => 1000]], new CatalogPromotionAction(), $executionContext); + } + + function it_adds_violation_if_catalog_promotion_action_has_not_configured_amount( + ChannelRepositoryInterface $channelRepository, + ExecutionContextInterface $executionContext, + ConstraintViolationBuilderInterface $constraintViolationBuilder, + ChannelInterface $channel + ): void { + $channelRepository->findOneBy(['code' => 'channel'])->willReturn($channel); + + $executionContext->buildViolation('sylius.catalog_promotion_action.fixed_discount.not_valid')->willReturn($constraintViolationBuilder); + $constraintViolationBuilder->atPath('configuration')->willReturn($constraintViolationBuilder); + $constraintViolationBuilder->addViolation()->shouldBeCalled(); + + $this->validate(['channel' => []], new CatalogPromotionAction(), $executionContext); + } + + function it_adds_violation_if_catalog_promotion_action_has_invalid_amount_configured( + ChannelRepositoryInterface $channelRepository, + ExecutionContextInterface $executionContext, + ConstraintViolationBuilderInterface $constraintViolationBuilder, + ChannelInterface $channel + ): void { + $channelRepository->findOneBy(['code' => 'channel'])->willReturn($channel); + + $executionContext->buildViolation('sylius.catalog_promotion_action.fixed_discount.not_valid')->willReturn($constraintViolationBuilder); + $constraintViolationBuilder->atPath('configuration')->willReturn($constraintViolationBuilder); + $constraintViolationBuilder->addViolation()->shouldBeCalled(); + + $this->validate(['channel' => ['amount' => 'wrong_value']], new CatalogPromotionAction(), $executionContext); + } + + function it_does_nothing_if_the_provided_configuration_is_valid( + ChannelRepositoryInterface $channelRepository, + ExecutionContextInterface $executionContext, + ChannelInterface $channel + ): void { + $channelRepository->findOneBy(['code' => 'channel'])->willReturn($channel); + + $executionContext->buildViolation('sylius.catalog_promotion_action.fixed_discount.not_empty')->shouldNotBeCalled(); + $executionContext->buildViolation('sylius.catalog_promotion_action.fixed_discount.invalid_channel')->shouldNotBeCalled(); + $executionContext->buildViolation('sylius.catalog_promotion_action.fixed_discount.not_valid')->shouldNotBeCalled(); + + $this->validate(['channel' => ['amount' => 1000]], new CatalogPromotionAction(), $executionContext); + } +} diff --git a/src/Sylius/Bundle/PromotionBundle/Resources/config/services/validators.xml b/src/Sylius/Bundle/PromotionBundle/Resources/config/services/validators.xml index c6f10b2e481..88eb9f44b9d 100644 --- a/src/Sylius/Bundle/PromotionBundle/Resources/config/services/validators.xml +++ b/src/Sylius/Bundle/PromotionBundle/Resources/config/services/validators.xml @@ -29,11 +29,16 @@ + + + + Sylius\Component\Promotion\Model\CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT Sylius\Component\Promotion\Model\CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT + diff --git a/src/Sylius/Bundle/PromotionBundle/Validator/CatalogPromotionAction/ActionValidatorInterface.php b/src/Sylius/Bundle/PromotionBundle/Validator/CatalogPromotionAction/ActionValidatorInterface.php new file mode 100644 index 00000000000..1c70d7c563c --- /dev/null +++ b/src/Sylius/Bundle/PromotionBundle/Validator/CatalogPromotionAction/ActionValidatorInterface.php @@ -0,0 +1,22 @@ +buildViolation($constraint->notNumberOrEmpty)->atPath('configuration.amount')->addViolation(); + + return; + } + + if (!is_float($configuration['amount']) && !is_integer($configuration['amount'])) { + $context->buildViolation($constraint->notNumberOrEmpty)->atPath('configuration.amount')->addViolation(); + + return; + } + + if ($configuration['amount'] < 0 || $configuration['amount'] > 1) { + $context->buildViolation($constraint->notInRangeDiscount)->atPath('configuration.amount')->addViolation(); + } + } +} diff --git a/src/Sylius/Bundle/PromotionBundle/Validator/CatalogPromotionActionValidator.php b/src/Sylius/Bundle/PromotionBundle/Validator/CatalogPromotionActionValidator.php index e11d55d0b58..b05693fe55c 100644 --- a/src/Sylius/Bundle/PromotionBundle/Validator/CatalogPromotionActionValidator.php +++ b/src/Sylius/Bundle/PromotionBundle/Validator/CatalogPromotionActionValidator.php @@ -13,8 +13,8 @@ namespace Sylius\Bundle\PromotionBundle\Validator; +use Sylius\Bundle\PromotionBundle\Validator\CatalogPromotionAction\ActionValidatorInterface; use Sylius\Bundle\PromotionBundle\Validator\Constraints\CatalogPromotionAction; -use Sylius\Component\Core\Model\CatalogPromotionScopeInterface; use Sylius\Component\Promotion\Model\CatalogPromotionActionInterface; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; @@ -24,9 +24,12 @@ final class CatalogPromotionActionValidator extends ConstraintValidator { private array $actionTypes; - public function __construct(array $actionTypes) + private array $actionValidators; + + public function __construct(array $actionTypes, iterable $actionValidators) { $this->actionTypes = $actionTypes; + $this->actionValidators = $actionValidators instanceof \Traversable ? iterator_to_array($actionValidators) : $actionValidators; } public function validate($value, Constraint $constraint): void @@ -41,26 +44,15 @@ public function validate($value, Constraint $constraint): void return; } - if ($value->getType() !== CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT) { + $type = $value->getType(); + if (!key_exists($type, $this->actionValidators)) { return; } $configuration = $value->getConfiguration(); - if (!array_key_exists('amount', $configuration)) { - $this->context->buildViolation($constraint->notNumberOrEmpty)->atPath('configuration.amount')->addViolation(); - - return; - } - - if (!is_float($configuration['amount']) && !is_integer($configuration['amount'])) { - $this->context->buildViolation($constraint->notNumberOrEmpty)->atPath('configuration.amount')->addViolation(); - - return; - } - - if ($configuration['amount'] < 0 || $configuration['amount'] > 1) { - $this->context->buildViolation($constraint->notInRangeDiscount)->atPath('configuration.amount')->addViolation(); - } + /** @var ActionValidatorInterface $validator */ + $validator = $this->actionValidators[$type]; + $validator->validate($configuration, $constraint, $this->context); } } diff --git a/src/Sylius/Bundle/PromotionBundle/spec/Validator/CatalogPromotionAction/PercentageDiscountActionValidatorSpec.php b/src/Sylius/Bundle/PromotionBundle/spec/Validator/CatalogPromotionAction/PercentageDiscountActionValidatorSpec.php new file mode 100644 index 00000000000..bb0fb7d1d9f --- /dev/null +++ b/src/Sylius/Bundle/PromotionBundle/spec/Validator/CatalogPromotionAction/PercentageDiscountActionValidatorSpec.php @@ -0,0 +1,84 @@ +shouldHaveType(ActionValidatorInterface::class); + } + + function it_adds_violation_if_catalog_promotion_action_has_invalid_discount( + ExecutionContextInterface $executionContext, + ConstraintViolationBuilderInterface $constraintViolationBuilder, + CatalogPromotionActionInterface $action + ): void { + $action->getConfiguration()->willReturn([]); + + $executionContext->buildViolation('sylius.catalog_promotion_action.percentage_discount.not_number_or_empty')->willReturn($constraintViolationBuilder); + $constraintViolationBuilder->atPath('configuration.amount')->willReturn($constraintViolationBuilder); + $constraintViolationBuilder->addViolation()->shouldBeCalled(); + + $this->validate([], new CatalogPromotionAction(), $executionContext); + } + + function it_adds_violation_if_catalog_promotion_action_has_discount_in_wrong_range( + ExecutionContextInterface $executionContext, + ConstraintViolationBuilderInterface $constraintViolationBuilder, + CatalogPromotionActionInterface $action + ): void { + $action->getConfiguration()->willReturn(['amount' => 2]); + + $executionContext->buildViolation('sylius.catalog_promotion_action.percentage_discount.not_in_range')->willReturn($constraintViolationBuilder); + $constraintViolationBuilder->atPath('configuration.amount')->willReturn($constraintViolationBuilder); + $constraintViolationBuilder->addViolation()->shouldBeCalled(); + + $this->validate(['amount' => 2], new CatalogPromotionAction(), $executionContext); + } + + function it_adds_violation_if_catalog_promotion_action_has_wrong_type_on_amount( + ExecutionContextInterface $executionContext, + ConstraintViolationBuilderInterface $constraintViolationBuilder, + CatalogPromotionActionInterface $action + ): void { + $action->getConfiguration()->willReturn(['amount' => 'text']); + + $executionContext->buildViolation('sylius.catalog_promotion_action.percentage_discount.not_number_or_empty')->willReturn($constraintViolationBuilder); + $constraintViolationBuilder->atPath('configuration.amount')->willReturn($constraintViolationBuilder); + $constraintViolationBuilder->addViolation()->shouldBeCalled(); + + $this->validate(['amount' => 'text'], new CatalogPromotionAction(), $executionContext); + } + + function it_does_nothing_if_catalog_promotion_action_is_valid( + ExecutionContextInterface $executionContext, + CatalogPromotionActionInterface $action + ): void { + $action->getConfiguration()->willReturn(['amount' => 0.2]); + + $executionContext->buildViolation('sylius.catalog_promotion_action.invalid_type')->shouldNotBeCalled(); + $executionContext->buildViolation('sylius.catalog_promotion_action.percentage_discount.not_valid')->shouldNotBeCalled(); + $executionContext->buildViolation('sylius.catalog_promotion_action.percentage_discount.not_in_range')->shouldNotBeCalled(); + + $this->validate(['amount' => 0.2], new CatalogPromotionAction(), $executionContext); + } +} diff --git a/src/Sylius/Bundle/PromotionBundle/spec/Validator/CatalogPromotionActionValidatorSpec.php b/src/Sylius/Bundle/PromotionBundle/spec/Validator/CatalogPromotionActionValidatorSpec.php index 18e0389dc21..1ac47e43a13 100644 --- a/src/Sylius/Bundle/PromotionBundle/spec/Validator/CatalogPromotionActionValidatorSpec.php +++ b/src/Sylius/Bundle/PromotionBundle/spec/Validator/CatalogPromotionActionValidatorSpec.php @@ -14,6 +14,7 @@ namespace spec\Sylius\Bundle\PromotionBundle\Validator; use PhpSpec\ObjectBehavior; +use Sylius\Bundle\PromotionBundle\Validator\CatalogPromotionAction\ActionValidatorInterface; use Sylius\Bundle\PromotionBundle\Validator\Constraints\CatalogPromotionAction; use Sylius\Component\Promotion\Model\CatalogPromotionActionInterface; use Symfony\Component\Validator\ConstraintValidator; @@ -22,12 +23,21 @@ final class CatalogPromotionActionValidatorSpec extends ObjectBehavior { - function let(ExecutionContextInterface $executionContext): void - { - $this->beConstructedWith([ - CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT, - CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT, - ]); + function let( + ExecutionContextInterface $executionContext, + ActionValidatorInterface $fixedDiscountValidator, + ActionValidatorInterface $percentageDiscountValidator + ): void { + $this->beConstructedWith( + [ + CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT, + CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT, + ], + [ + CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT => $fixedDiscountValidator, + CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT => $percentageDiscountValidator, + ] + ); $this->initialize($executionContext); } @@ -51,62 +61,18 @@ function it_adds_violation_if_catalog_promotion_action_has_invalid_type( $this->validate($action, new CatalogPromotionAction()); } - function it_adds_violation_if_catalog_promotion_action_has_invalid_discount( + function it_calls_a_proper_validator_to_validate_the_configuration( ExecutionContextInterface $executionContext, - ConstraintViolationBuilderInterface $constraintViolationBuilder, - CatalogPromotionActionInterface $action + CatalogPromotionActionInterface $action, + ActionValidatorInterface $percentageDiscountValidator ): void { - $action->getType()->willReturn(CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT); - $action->getConfiguration()->willReturn([]); - - $executionContext->buildViolation('sylius.catalog_promotion_action.percentage_discount.not_number_or_empty')->willReturn($constraintViolationBuilder); - $constraintViolationBuilder->atPath('configuration.amount')->willReturn($constraintViolationBuilder); - $constraintViolationBuilder->addViolation()->shouldBeCalled(); - - $this->validate($action, new CatalogPromotionAction()); - } + $constraint = new CatalogPromotionAction(); - function it_adds_violation_if_catalog_promotion_action_has_discount_in_wrong_range( - ExecutionContextInterface $executionContext, - ConstraintViolationBuilderInterface $constraintViolationBuilder, - CatalogPromotionActionInterface $action - ): void { $action->getType()->willReturn(CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT); - $action->getConfiguration()->willReturn(['amount' => 2]); - - $executionContext->buildViolation('sylius.catalog_promotion_action.percentage_discount.not_in_range')->willReturn($constraintViolationBuilder); - $constraintViolationBuilder->atPath('configuration.amount')->willReturn($constraintViolationBuilder); - $constraintViolationBuilder->addViolation()->shouldBeCalled(); - - $this->validate($action, new CatalogPromotionAction()); - } - - function it_adds_violation_if_catalog_promotion_action_has_wrong_type_on_amount( - ExecutionContextInterface $executionContext, - ConstraintViolationBuilderInterface $constraintViolationBuilder, - CatalogPromotionActionInterface $action - ): void { - $action->getType()->willReturn(CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT); - $action->getConfiguration()->willReturn(['amount' => 'text']); - - $executionContext->buildViolation('sylius.catalog_promotion_action.percentage_discount.not_number_or_empty')->willReturn($constraintViolationBuilder); - $constraintViolationBuilder->atPath('configuration.amount')->willReturn($constraintViolationBuilder); - $constraintViolationBuilder->addViolation()->shouldBeCalled(); - - $this->validate($action, new CatalogPromotionAction()); - } - - function it_does_nothing_if_catalog_promotion_action_is_valid( - ExecutionContextInterface $executionContext, - CatalogPromotionActionInterface $action - ): void { - $action->getType()->willReturn(CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT); - $action->getConfiguration()->willReturn(['amount' => 0.2]); + $action->getConfiguration()->willReturn([]); - $executionContext->buildViolation('sylius.catalog_promotion_action.invalid_type')->shouldNotBeCalled(); - $executionContext->buildViolation('sylius.catalog_promotion_action.percentage_discount.not_valid')->shouldNotBeCalled(); - $executionContext->buildViolation('sylius.catalog_promotion_action.percentage_discount.not_in_range')->shouldNotBeCalled(); + $percentageDiscountValidator->validate([], $constraint, $executionContext)->shouldBeCalled(); - $this->validate($action, new CatalogPromotionAction()); + $this->validate($action, $constraint); } } diff --git a/tests/Api/Admin/CatalogPromotionsTest.php b/tests/Api/Admin/CatalogPromotionsTest.php index 4d9c53ad2ce..ff94169e6c1 100644 --- a/tests/Api/Admin/CatalogPromotionsTest.php +++ b/tests/Api/Admin/CatalogPromotionsTest.php @@ -324,6 +324,32 @@ public function it_does_not_create_a_catalog_promotion_with_invalid_actions(): v 'configuration' => [ 'amount' => 'text' ] + ], + [ + 'type' => CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT, + 'configuration' => [] + ], + [ + 'type' => CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT, + 'configuration' => [ + 'WEB' => [], + ] + ], + [ + 'type' => CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT, + 'configuration' => [ + 'invalid_channel' => [ + 'amount' => 1000, + ], + ] + ], + [ + 'type' => CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT, + 'configuration' => [ + 'WEB' => [ + 'amount' => 'text', + ] + ] ] ], 'scopes' => [ diff --git a/tests/Api/Responses/Expected/admin/catalog_promotion/post_catalog_promotion_with_invalid_actions_response.json b/tests/Api/Responses/Expected/admin/catalog_promotion/post_catalog_promotion_with_invalid_actions_response.json index 29f015695f3..f30481faca7 100644 --- a/tests/Api/Responses/Expected/admin/catalog_promotion/post_catalog_promotion_with_invalid_actions_response.json +++ b/tests/Api/Responses/Expected/admin/catalog_promotion/post_catalog_promotion_with_invalid_actions_response.json @@ -2,7 +2,7 @@ "@context": "/api/v2/contexts/ConstraintViolationList", "@type": "ConstraintViolationList", "hydra:title": "An error occurred", - "hydra:description": "actions[0].type: Catalog promotion action type is invalid. Please choose a valid type.\nactions[1].configuration.amount: The percentage discount amount must be a number and can not be empty.\nactions[2].configuration.amount: The percentage discount amount must be between 0% and 100%.\nactions[3].configuration.amount: The percentage discount amount must be a number and can not be empty.", + "hydra:description": "actions[0].type: Catalog promotion action type is invalid. Please choose a valid type.\nactions[1].configuration.amount: The percentage discount amount must be a number and can not be empty.\nactions[2].configuration.amount: The percentage discount amount must be between 0% and 100%.\nactions[3].configuration.amount: The percentage discount amount must be a number and can not be empty.\nactions[4].configuration: Provided configuration contains errors. Please add the fixed discount amount greater than 0 for at least 1 channel.\nactions[5].configuration: Provided configuration contains errors. Please add the fixed discount amount greater than 0 for at least 1 channel.\nactions[6].configuration: Provided configuration contains errors. At least one of the provided channel codes does not exist.\nactions[7].configuration: Provided configuration contains errors. Please add the fixed discount amount greater than 0 for at least 1 channel.", "violations": [ { "propertyPath": "actions[0].type", @@ -23,6 +23,26 @@ "propertyPath": "actions[3].configuration.amount", "message": "The percentage discount amount must be a number and can not be empty.", "code": null + }, + { + "propertyPath": "actions[4].configuration", + "message": "Provided configuration contains errors. Please add the fixed discount amount greater than 0 for at least 1 channel.", + "code": null + }, + { + "propertyPath": "actions[5].configuration", + "message": "Provided configuration contains errors. Please add the fixed discount amount greater than 0 for at least 1 channel.", + "code": null + }, + { + "propertyPath": "actions[6].configuration", + "message": "Provided configuration contains errors. At least one of the provided channel codes does not exist.", + "code": null + }, + { + "propertyPath": "actions[7].configuration", + "message": "Provided configuration contains errors. Please add the fixed discount amount greater than 0 for at least 1 channel.", + "code": null } ] } From 7bcb012761ad34075efd389639b1a48902432b4d Mon Sep 17 00:00:00 2001 From: Grzegorz Sadowski Date: Wed, 24 Nov 2021 07:30:02 +0100 Subject: [PATCH 05/11] [CatalogPromotion][API][UI] Apply new fixed discount action on catalog --- ...ilable_types_of_catalog_promotions.feature | 2 +- .../Api/Shop/ProductVariantContext.php | 17 ++ .../Context/Setup/CatalogPromotionContext.php | 148 ++++++++++++++++++ .../Behat/Context/Ui/Shop/ProductContext.php | 17 ++ .../Applicator/CatalogPromotionApplicator.php | 39 ++--- .../ActionBasedPriceCalculatorInterface.php | 24 +++ .../CatalogPromotionPriceCalculator.php | 40 +++++ ...talogPromotionPriceCalculatorInterface.php | 22 +++ .../FixedDiscountPriceCalculator.php | 40 +++++ .../PercentageDiscountPriceCalculator.php | 36 +++++ .../CoreBundle/Resources/config/services.xml | 2 + .../Resources/config/services/calculators.xml | 33 ++++ .../CatalogPromotionApplicatorSpec.php | 35 ++++- .../CatalogPromotionPriceCalculatorSpec.php | 65 ++++++++ .../FixedDiscountPriceCalculatorSpec.php | 51 ++++++ .../PercentageDiscountPriceCalculatorSpec.php | 50 ++++++ .../FixedDiscountActionValidatorSpec.php | 2 +- ...nBasedPriceCalculatorNotFoundException.php | 22 +++ 18 files changed, 613 insertions(+), 32 deletions(-) create mode 100644 src/Sylius/Bundle/CoreBundle/Calculator/ActionBasedPriceCalculatorInterface.php create mode 100644 src/Sylius/Bundle/CoreBundle/Calculator/CatalogPromotionPriceCalculator.php create mode 100644 src/Sylius/Bundle/CoreBundle/Calculator/CatalogPromotionPriceCalculatorInterface.php create mode 100644 src/Sylius/Bundle/CoreBundle/Calculator/FixedDiscountPriceCalculator.php create mode 100644 src/Sylius/Bundle/CoreBundle/Calculator/PercentageDiscountPriceCalculator.php create mode 100644 src/Sylius/Bundle/CoreBundle/Resources/config/services/calculators.xml create mode 100644 src/Sylius/Bundle/CoreBundle/spec/Calculator/CatalogPromotionPriceCalculatorSpec.php create mode 100644 src/Sylius/Bundle/CoreBundle/spec/Calculator/FixedDiscountPriceCalculatorSpec.php create mode 100644 src/Sylius/Bundle/CoreBundle/spec/Calculator/PercentageDiscountPriceCalculatorSpec.php create mode 100644 src/Sylius/Component/Core/Exception/ActionBasedPriceCalculatorNotFoundException.php diff --git a/features/promotion/applying_catalog_promotions/applying_all_available_types_of_catalog_promotions.feature b/features/promotion/applying_catalog_promotions/applying_all_available_types_of_catalog_promotions.feature index ea0b27f8bf4..c881c6d204a 100644 --- a/features/promotion/applying_catalog_promotions/applying_all_available_types_of_catalog_promotions.feature +++ b/features/promotion/applying_catalog_promotions/applying_all_available_types_of_catalog_promotions.feature @@ -9,7 +9,7 @@ Feature: Applying all available catalog promotions And the store classifies its products as "Clothes" And the store has a "T-Shirt" configurable product And this product belongs to "Clothes" - And this product has "PHP T-Shirt" variant priced at "$20.00" in "Web-US" channel + And this product has "PHP T-Shirt" variant priced at "$100.00" in "Web-US" channel And there is a catalog promotion "PHP sale" that reduces price by "10%" and applies on "PHP T-Shirt" variant And there is a catalog promotion "T-Shirt sale" that reduces price by "10%" and applies on "T-Shirt" product And there is a catalog promotion "Clothes sale" that reduces price by "10%" and applies on "Clothes" taxon diff --git a/src/Sylius/Behat/Context/Api/Shop/ProductVariantContext.php b/src/Sylius/Behat/Context/Api/Shop/ProductVariantContext.php index 9c33cad7972..356856544b1 100644 --- a/src/Sylius/Behat/Context/Api/Shop/ProductVariantContext.php +++ b/src/Sylius/Behat/Context/Api/Shop/ProductVariantContext.php @@ -93,6 +93,7 @@ public function theProductOriginalPriceShouldBe(int $originalPrice): void * @Then /^I should see (this variant) is discounted from ("[^"]+") to ("[^"]+") with "([^"]+)" promotion$/ * @Then /^I should see (this variant) is discounted from ("[^"]+") to ("[^"]+") with "([^"]+)" and "([^"]+)" promotions$/ * @Then /^I should see (this variant) is discounted from ("[^"]+") to ("[^"]+") with "([^"]+)", "([^"]+)" and "([^"]+)" promotions$/ + * @Then /^I should see (this variant) is discounted from ("[^"]+") to ("[^"]+") with "([^"]+)", "([^"]+)", "([^"]+)" and "([^"]+)" promotions$/ */ public function iShouldSeeVariantIsDiscountedFromToWithPromotions( ProductVariantInterface $variant, @@ -111,6 +112,22 @@ public function iShouldSeeVariantIsDiscountedFromToWithPromotions( } } + /** + * @Then /^I should see (this variant) is discounted from ("[^"]+") to ("[^"]+") with ([^"]+) promotions$/ + */ + public function iShouldSeeVariantIsDiscountedFromToWithNumberOfPromotions( + ProductVariantInterface $variant, + int $originalPrice, + int $price, + int $numberOfPromotions + ): void { + $content = $this->findVariant($variant); + + Assert::same($content['price'], $price); + Assert::same($content['originalPrice'], $originalPrice); + Assert::count($content['appliedPromotions'], $numberOfPromotions); + } + /** * @Then /^I should see (this variant) is discounted from ("[^"]+") to ("[^"]+") with only "([^"]+)" promotion$/ */ diff --git a/src/Sylius/Behat/Context/Setup/CatalogPromotionContext.php b/src/Sylius/Behat/Context/Setup/CatalogPromotionContext.php index 792be034ecb..b443082fc40 100644 --- a/src/Sylius/Behat/Context/Setup/CatalogPromotionContext.php +++ b/src/Sylius/Behat/Context/Setup/CatalogPromotionContext.php @@ -208,6 +208,90 @@ public function thereIsACatalogPromotionThatReducesPriceByAndAppliesOn( $this->entityManager->flush(); } + /** + * @Given /^there is a catalog promotion "([^"]*)" that reduces price by fixed ("[^"]+") and applies on ("[^"]+" variant)$/ + */ + public function thereIsACatalogPromotionThatReducesPriceByFixedAndAppliesOnVariant( + string $name, + int $discount, + ProductVariantInterface $variant + ): void { + /** @var ChannelInterface $channel */ + $channel = $this->sharedStorage->get('channel'); + + $this->createCatalogPromotion( + $name, + null, + [], + [[ + 'type' => CatalogPromotionScopeInterface::TYPE_FOR_VARIANTS, + 'configuration' => ['variants' => [$variant->getCode()]], + ]], + [[ + 'type' => CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT, + 'configuration' => [$channel->getCode() => ['amount' => $discount]], + ]] + ); + + $this->entityManager->flush(); + } + + /** + * @Given /^there is a catalog promotion "([^"]*)" that reduces price by fixed ("[^"]+") and applies on ("[^"]+" product)$/ + */ + public function thereIsACatalogPromotionThatReducesPriceByFixedAndAppliesOnProduct( + string $name, + int $discount, + ProductInterface $product + ): void { + /** @var ChannelInterface $channel */ + $channel = $this->sharedStorage->get('channel'); + + $this->createCatalogPromotion( + $name, + null, + [], + [[ + 'type' => CatalogPromotionScopeInterface::TYPE_FOR_PRODUCTS, + 'configuration' => ['products' => [$product->getCode()]], + ]], + [[ + 'type' => CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT, + 'configuration' => [$channel->getCode() => ['amount' => $discount]], + ]] + ); + + $this->entityManager->flush(); + } + + /** + * @Given /^there is a catalog promotion "([^"]*)" that reduces price by fixed ("[^"]+") and applies on ("[^"]+" taxon)$/ + */ + public function thereIsACatalogPromotionThatReducesPriceByFixedAndAppliesOnTaxon( + string $name, + int $discount, + TaxonInterface $taxon + ): void { + /** @var ChannelInterface $channel */ + $channel = $this->sharedStorage->get('channel'); + + $this->createCatalogPromotion( + $name, + null, + [], + [[ + 'type' => CatalogPromotionScopeInterface::TYPE_FOR_TAXONS, + 'configuration' => ['taxons' => [$taxon->getCode()]], + ]], + [[ + 'type' => CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT, + 'configuration' => [$channel->getCode() => ['amount' => $discount]], + ]] + ); + + $this->entityManager->flush(); + } + /** * @Given catalog promotion :catalogPromotion has failed processing */ @@ -413,6 +497,70 @@ public function thereIsAnExclusiveCatalogPromotionWithPriorityThatReducesPriceBy $this->eventBus->dispatch(new CatalogPromotionUpdated($catalogPromotion->getCode())); } + /** + * @Given /^there is a catalog promotion "([^"]+)" with priority ([^"]+) that reduces price by fixed ("[^"]+") and applies on ("[^"]+" product)$/ + */ + public function thereIsACatalogPromotionWithPriorityThatReducesPriceByFixedAndAppliesOnProduct( + string $name, + int $priority, + int $discount, + ProductInterface $product + ): void { + /** @var ChannelInterface $channel */ + $channel = $this->sharedStorage->get('channel'); + + $catalogPromotion = $this->createCatalogPromotion( + $name, + null, + [], + [[ + 'type' => CatalogPromotionScopeInterface::TYPE_FOR_PRODUCTS, + 'configuration' => ['products' => [$product->getCode()]], + ]], + [[ + 'type' => CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT, + 'configuration' => [$channel->getCode() => ['amount' => $discount]], + ]], + $priority + ); + + $this->entityManager->flush(); + + $this->eventBus->dispatch(new CatalogPromotionUpdated($catalogPromotion->getCode())); + } + + /** + * @Given /^there is a catalog promotion "([^"]+)" with priority ([^"]+) that reduces price by fixed ("[^"]+") and applies on ("[^"]+" taxon)$/ + */ + public function thereIsACatalogPromotionWithPriorityThatReducesPriceByFixedAndAppliesOnTaxon( + string $name, + int $priority, + int $discount, + TaxonInterface $taxon + ): void { + /** @var ChannelInterface $channel */ + $channel = $this->sharedStorage->get('channel'); + + $catalogPromotion = $this->createCatalogPromotion( + $name, + null, + [], + [[ + 'type' => CatalogPromotionScopeInterface::TYPE_FOR_TAXONS, + 'configuration' => ['taxons' => [$taxon->getCode()]], + ]], + [[ + 'type' => CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT, + 'configuration' => ['amount' => $discount], + ]], + $priority, + ); + + $this->entityManager->flush(); + + $this->eventBus->dispatch(new CatalogPromotionUpdated($catalogPromotion->getCode())); + } + /** * @When the :catalogPromotion catalog promotion is no longer available */ diff --git a/src/Sylius/Behat/Context/Ui/Shop/ProductContext.php b/src/Sylius/Behat/Context/Ui/Shop/ProductContext.php index 6de49097631..d26888f877c 100644 --- a/src/Sylius/Behat/Context/Ui/Shop/ProductContext.php +++ b/src/Sylius/Behat/Context/Ui/Shop/ProductContext.php @@ -499,6 +499,7 @@ public function iViewVariantOfProduct(string $variantName, ProductInterface $pro * @Then /^I should see (this variant) is discounted from "([^"]+)" to "([^"]+)" with "([^"]+)" promotion$/ * @Then /^I should see (this variant) is discounted from "([^"]+)" to "([^"]+)" with "([^"]+)" and "([^"]+)" promotions$/ * @Then /^I should see (this variant) is discounted from "([^"]+)" to "([^"]+)" with "([^"]+)", "([^"]+)" and "([^"]+)" promotions$/ + * @Then /^I should see (this variant) is discounted from "([^"]+)" to "([^"]+)" with "([^"]+)", "([^"]+)", "([^"]+)" and "([^"]+)" promotions$/ */ public function iShouldSeeVariantIsDiscountedFromToWithPromotions( ProductVariantInterface $variant, @@ -515,6 +516,22 @@ public function iShouldSeeVariantIsDiscountedFromToWithPromotions( } } + /** + * @Then /^I should see (this variant) is discounted from "([^"]+)" to "([^"]+)" with ([^"]+) promotions$/ + */ + public function iShouldSeeVariantIsDiscountedFromToWithNumberOfPromotions( + ProductVariantInterface $variant, + string $originalPrice, + string $price, + int $numberOfPromotions + ): void { + $this->showPage->selectVariant($variant->getName()); + + Assert::same($this->showPage->getPrice(), $price); + Assert::same($this->showPage->getOriginalPrice(), $originalPrice); + Assert::count($this->showPage->getCatalogPromotionNames(), $numberOfPromotions); + } + /** * @Then /^I should see (this variant) is discounted from "([^"]+)" to "([^"]+)" with only "([^"]+)" promotion$/ */ diff --git a/src/Sylius/Bundle/CoreBundle/Applicator/CatalogPromotionApplicator.php b/src/Sylius/Bundle/CoreBundle/Applicator/CatalogPromotionApplicator.php index 7ef7782f6d1..adeac03802a 100644 --- a/src/Sylius/Bundle/CoreBundle/Applicator/CatalogPromotionApplicator.php +++ b/src/Sylius/Bundle/CoreBundle/Applicator/CatalogPromotionApplicator.php @@ -13,7 +13,9 @@ namespace Sylius\Bundle\CoreBundle\Applicator; +use Sylius\Bundle\CoreBundle\Calculator\CatalogPromotionPriceCalculatorInterface; use Sylius\Bundle\CoreBundle\Formatter\AppliedPromotionInformationFormatterInterface; +use Sylius\Component\Core\Exception\ActionBasedPriceCalculatorNotFoundException; use Sylius\Component\Core\Model\CatalogPromotionInterface; use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\ChannelPricingInterface; @@ -22,10 +24,15 @@ final class CatalogPromotionApplicator implements CatalogPromotionApplicatorInterface { + private CatalogPromotionPriceCalculatorInterface $priceCalculator; + private AppliedPromotionInformationFormatterInterface $appliedPromotionInformationFormatter; - public function __construct(AppliedPromotionInformationFormatterInterface $appliedPromotionInformationFormatter) - { + public function __construct( + CatalogPromotionPriceCalculatorInterface $priceCalculator, + AppliedPromotionInformationFormatterInterface $appliedPromotionInformationFormatter + ) { + $this->priceCalculator = $priceCalculator; $this->appliedPromotionInformationFormatter = $appliedPromotionInformationFormatter; } @@ -34,10 +41,6 @@ public function applyOnVariant( CatalogPromotionInterface $catalogPromotion ): void { foreach ($catalogPromotion->getActions() as $action) { - if ($action->getType() === CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT) { - continue; - } - $this->applyDiscountFromAction($catalogPromotion, $action, $variant); } } @@ -51,15 +54,7 @@ public function applyOnChannelPricing( } foreach ($catalogPromotion->getActions() as $action) { - if ($action->getType() === CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT) { - continue; - } - - $this->applyDiscountFromActionOnChannelPricing( - $catalogPromotion, - $action->getConfiguration()['amount'], - $channelPricing - ); + $this->applyDiscountFromActionOnChannelPricing($catalogPromotion, $action, $channelPricing); } } @@ -68,21 +63,19 @@ private function applyDiscountFromAction( CatalogPromotionActionInterface $action, ProductVariantInterface $variant ): void { - $discount = $action->getConfiguration()['amount']; - foreach ($catalogPromotion->getChannels() as $channel) { $channelPricing = $variant->getChannelPricingForChannel($channel); if ($channelPricing === null) { continue; } - $this->applyDiscountFromActionOnChannelPricing($catalogPromotion, $discount, $channelPricing); + $this->applyDiscountFromActionOnChannelPricing($catalogPromotion, $action, $channelPricing); } } private function applyDiscountFromActionOnChannelPricing( CatalogPromotionInterface $catalogPromotion, - float $discount, + CatalogPromotionActionInterface $action, ChannelPricingInterface $channelPricing ): void { if ($channelPricing->hasExclusiveCatalogPromotionApplied()) { @@ -97,10 +90,10 @@ private function applyDiscountFromActionOnChannelPricing( return; } - $price = (int) ($channelPricing->getPrice() - ($channelPricing->getPrice() * $discount)); - - if ($price < $channelPricing->getMinimumPrice()) { - $price = $channelPricing->getMinimumPrice(); + try { + $price = $this->priceCalculator->calculate($channelPricing, $action); + } catch (ActionBasedPriceCalculatorNotFoundException $exception) { + return; } $channelPricing->setPrice($price); diff --git a/src/Sylius/Bundle/CoreBundle/Calculator/ActionBasedPriceCalculatorInterface.php b/src/Sylius/Bundle/CoreBundle/Calculator/ActionBasedPriceCalculatorInterface.php new file mode 100644 index 00000000000..9a85fe7111d --- /dev/null +++ b/src/Sylius/Bundle/CoreBundle/Calculator/ActionBasedPriceCalculatorInterface.php @@ -0,0 +1,24 @@ +priceCalculators = $priceCalculators; + } + + public function calculate(ChannelPricingInterface $channelPricing, CatalogPromotionActionInterface $action): int + { + /** @var ActionBasedPriceCalculatorInterface $calculator */ + foreach ($this->priceCalculators as $calculator) { + if ($calculator->supports($action)) { + return $calculator->calculate($channelPricing, $action); + } + } + + throw new ActionBasedPriceCalculatorNotFoundException(); + } +} diff --git a/src/Sylius/Bundle/CoreBundle/Calculator/CatalogPromotionPriceCalculatorInterface.php b/src/Sylius/Bundle/CoreBundle/Calculator/CatalogPromotionPriceCalculatorInterface.php new file mode 100644 index 00000000000..2a9af8714c5 --- /dev/null +++ b/src/Sylius/Bundle/CoreBundle/Calculator/CatalogPromotionPriceCalculatorInterface.php @@ -0,0 +1,22 @@ +getType() === CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT; + } + + public function calculate(ChannelPricingInterface $channelPricing, CatalogPromotionActionInterface $action): int + { + if (!isset($action->getConfiguration()[$channelPricing->getChannelCode()])) { + return $channelPricing->getPrice(); + } + + $price = $channelPricing->getPrice() - $action->getConfiguration()[$channelPricing->getChannelCode()]['amount']; + + if ($price < $channelPricing->getMinimumPrice()) { + return $channelPricing->getMinimumPrice(); + } + + return $price; + } +} diff --git a/src/Sylius/Bundle/CoreBundle/Calculator/PercentageDiscountPriceCalculator.php b/src/Sylius/Bundle/CoreBundle/Calculator/PercentageDiscountPriceCalculator.php new file mode 100644 index 00000000000..38e92af5247 --- /dev/null +++ b/src/Sylius/Bundle/CoreBundle/Calculator/PercentageDiscountPriceCalculator.php @@ -0,0 +1,36 @@ +getType() === CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT; + } + + public function calculate(ChannelPricingInterface $channelPricing, CatalogPromotionActionInterface $action): int + { + $price = (int) ($channelPricing->getPrice() - ($channelPricing->getPrice() * $action->getConfiguration()['amount'])); + + if ($price < $channelPricing->getMinimumPrice()) { + return $channelPricing->getMinimumPrice(); + } + + return $price; + } +} diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/services.xml b/src/Sylius/Bundle/CoreBundle/Resources/config/services.xml index 140d288b3de..785b9f5d316 100644 --- a/src/Sylius/Bundle/CoreBundle/Resources/config/services.xml +++ b/src/Sylius/Bundle/CoreBundle/Resources/config/services.xml @@ -13,6 +13,7 @@ + @@ -259,6 +260,7 @@ id="Sylius\Bundle\CoreBundle\Applicator\CatalogPromotionApplicatorInterface" class="Sylius\Bundle\CoreBundle\Applicator\CatalogPromotionApplicator" > + diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/services/calculators.xml b/src/Sylius/Bundle/CoreBundle/Resources/config/services/calculators.xml new file mode 100644 index 00000000000..a947465c16d --- /dev/null +++ b/src/Sylius/Bundle/CoreBundle/Resources/config/services/calculators.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Sylius/Bundle/CoreBundle/spec/Applicator/CatalogPromotionApplicatorSpec.php b/src/Sylius/Bundle/CoreBundle/spec/Applicator/CatalogPromotionApplicatorSpec.php index 1ec0f6dc900..559bb06e73c 100644 --- a/src/Sylius/Bundle/CoreBundle/spec/Applicator/CatalogPromotionApplicatorSpec.php +++ b/src/Sylius/Bundle/CoreBundle/spec/Applicator/CatalogPromotionApplicatorSpec.php @@ -17,6 +17,7 @@ use PhpSpec\ObjectBehavior; use Prophecy\Argument; use Sylius\Bundle\CoreBundle\Applicator\CatalogPromotionApplicatorInterface; +use Sylius\Bundle\CoreBundle\Calculator\CatalogPromotionPriceCalculatorInterface; use Sylius\Bundle\CoreBundle\Formatter\AppliedPromotionInformationFormatterInterface; use Sylius\Component\Core\Model\CatalogPromotionInterface; use Sylius\Component\Core\Model\ChannelInterface; @@ -26,9 +27,11 @@ final class CatalogPromotionApplicatorSpec extends ObjectBehavior { - function let(AppliedPromotionInformationFormatterInterface $appliedPromotionInformationFormatter): void - { - $this->beConstructedWith($appliedPromotionInformationFormatter); + function let( + CatalogPromotionPriceCalculatorInterface $priceCalculator, + AppliedPromotionInformationFormatterInterface $appliedPromotionInformationFormatter + ): void { + $this->beConstructedWith($priceCalculator, $appliedPromotionInformationFormatter); } function it_implements_catalog_promotion_applicator_interface(): void @@ -37,6 +40,7 @@ function it_implements_catalog_promotion_applicator_interface(): void } function it_applies_percentage_discount_on_product_variant( + CatalogPromotionPriceCalculatorInterface $priceCalculator, AppliedPromotionInformationFormatterInterface $appliedPromotionInformationFormatter, ProductVariantInterface $variant, CatalogPromotionInterface $catalogPromotion, @@ -56,13 +60,15 @@ function it_applies_percentage_discount_on_product_variant( $variant->getChannelPricingForChannel($secondChannel)->willReturn($secondChannelPricing); $appliedPromotionInformationFormatter->format($catalogPromotion)->willReturn(['winter_sale' => ['name' => 'Winter sale']]); - $catalogPromotionAction->getConfiguration()->willReturn(['amount' => 0.3]); $firstChannelPricing->hasExclusiveCatalogPromotionApplied()->willReturn(false); $firstChannelPricing->getPrice()->willReturn(1000); $firstChannelPricing->getOriginalPrice()->willReturn(null); $firstChannelPricing->getMinimumPrice()->willReturn(0); $firstChannelPricing->setOriginalPrice(1000)->shouldBeCalled(); + + $priceCalculator->calculate($firstChannelPricing, $catalogPromotionAction)->willReturn(700); + $firstChannelPricing->setPrice(700)->shouldBeCalled(); $firstChannelPricing->addAppliedPromotion(['winter_sale' => ['name' => 'Winter sale']])->shouldBeCalled(); $catalogPromotion->isExclusive()->willReturn(false); @@ -72,6 +78,9 @@ function it_applies_percentage_discount_on_product_variant( $secondChannelPricing->getOriginalPrice()->willReturn(null); $secondChannelPricing->getMinimumPrice()->willReturn(0); $secondChannelPricing->setOriginalPrice(1400)->shouldBeCalled(); + + $priceCalculator->calculate($secondChannelPricing, $catalogPromotionAction)->willReturn(980); + $secondChannelPricing->setPrice(980)->shouldBeCalled(); $secondChannelPricing->addAppliedPromotion(['winter_sale' => ['name' => 'Winter sale']])->shouldBeCalled(); $catalogPromotion->isExclusive()->willReturn(false); @@ -118,6 +127,7 @@ function it_applies_discount_on_product_variant_only_if_exclusive_promotion_is_n } function it_does_not_set_original_price_during_application_if_its_already_there( + CatalogPromotionPriceCalculatorInterface $priceCalculator, AppliedPromotionInformationFormatterInterface $appliedPromotionInformationFormatter, ProductVariantInterface $variant, CatalogPromotionInterface $catalogPromotion, @@ -131,12 +141,14 @@ function it_does_not_set_original_price_during_application_if_its_already_there( $variant->getChannelPricingForChannel($channel)->willReturn($channelPricing); $appliedPromotionInformationFormatter->format($catalogPromotion)->willReturn(['winter_sale' => ['name' => 'Winter sale']]); - $catalogPromotionAction->getConfiguration()->willReturn(['amount' => 0.5]); $channelPricing->hasExclusiveCatalogPromotionApplied()->willReturn(false); $channelPricing->getPrice()->willReturn(1000); $channelPricing->getOriginalPrice()->willReturn(2000); $channelPricing->getMinimumPrice()->willReturn(0); + + $priceCalculator->calculate($channelPricing, $catalogPromotionAction)->willReturn(500); + $channelPricing->setOriginalPrice(Argument::any())->shouldNotBeCalled(); $channelPricing->setPrice(500)->shouldBeCalled(); $channelPricing->addAppliedPromotion(['winter_sale' => ['name' => 'Winter sale']])->shouldBeCalled(); @@ -146,6 +158,7 @@ function it_does_not_set_original_price_during_application_if_its_already_there( } function it_applies_percentage_discount_on_channel_pricing( + CatalogPromotionPriceCalculatorInterface $priceCalculator, AppliedPromotionInformationFormatterInterface $appliedPromotionInformationFormatter, CatalogPromotionInterface $catalogPromotion, CatalogPromotionActionInterface $catalogPromotionAction, @@ -158,7 +171,6 @@ function it_applies_percentage_discount_on_channel_pricing( $catalogPromotion->getChannels()->willReturn(new ArrayCollection([$channel->getWrappedObject()])); $appliedPromotionInformationFormatter->format($catalogPromotion)->willReturn(['winter_sale' => ['name' => 'Winter sale']]); - $catalogPromotionAction->getConfiguration()->willReturn(['amount' => 0.3]); $channelPricing->hasExclusiveCatalogPromotionApplied()->willReturn(false); $channelPricing->getPrice()->willReturn(1000); @@ -166,6 +178,8 @@ function it_applies_percentage_discount_on_channel_pricing( $channelPricing->getOriginalPrice()->willReturn(null); $channelPricing->getChannelCode()->willReturn('WEB'); + $priceCalculator->calculate($channelPricing, $catalogPromotionAction)->willReturn(700); + $channelPricing->setOriginalPrice(1000)->shouldBeCalled(); $channelPricing->setPrice(700)->shouldBeCalled(); $channelPricing->addAppliedPromotion(['winter_sale' => ['name' => 'Winter sale']])->shouldBeCalled(); @@ -175,6 +189,7 @@ function it_applies_percentage_discount_on_channel_pricing( } function it_does_not_apply_percentage_discount_on_channel_pricing_if_catalog_promotion_does_not_have_the_proper_channel( + CatalogPromotionPriceCalculatorInterface $priceCalculator, AppliedPromotionInformationFormatterInterface $appliedPromotionInformationFormatter, CatalogPromotionInterface $catalogPromotion, CatalogPromotionActionInterface $catalogPromotionAction, @@ -188,6 +203,7 @@ function it_does_not_apply_percentage_discount_on_channel_pricing_if_catalog_pro $channelPricing->getChannelCode()->willReturn('MOBILE'); + $priceCalculator->calculate($channelPricing, $catalogPromotionAction)->shouldNotBeCalled(); $appliedPromotionInformationFormatter->format($catalogPromotion)->shouldNotBeCalled(); $channelPricing->setOriginalPrice(1000)->shouldNotBeCalled(); @@ -198,6 +214,7 @@ function it_does_not_apply_percentage_discount_on_channel_pricing_if_catalog_pro } function it_does_not_apply_catalog_promotion_below_minimum_price( + CatalogPromotionPriceCalculatorInterface $priceCalculator, AppliedPromotionInformationFormatterInterface $appliedPromotionInformationFormatter, CatalogPromotionInterface $catalogPromotion, CatalogPromotionActionInterface $catalogPromotionAction, @@ -210,7 +227,6 @@ function it_does_not_apply_catalog_promotion_below_minimum_price( $catalogPromotion->getChannels()->willReturn(new ArrayCollection([$channel->getWrappedObject()])); $appliedPromotionInformationFormatter->format($catalogPromotion)->willReturn(['winter_sale' => ['name' => 'Winter sale']]); - $catalogPromotionAction->getConfiguration()->willReturn(['amount' => 0.3]); $channelPricing->hasExclusiveCatalogPromotionApplied()->willReturn(false); $channelPricing->getPrice()->willReturn(1000); @@ -218,6 +234,8 @@ function it_does_not_apply_catalog_promotion_below_minimum_price( $channelPricing->getOriginalPrice()->willReturn(null); $channelPricing->getChannelCode()->willReturn('WEB'); + $priceCalculator->calculate($channelPricing, $catalogPromotionAction)->willReturn(900); + $channelPricing->setOriginalPrice(1000)->shouldBeCalled(); $channelPricing->setPrice(900)->shouldBeCalled(); $channelPricing->addAppliedPromotion(['winter_sale' => ['name' => 'Winter sale']])->shouldBeCalled(); @@ -227,6 +245,7 @@ function it_does_not_apply_catalog_promotion_below_minimum_price( } function it_does_not_apply_catalog_promotion_if_product_variant_is_at_its_minimum_price( + CatalogPromotionPriceCalculatorInterface $priceCalculator, AppliedPromotionInformationFormatterInterface $appliedPromotionInformationFormatter, CatalogPromotionInterface $catalogPromotion, CatalogPromotionActionInterface $catalogPromotionAction, @@ -247,6 +266,8 @@ function it_does_not_apply_catalog_promotion_if_product_variant_is_at_its_minimu $channelPricing->getOriginalPrice()->willReturn(900); $channelPricing->getChannelCode()->willReturn('WEB'); + $priceCalculator->calculate($channelPricing, $catalogPromotionAction)->shouldNotBeCalled(); + $channelPricing->setOriginalPrice(Argument::any())->shouldNotBeCalled(); $channelPricing->setPrice(Argument::any())->shouldNotBeCalled(); $channelPricing->addAppliedPromotion(Argument::any())->shouldNotBeCalled(); diff --git a/src/Sylius/Bundle/CoreBundle/spec/Calculator/CatalogPromotionPriceCalculatorSpec.php b/src/Sylius/Bundle/CoreBundle/spec/Calculator/CatalogPromotionPriceCalculatorSpec.php new file mode 100644 index 00000000000..66b49777943 --- /dev/null +++ b/src/Sylius/Bundle/CoreBundle/spec/Calculator/CatalogPromotionPriceCalculatorSpec.php @@ -0,0 +1,65 @@ +beConstructedWith([$fixedDiscountCalculator, $percentageDiscountCalculator]); + } + + function it_implements_catalog_promotion_price_calculator_interface(): void + { + $this->shouldImplement(CatalogPromotionPriceCalculatorInterface::class); + } + + function it_calculates_price_of_channel_pricing_for_given_action_by_a_proper_calculator( + ActionBasedPriceCalculatorInterface $fixedDiscountCalculator, + ActionBasedPriceCalculatorInterface $percentageDiscountCalculator, + CatalogPromotionActionInterface $action, + ChannelPricingInterface $channelPricing + ): void { + $fixedDiscountCalculator->supports($action)->willReturn(false); + $percentageDiscountCalculator->supports($action)->willReturn(true); + + $percentageDiscountCalculator->calculate($channelPricing, $action)->willReturn(1000); + + $this->calculate($channelPricing, $action)->shouldReturn(1000); + } + + function it_throws_an_exception_if_there_is_no_calculator_that_supports_given_action( + ActionBasedPriceCalculatorInterface $fixedDiscountCalculator, + ActionBasedPriceCalculatorInterface $percentageDiscountCalculator, + CatalogPromotionActionInterface $action, + ChannelPricingInterface $channelPricing + ): void { + $fixedDiscountCalculator->supports($action)->willReturn(false); + $percentageDiscountCalculator->supports($action)->willReturn(false); + + $this + ->shouldThrow(ActionBasedPriceCalculatorNotFoundException::class) + ->during('calculate', [$channelPricing, $action]) + ; + } +} diff --git a/src/Sylius/Bundle/CoreBundle/spec/Calculator/FixedDiscountPriceCalculatorSpec.php b/src/Sylius/Bundle/CoreBundle/spec/Calculator/FixedDiscountPriceCalculatorSpec.php new file mode 100644 index 00000000000..0a47391d1a3 --- /dev/null +++ b/src/Sylius/Bundle/CoreBundle/spec/Calculator/FixedDiscountPriceCalculatorSpec.php @@ -0,0 +1,51 @@ +shouldImplement(ActionBasedPriceCalculatorInterface::class); + } + + function it_supports_only_fixed_discount_catalog_promotion_action( + CatalogPromotionActionInterface $fixedDiscountAction, + CatalogPromotionActionInterface $percentageDiscountAction + ): void { + $fixedDiscountAction->getType()->willReturn(CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT); + $percentageDiscountAction->getType()->willReturn(CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT); + + $this->supports($fixedDiscountAction)->shouldReturn(true); + $this->supports($percentageDiscountAction)->shouldReturn(false); + } + + function it_calculates_price_for_given_channel_pricing_and_action( + ChannelPricingInterface $channelPricing, + CatalogPromotionActionInterface $action + ): void { + $action->getConfiguration()->willReturn(['WEB' => ['amount' => 200]]); + + $channelPricing->getChannelCode()->willReturn('WEB'); + $channelPricing->getPrice()->willReturn(1000); + $channelPricing->getMinimumPrice()->willReturn(0); + + $this->calculate($channelPricing, $action)->shouldReturn(800); + } +} diff --git a/src/Sylius/Bundle/CoreBundle/spec/Calculator/PercentageDiscountPriceCalculatorSpec.php b/src/Sylius/Bundle/CoreBundle/spec/Calculator/PercentageDiscountPriceCalculatorSpec.php new file mode 100644 index 00000000000..01d14ffd865 --- /dev/null +++ b/src/Sylius/Bundle/CoreBundle/spec/Calculator/PercentageDiscountPriceCalculatorSpec.php @@ -0,0 +1,50 @@ +shouldImplement(ActionBasedPriceCalculatorInterface::class); + } + + function it_supports_only_percentage_discount_catalog_promotion_action( + CatalogPromotionActionInterface $fixedDiscountAction, + CatalogPromotionActionInterface $percentageDiscountAction + ): void { + $fixedDiscountAction->getType()->willReturn(CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT); + $percentageDiscountAction->getType()->willReturn(CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT); + + $this->supports($fixedDiscountAction)->shouldReturn(false); + $this->supports($percentageDiscountAction)->shouldReturn(true); + } + + function it_calculates_price_for_given_channel_pricing_and_action( + ChannelPricingInterface $channelPricing, + CatalogPromotionActionInterface $action + ): void { + $action->getConfiguration()->willReturn(['amount' => 0.3]); + + $channelPricing->getPrice()->willReturn(1000); + $channelPricing->getMinimumPrice()->willReturn(0); + + $this->calculate($channelPricing, $action)->shouldReturn(700); + } +} diff --git a/src/Sylius/Bundle/CoreBundle/spec/Validator/CatalogPromotionAction/FixedDiscountActionValidatorSpec.php b/src/Sylius/Bundle/CoreBundle/spec/Validator/CatalogPromotionAction/FixedDiscountActionValidatorSpec.php index 5342f4cd03e..13fcb87428a 100644 --- a/src/Sylius/Bundle/CoreBundle/spec/Validator/CatalogPromotionAction/FixedDiscountActionValidatorSpec.php +++ b/src/Sylius/Bundle/CoreBundle/spec/Validator/CatalogPromotionAction/FixedDiscountActionValidatorSpec.php @@ -37,7 +37,7 @@ function it_adds_violation_if_catalog_promotion_action_has_an_empty_configuratio ExecutionContextInterface $executionContext, ConstraintViolationBuilderInterface $constraintViolationBuilder ): void { - $executionContext->buildViolation('sylius.catalog_promotion_action.fixed_discount.not_empty')->willReturn($constraintViolationBuilder); + $executionContext->buildViolation('sylius.catalog_promotion_action.fixed_discount.not_valid')->willReturn($constraintViolationBuilder); $constraintViolationBuilder->atPath('configuration')->willReturn($constraintViolationBuilder); $constraintViolationBuilder->addViolation()->shouldBeCalled(); diff --git a/src/Sylius/Component/Core/Exception/ActionBasedPriceCalculatorNotFoundException.php b/src/Sylius/Component/Core/Exception/ActionBasedPriceCalculatorNotFoundException.php new file mode 100644 index 00000000000..85a066816bf --- /dev/null +++ b/src/Sylius/Component/Core/Exception/ActionBasedPriceCalculatorNotFoundException.php @@ -0,0 +1,22 @@ + Date: Wed, 24 Nov 2021 07:32:50 +0100 Subject: [PATCH 06/11] [CatalogPromotion][Documentation] Add new fixed discount action to actions reference --- docs/book/products/catalog_promotions.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/book/products/catalog_promotions.rst b/docs/book/products/catalog_promotions.rst index d907c7336b2..54fd451ca1a 100644 --- a/docs/book/products/catalog_promotions.rst +++ b/docs/book/products/catalog_promotions.rst @@ -292,6 +292,8 @@ Catalog Promotion Actions configuration reference +-------------------------------+--------------------------------------------------------------------+ | Action type | Action Configuration Array | +===============================+====================================================================+ +| ``fixed_discount`` | ``[$channelCode => ['amount' => $amountInteger]]`` | ++-------------------------------+--------------------------------------------------------------------+ | ``percentage_discount`` | ``['amount' => $amountFloat]`` | +-------------------------------+--------------------------------------------------------------------+ From ca72095d7e5eb5cc1b85429060a9969bc734c838 Mon Sep 17 00:00:00 2001 From: Grzegorz Sadowski Date: Wed, 24 Nov 2021 07:53:29 +0100 Subject: [PATCH 07/11] [CatalogPromotion] Add missing license docblocks --- .../Extension/CatalogPromotionActionTypeExtension.php | 11 ++++++++++- .../Extension/CatalogPromotionScopeTypeExtension.php | 9 +++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Sylius/Bundle/AdminBundle/Form/Extension/CatalogPromotionActionTypeExtension.php b/src/Sylius/Bundle/AdminBundle/Form/Extension/CatalogPromotionActionTypeExtension.php index 909081f48b9..c5a72407abc 100644 --- a/src/Sylius/Bundle/AdminBundle/Form/Extension/CatalogPromotionActionTypeExtension.php +++ b/src/Sylius/Bundle/AdminBundle/Form/Extension/CatalogPromotionActionTypeExtension.php @@ -1,5 +1,14 @@ $this->twig->render( '@SyliusAdmin/CatalogPromotion/_action.html.twig', ['field' => $builder->create( - 'configuration', + 'configuration', $this->actionConfigurationTypes[$type], ['label' => false, 'csrf_protection' => false] )->getForm()->createView()] diff --git a/src/Sylius/Bundle/AdminBundle/Form/Extension/CatalogPromotionScopeTypeExtension.php b/src/Sylius/Bundle/AdminBundle/Form/Extension/CatalogPromotionScopeTypeExtension.php index 4a2bd7c4620..d13e5169b51 100644 --- a/src/Sylius/Bundle/AdminBundle/Form/Extension/CatalogPromotionScopeTypeExtension.php +++ b/src/Sylius/Bundle/AdminBundle/Form/Extension/CatalogPromotionScopeTypeExtension.php @@ -1,5 +1,14 @@ Date: Wed, 24 Nov 2021 14:19:32 +0100 Subject: [PATCH 08/11] [CatalogPromotion] Fix editing catalog promotion by changing its action --- .../Api/Admin/ManagingCatalogPromotionsContext.php | 13 +++++++------ .../Ui/Admin/ManagingCatalogPromotionsContext.php | 1 + .../Form/Type/CatalogPromotionActionType.php | 14 ++++++++++---- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php b/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php index 61d284caedf..070e4f08876 100644 --- a/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php +++ b/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php @@ -561,14 +561,14 @@ public function iEditCatalogPromotionToHaveFixedDiscountInTheChannel( ChannelInterface $channel ): void { $this->client->buildUpdateRequest($catalogPromotion->getCode()); - $scopes = [[ + $content = $this->client->getContent(); + + $content['actions'] = [[ 'type' => CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT, - 'configuration' => [ - 'amount' => $amount, - ], + 'configuration' => [$channel->getCode() => ['amount' => $amount]], ]]; - $this->client->updateRequestData(['actions' => $scopes]); + $this->client->setRequestData($content); $this->client->update(); } @@ -801,7 +801,7 @@ public function thisCatalogPromotionShouldHaveFixedDiscountInTheChannel(int $amo $catalogPromotionActions = $this->responseChecker->getValue($this->client->getLastResponse(), 'actions'); Assert::same($catalogPromotionActions[0]['type'], CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT); - Assert::same($catalogPromotionActions[0]['configuration']['amount'], $amount); + Assert::same($catalogPromotionActions[0]['configuration'][$channel->getCode()]['amount'], $amount); } /** @@ -1036,6 +1036,7 @@ public function iShouldBeNotifiedThatItHasBeenSuccessfullyCreated(): void */ public function iShouldBeNotifiedThatItHasBeenSuccessfullyEdited(): void { + $res = $this->client->getLastResponse(); Assert::true( $this->responseChecker->isUpdateSuccessful($this->client->getLastResponse()), 'Catalog promotion could not be edited' diff --git a/src/Sylius/Behat/Context/Ui/Admin/ManagingCatalogPromotionsContext.php b/src/Sylius/Behat/Context/Ui/Admin/ManagingCatalogPromotionsContext.php index 494436993b3..47a18fbcf01 100644 --- a/src/Sylius/Behat/Context/Ui/Admin/ManagingCatalogPromotionsContext.php +++ b/src/Sylius/Behat/Context/Ui/Admin/ManagingCatalogPromotionsContext.php @@ -14,6 +14,7 @@ namespace Sylius\Behat\Context\Ui\Admin; use Behat\Behat\Context\Context; +use http\Exception\InvalidArgumentException; use Sylius\Behat\Element\Admin\CatalogPromotion\FormElementInterface; use Sylius\Behat\NotificationType; use Sylius\Behat\Page\Admin\CatalogPromotion\CreatePageInterface; diff --git a/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionActionType.php b/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionActionType.php index 81f0a8d553e..3503dc5adc5 100644 --- a/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionActionType.php +++ b/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionActionType.php @@ -50,12 +50,12 @@ public function buildForm(FormBuilderInterface $builder, array $options) ->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event): void { /** @var CatalogPromotionActionInterface|null $data */ $data = $event->getData(); - $form = $event->getForm(); - if ($data === null) { return; } + $form = $event->getForm(); + $actionConfigurationType = $this->actionConfigurationTypes[$data->getType()]; $form->add('configuration', $actionConfigurationType, [ 'label' => false, @@ -64,12 +64,18 @@ public function buildForm(FormBuilderInterface $builder, array $options) ->addEventListener(FormEvents::PRE_SUBMIT, function(FormEvent $event): void { /** @var array|null $data */ $data = $event->getData(); - $form = $event->getForm(); - if ($data === null) { return; } + $form = $event->getForm(); + $formData = $form->getData(); + if ($formData !== null) { + $formData->setType($data['type']); + $formData->setConfiguration($data['configuration']); + $form->setData($formData); + } + $actionConfigurationType = $this->actionConfigurationTypes[$data['type']]; $form->add('configuration', $actionConfigurationType, [ 'label' => false, From e9a57c7aeceebcecded441b87de7cb80268edab9 Mon Sep 17 00:00:00 2001 From: Grzegorz Sadowski Date: Thu, 25 Nov 2021 07:57:54 +0100 Subject: [PATCH 09/11] [CatalogPromotion] Minor refactor of fixed discount action --- ...ilable_types_of_catalog_promotions.feature | 8 ++-- ...g_promotion_with_proper_priorities.feature | 4 +- ...motions_with_fixed_discount_action.feature | 6 +-- ..._taking_minimum_price_into_account.feature | 4 +- .../creating_catalog_promotion.feature | 4 +- .../editing_catalog_promotion.feature | 4 +- .../ManagingCatalogPromotionsContext.php | 9 ++--- .../Context/Setup/CatalogPromotionContext.php | 40 +++++++------------ .../Behat/Context/Setup/ProductContext.php | 2 +- .../ManagingCatalogPromotionsContext.php | 21 ++++++---- .../Admin/CatalogPromotion/FormElement.php | 6 +-- .../CatalogPromotionActionTypeExtension.php | 1 - .../FixedDiscountPriceCalculator.php | 14 ++++++- .../PercentageDiscountPriceCalculator.php | 16 ++++++-- .../FixedDiscountPriceCalculatorSpec.php | 26 ++++++++++++ .../PercentageDiscountPriceCalculatorSpec.php | 12 ++++++ .../FixedDiscountActionConfigurationType.php | 4 -- .../Form/Type/CatalogPromotionActionType.php | 1 - 18 files changed, 115 insertions(+), 67 deletions(-) diff --git a/features/promotion/applying_catalog_promotions/applying_all_available_types_of_catalog_promotions.feature b/features/promotion/applying_catalog_promotions/applying_all_available_types_of_catalog_promotions.feature index c881c6d204a..657736223a6 100644 --- a/features/promotion/applying_catalog_promotions/applying_all_available_types_of_catalog_promotions.feature +++ b/features/promotion/applying_catalog_promotions/applying_all_available_types_of_catalog_promotions.feature @@ -1,5 +1,5 @@ @applying_catalog_promotions -Feature: Applying all available catalog promotions +Feature: Applying all available types of catalog promotions In order to see products in best prices As a Customer I want to see discounted products in the catalog @@ -13,9 +13,9 @@ Feature: Applying all available catalog promotions And there is a catalog promotion "PHP sale" that reduces price by "10%" and applies on "PHP T-Shirt" variant And there is a catalog promotion "T-Shirt sale" that reduces price by "10%" and applies on "T-Shirt" product And there is a catalog promotion "Clothes sale" that reduces price by "10%" and applies on "Clothes" taxon - And there is a catalog promotion "Fixed PHP sale" that reduces price by fixed "$5.00" and applies on "PHP T-Shirt" variant - And there is a catalog promotion "Fixed T-Shirt sale" that reduces price by fixed "$5.00" and applies on "T-Shirt" product - And there is a catalog promotion "Fixed Clothes sale" that reduces price by fixed "$5.00" and applies on "Clothes" taxon + And there is a catalog promotion "Fixed PHP sale" that reduces price by fixed "$5.00" in the "Web-US" channel and applies on "PHP T-Shirt" variant + And there is a catalog promotion "Fixed T-Shirt sale" that reduces price by fixed "$5.00" in the "Web-US" channel and applies on "T-Shirt" product + And there is a catalog promotion "Fixed Clothes sale" that reduces price by fixed "$5.00" in the "Web-US" channel and applies on "Clothes" taxon @api @ui Scenario: Applying multiple catalog promotions diff --git a/features/promotion/applying_catalog_promotions/applying_catalog_promotion_with_proper_priorities.feature b/features/promotion/applying_catalog_promotions/applying_catalog_promotion_with_proper_priorities.feature index 34964a71e53..f99e6c0c2f2 100644 --- a/features/promotion/applying_catalog_promotions/applying_catalog_promotion_with_proper_priorities.feature +++ b/features/promotion/applying_catalog_promotions/applying_catalog_promotion_with_proper_priorities.feature @@ -11,8 +11,8 @@ Feature: Applying catalog promotion with proper priorities And this product belongs to "Clothes" And this product has "PHP T-Shirt" variant priced at "$100.00" And there is a catalog promotion "Autumn sale" with priority 10 that reduces price by "25%" and applies on "PHP T-Shirt" variant - And there is a catalog promotion "Winter sale" with priority 40 that reduces price by fixed "$10.00" and applies on "T-Shirt" product - And there is a catalog promotion "Spring sale" with priority 20 that reduces price by fixed "$5.00" and applies on "Clothes" taxon + And there is a catalog promotion "Winter sale" with priority 40 that reduces price by fixed "$10.00" in the "United States" channel and applies on "T-Shirt" product + And there is a catalog promotion "Spring sale" with priority 20 that reduces price by fixed "$5.00" in the "United States" channel and applies on "Clothes" taxon And there is a catalog promotion "Summer sale" with priority 30 that reduces price by "50%" and applies on "PHP T-Shirt" variant @api @ui diff --git a/features/promotion/applying_catalog_promotions/applying_catalog_promotions_with_fixed_discount_action.feature b/features/promotion/applying_catalog_promotions/applying_catalog_promotions_with_fixed_discount_action.feature index a059887fb35..6ba8e108bbf 100644 --- a/features/promotion/applying_catalog_promotions/applying_catalog_promotions_with_fixed_discount_action.feature +++ b/features/promotion/applying_catalog_promotions/applying_catalog_promotions_with_fixed_discount_action.feature @@ -1,5 +1,5 @@ @applying_catalog_promotions -Feature: Applying catalog promotions for product +Feature: Applying catalog promotions with fixed discount action In order to be attracted to specific products As a Visitor I want to see discounted products in the catalog @@ -9,7 +9,7 @@ Feature: Applying catalog promotions for product And the store has a "T-Shirt" configurable product And this product has "PHP T-Shirt" variant priced at "$20.00" And this product has "Java T-Shirt" variant priced at "$30.00" - And there is a catalog promotion "Winter sale" that reduces price by fixed "$10.00" and applies on "T-Shirt" product + And there is a catalog promotion "Winter sale" that reduces price by fixed "$10.00" in the "United States" channel and applies on "T-Shirt" product @api @ui Scenario: Applying simple catalog promotion @@ -23,6 +23,6 @@ Feature: Applying catalog promotions for product @api @ui Scenario: Applying multiple catalog promotions - Given there is a catalog promotion "Christmas sale" that reduces price by fixed "$5.00" and applies on "PHP T-Shirt" variant + Given there is a catalog promotion "Christmas sale" that reduces price by fixed "$5.00" in the "United States" channel and applies on "PHP T-Shirt" variant When I view "PHP T-Shirt" variant of the "T-Shirt" product Then I should see this variant is discounted from "$20.00" to "$5.00" with "Winter sale" and "Christmas sale" promotions diff --git a/features/promotion/applying_catalog_promotions/applying_catalog_promotions_with_taking_minimum_price_into_account.feature b/features/promotion/applying_catalog_promotions/applying_catalog_promotions_with_taking_minimum_price_into_account.feature index 209e533ad1a..ccf46c19cb2 100644 --- a/features/promotion/applying_catalog_promotions/applying_catalog_promotions_with_taking_minimum_price_into_account.feature +++ b/features/promotion/applying_catalog_promotions/applying_catalog_promotions_with_taking_minimum_price_into_account.feature @@ -8,7 +8,7 @@ Feature: Applying catalog promotions with taking minimum price into account Given the store operates on a single channel in "United States" And the store has a "T-Shirt" configurable product And this product has "PHP T-Shirt" variant priced at "$20.00" - And the "PHP T-Shirt" variant has minimum price "$15.00" in "United States" channel + And the "PHP T-Shirt" variant has minimum price of "$15.00" in the "United States" channel @api @ui Scenario: Applying percentage discount up to minimum price @@ -18,7 +18,7 @@ Feature: Applying catalog promotions with taking minimum price into account @api @ui Scenario: Applying fixed discount up to minimum price - Given there is a catalog promotion "Winter sale" that reduces price by fixed "$10.00" and applies on "PHP T-Shirt" variant + Given there is a catalog promotion "Winter sale" that reduces price by fixed "$10.00" in the "United States" channel and applies on "PHP T-Shirt" variant When I view "PHP T-Shirt" variant of the "T-Shirt" product Then I should see this variant is discounted from "$20.00" to "$15.00" with "Winter sale" promotion diff --git a/features/promotion/managing_catalog_promotions/creating_catalog_promotion.feature b/features/promotion/managing_catalog_promotions/creating_catalog_promotion.feature index e16e86a82cc..caecf358e45 100644 --- a/features/promotion/managing_catalog_promotions/creating_catalog_promotion.feature +++ b/features/promotion/managing_catalog_promotions/creating_catalog_promotion.feature @@ -73,11 +73,11 @@ Feature: Creating a catalog promotion And I specify its code as "winter_sale" And I name it "Winter sale" And I add scope that applies on "Clothes" taxon - And I add action that gives "$10.00" fixed discount in the "United States" channel + And I add action that gives "$10.00" of fixed discount in the "United States" channel And I add it Then there should be 1 new catalog promotion on the list And it should have "winter_sale" code and "Winter sale" name - And the "Winter sale" catalog promotion should have "$10.00" fixed discount in the "United States" channel + And the "Winter sale" catalog promotion should have "$10.00" of fixed discount in the "United States" channel @api @ui @javascript Scenario: Creating a catalog promotion for taxon diff --git a/features/promotion/managing_catalog_promotions/editing_catalog_promotion.feature b/features/promotion/managing_catalog_promotions/editing_catalog_promotion.feature index 195007241fc..e3dce205f20 100644 --- a/features/promotion/managing_catalog_promotions/editing_catalog_promotion.feature +++ b/features/promotion/managing_catalog_promotions/editing_catalog_promotion.feature @@ -78,9 +78,9 @@ Feature: Editing catalog promotion @api @ui @javascript Scenario: Editing catalog promotion action to be a fixed discount - When I edit "Christmas sale" catalog promotion to have "$10.00" fixed discount in the "United States" channel + When I edit "Christmas sale" catalog promotion to have "$10.00" of fixed discount in the "United States" channel Then I should be notified that it has been successfully edited - And this catalog promotion should have "$10.00" fixed discount in the "United States" channel + And this catalog promotion should have "$10.00" of fixed discount in the "United States" channel @api @ui Scenario: Being unable to edit catalog promotion if it is currently being processed diff --git a/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php b/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php index 070e4f08876..697a1e00424 100644 --- a/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php +++ b/src/Sylius/Behat/Context/Api/Admin/ManagingCatalogPromotionsContext.php @@ -207,7 +207,7 @@ public function iAddActionThatGivesPercentageDiscount(float $amount): void } /** - * @When /^I add action that gives ("[^"]+") fixed discount in the ("[^"]+" channel)$/ + * @When /^I add action that gives ("[^"]+") of fixed discount in the ("[^"]+" channel)$/ */ public function iAddActionThatGivesFixedDiscount(int $amount, ChannelInterface $channel): void { @@ -553,7 +553,7 @@ public function iEditCatalogPromotionToHaveDiscount(CatalogPromotionInterface $c } /** - * @When /^I edit ("[^"]+" catalog promotion) to have ("[^"]+") fixed discount in the ("[^"]+" channel)$/ + * @When /^I edit ("[^"]+" catalog promotion) to have ("[^"]+") of fixed discount in the ("[^"]+" channel)$/ */ public function iEditCatalogPromotionToHaveFixedDiscountInTheChannel( CatalogPromotionInterface $catalogPromotion, @@ -781,7 +781,7 @@ public function itShouldHaveDiscount(float $amount): void } /** - * @Then /^the ("[^"]+" catalog promotion) should have ("[^"]+") fixed discount in the ("[^"]+" channel)$/ + * @Then /^the ("[^"]+" catalog promotion) should have ("[^"]+") of fixed discount in the ("[^"]+" channel)$/ */ public function theCatalogPromotionShouldHaveFixedDiscountInTheChannel( CatalogPromotionInterface $catalogPromotion, @@ -794,7 +794,7 @@ public function theCatalogPromotionShouldHaveFixedDiscountInTheChannel( } /** - * @Then /^this catalog promotion should have ("[^"]+") fixed discount in the ("[^"]+" channel)$/ + * @Then /^this catalog promotion should have ("[^"]+") of fixed discount in the ("[^"]+" channel)$/ */ public function thisCatalogPromotionShouldHaveFixedDiscountInTheChannel(int $amount, ChannelInterface $channel): void { @@ -1036,7 +1036,6 @@ public function iShouldBeNotifiedThatItHasBeenSuccessfullyCreated(): void */ public function iShouldBeNotifiedThatItHasBeenSuccessfullyEdited(): void { - $res = $this->client->getLastResponse(); Assert::true( $this->responseChecker->isUpdateSuccessful($this->client->getLastResponse()), 'Catalog promotion could not be edited' diff --git a/src/Sylius/Behat/Context/Setup/CatalogPromotionContext.php b/src/Sylius/Behat/Context/Setup/CatalogPromotionContext.php index b443082fc40..52e8ea32986 100644 --- a/src/Sylius/Behat/Context/Setup/CatalogPromotionContext.php +++ b/src/Sylius/Behat/Context/Setup/CatalogPromotionContext.php @@ -209,16 +209,14 @@ public function thereIsACatalogPromotionThatReducesPriceByAndAppliesOn( } /** - * @Given /^there is a catalog promotion "([^"]*)" that reduces price by fixed ("[^"]+") and applies on ("[^"]+" variant)$/ + * @Given /^there is a catalog promotion "([^"]*)" that reduces price by fixed ("[^"]+") in the ("[^"]+" channel) and applies on ("[^"]+" variant)$/ */ - public function thereIsACatalogPromotionThatReducesPriceByFixedAndAppliesOnVariant( + public function thereIsACatalogPromotionThatReducesPriceByFixedInTheChannelAndAppliesOnVariant( string $name, int $discount, + ChannelInterface $channel, ProductVariantInterface $variant ): void { - /** @var ChannelInterface $channel */ - $channel = $this->sharedStorage->get('channel'); - $this->createCatalogPromotion( $name, null, @@ -237,16 +235,14 @@ public function thereIsACatalogPromotionThatReducesPriceByFixedAndAppliesOnVaria } /** - * @Given /^there is a catalog promotion "([^"]*)" that reduces price by fixed ("[^"]+") and applies on ("[^"]+" product)$/ + * @Given /^there is a catalog promotion "([^"]*)" that reduces price by fixed ("[^"]+") in the ("[^"]+" channel) and applies on ("[^"]+" product)$/ */ - public function thereIsACatalogPromotionThatReducesPriceByFixedAndAppliesOnProduct( + public function thereIsACatalogPromotionThatReducesPriceByFixedInTheChannelAndAppliesOnProduct( string $name, int $discount, + ChannelInterface $channel, ProductInterface $product ): void { - /** @var ChannelInterface $channel */ - $channel = $this->sharedStorage->get('channel'); - $this->createCatalogPromotion( $name, null, @@ -265,16 +261,14 @@ public function thereIsACatalogPromotionThatReducesPriceByFixedAndAppliesOnProdu } /** - * @Given /^there is a catalog promotion "([^"]*)" that reduces price by fixed ("[^"]+") and applies on ("[^"]+" taxon)$/ + * @Given /^there is a catalog promotion "([^"]*)" that reduces price by fixed ("[^"]+") in the ("[^"]+" channel) and applies on ("[^"]+" taxon)$/ */ - public function thereIsACatalogPromotionThatReducesPriceByFixedAndAppliesOnTaxon( + public function thereIsACatalogPromotionThatReducesPriceByFixedInTheChannelAndAppliesOnTaxon( string $name, int $discount, + ChannelInterface $channel, TaxonInterface $taxon ): void { - /** @var ChannelInterface $channel */ - $channel = $this->sharedStorage->get('channel'); - $this->createCatalogPromotion( $name, null, @@ -498,17 +492,15 @@ public function thereIsAnExclusiveCatalogPromotionWithPriorityThatReducesPriceBy } /** - * @Given /^there is a catalog promotion "([^"]+)" with priority ([^"]+) that reduces price by fixed ("[^"]+") and applies on ("[^"]+" product)$/ + * @Given /^there is a catalog promotion "([^"]+)" with priority ([^"]+) that reduces price by fixed ("[^"]+") in the ("[^"]+" channel) and applies on ("[^"]+" product)$/ */ - public function thereIsACatalogPromotionWithPriorityThatReducesPriceByFixedAndAppliesOnProduct( + public function thereIsACatalogPromotionWithPriorityThatReducesPriceByFixedInTheChannelAndAppliesOnProduct( string $name, int $priority, int $discount, + ChannelInterface $channel, ProductInterface $product ): void { - /** @var ChannelInterface $channel */ - $channel = $this->sharedStorage->get('channel'); - $catalogPromotion = $this->createCatalogPromotion( $name, null, @@ -530,17 +522,15 @@ public function thereIsACatalogPromotionWithPriorityThatReducesPriceByFixedAndAp } /** - * @Given /^there is a catalog promotion "([^"]+)" with priority ([^"]+) that reduces price by fixed ("[^"]+") and applies on ("[^"]+" taxon)$/ + * @Given /^there is a catalog promotion "([^"]+)" with priority ([^"]+) that reduces price by fixed ("[^"]+") in the ("[^"]+" channel) and applies on ("[^"]+" taxon)$/ */ - public function thereIsACatalogPromotionWithPriorityThatReducesPriceByFixedAndAppliesOnTaxon( + public function thereIsACatalogPromotionWithPriorityThatReducesPriceByFixedInTheChannelAndAppliesOnTaxon( string $name, int $priority, int $discount, + ChannelInterface $channel, TaxonInterface $taxon ): void { - /** @var ChannelInterface $channel */ - $channel = $this->sharedStorage->get('channel'); - $catalogPromotion = $this->createCatalogPromotion( $name, null, diff --git a/src/Sylius/Behat/Context/Setup/ProductContext.php b/src/Sylius/Behat/Context/Setup/ProductContext.php index 5d1ea9098bd..a164cf5374f 100644 --- a/src/Sylius/Behat/Context/Setup/ProductContext.php +++ b/src/Sylius/Behat/Context/Setup/ProductContext.php @@ -347,7 +347,7 @@ public function variantIsOriginalPricedAtInChannel( } /** - * @Given /^the ("[^"]+" variant) has minimum price ("[^"]+") in ("[^"]+" channel)$/ + * @Given /^the ("[^"]+" variant) has minimum price of ("[^"]+") in the ("[^"]+" channel)$/ */ public function variantHasMinimumPriceInChannel( ProductVariantInterface $productVariant, diff --git a/src/Sylius/Behat/Context/Ui/Admin/ManagingCatalogPromotionsContext.php b/src/Sylius/Behat/Context/Ui/Admin/ManagingCatalogPromotionsContext.php index 47a18fbcf01..0585e7b6044 100644 --- a/src/Sylius/Behat/Context/Ui/Admin/ManagingCatalogPromotionsContext.php +++ b/src/Sylius/Behat/Context/Ui/Admin/ManagingCatalogPromotionsContext.php @@ -14,7 +14,6 @@ namespace Sylius\Behat\Context\Ui\Admin; use Behat\Behat\Context\Context; -use http\Exception\InvalidArgumentException; use Sylius\Behat\Element\Admin\CatalogPromotion\FormElementInterface; use Sylius\Behat\NotificationType; use Sylius\Behat\Page\Admin\CatalogPromotion\CreatePageInterface; @@ -266,7 +265,7 @@ public function iAddActionThatGivesPercentageDiscount(string $discount): void } /** - * @When /^I add action that gives "(?:€|£|\$)([^"]+)" fixed discount in the ("[^"]+" channel)$/ + * @When /^I add action that gives "(?:€|£|\$)([^"]+)" of fixed discount in the ("[^"]+" channel)$/ */ public function iAddActionThatGivesFixedDiscount(string $discount, ChannelInterface $channel): void { @@ -370,7 +369,7 @@ public function iEditCatalogPromotionToHaveDiscount(CatalogPromotionInterface $c } /** - * @When /^I edit ("[^"]+" catalog promotion) to have "(?:€|£|\$)([^"]+)" fixed discount in the ("[^"]+" channel)$/ + * @When /^I edit ("[^"]+" catalog promotion) to have "(?:€|£|\$)([^"]+)" of fixed discount in the ("[^"]+" channel)$/ */ public function iEditCatalogPromotionToHaveFixedDiscountInTheChannel( CatalogPromotionInterface $catalogPromotion, @@ -444,11 +443,20 @@ public function iAddCatalogPromotionScopeForProductWithoutProducts(): void /** * @When I add percentage discount action without amount configured + */ + public function iAddPercentageDiscountActionWithoutAmountConfigured(): void + { + $this->formElement->addAction(); + $this->formElement->chooseActionType('Percentage discount'); + } + + /** * @When I add fixed discount action without amount configured */ - public function iAddDiscountActionWithoutAmountConfigured(): void + public function iAddFixedDiscountActionWithoutAmountConfigured(): void { $this->formElement->addAction(); + $this->formElement->chooseActionType('Fixed discount'); } /** @@ -460,7 +468,6 @@ public function iAddInvalidPercentageDiscountActionWithNonNumberInAmount(): void $this->formElement->specifyLastActionDiscount('alot'); } - /** * @When I add invalid fixed discount action with non number in amount for the :channel channel */ @@ -714,8 +721,8 @@ public function itShouldHaveDiscount(string $amount): void } /** - * @Then /^the ("[^"]+" catalog promotion) should have "(?:€|£|\$)([^"]+)" fixed discount in the ("[^"]+" channel)$/ - * @Then /^(this catalog promotion) should have "(?:€|£|\$)([^"]+)" fixed discount in the ("[^"]+" channel)$/ + * @Then /^the ("[^"]+" catalog promotion) should have "(?:€|£|\$)([^"]+)" of fixed discount in the ("[^"]+" channel)$/ + * @Then /^(this catalog promotion) should have "(?:€|£|\$)([^"]+)" of fixed discount in the ("[^"]+" channel)$/ */ public function theCatalogPromotionShouldHaveFixedDiscountInTheChannel( CatalogPromotionInterface $catalogPromotion, diff --git a/src/Sylius/Behat/Element/Admin/CatalogPromotion/FormElement.php b/src/Sylius/Behat/Element/Admin/CatalogPromotion/FormElement.php index c025638313e..06655da4af2 100644 --- a/src/Sylius/Behat/Element/Admin/CatalogPromotion/FormElement.php +++ b/src/Sylius/Behat/Element/Admin/CatalogPromotion/FormElement.php @@ -89,9 +89,9 @@ public function chooseScopeType(string $type): void public function chooseActionType(string $type): void { - $lastScope = $this->getElement('last_action'); + $lastAction = $this->getElement('last_action'); - $lastScope->selectFieldOption('Type', $type); + $lastAction->selectFieldOption('Type', $type); } public function chooseLastScopeCodes(array $codes): void @@ -104,7 +104,7 @@ public function chooseLastScopeCodes(array $codes): void public function specifyLastActionDiscount(string $discount): void { $lastAction = $this->getElement('last_action'); -// + $lastAction->find('css', 'input')->setValue($discount); } diff --git a/src/Sylius/Bundle/AdminBundle/Form/Extension/CatalogPromotionActionTypeExtension.php b/src/Sylius/Bundle/AdminBundle/Form/Extension/CatalogPromotionActionTypeExtension.php index c5a72407abc..ddee47d501f 100644 --- a/src/Sylius/Bundle/AdminBundle/Form/Extension/CatalogPromotionActionTypeExtension.php +++ b/src/Sylius/Bundle/AdminBundle/Form/Extension/CatalogPromotionActionTypeExtension.php @@ -33,7 +33,6 @@ public function __construct(iterable $actionConfigurationTypes, Environment $twi $this->actionConfigurationTypes[$type] = get_class($formType); $this->actionTypes['sylius.form.catalog_promotion.action.' . $type] = $type; } - ksort($this->actionTypes); $this->twig = $twig; } diff --git a/src/Sylius/Bundle/CoreBundle/Calculator/FixedDiscountPriceCalculator.php b/src/Sylius/Bundle/CoreBundle/Calculator/FixedDiscountPriceCalculator.php index 4e8ac1c35ba..5dfef8c857f 100644 --- a/src/Sylius/Bundle/CoreBundle/Calculator/FixedDiscountPriceCalculator.php +++ b/src/Sylius/Bundle/CoreBundle/Calculator/FixedDiscountPriceCalculator.php @@ -31,10 +31,20 @@ public function calculate(ChannelPricingInterface $channelPricing, CatalogPromot $price = $channelPricing->getPrice() - $action->getConfiguration()[$channelPricing->getChannelCode()]['amount']; - if ($price < $channelPricing->getMinimumPrice()) { - return $channelPricing->getMinimumPrice(); + $minimumPrice = $this->provideMinimumPrice($channelPricing); + if ($price < $minimumPrice) { + return $minimumPrice; } return $price; } + + private function provideMinimumPrice(ChannelPricingInterface $channelPricing): int + { + if ($channelPricing->getMinimumPrice() === null || $channelPricing->getMinimumPrice() <= 0) { + return 0; + } + + return $channelPricing->getMinimumPrice(); + } } diff --git a/src/Sylius/Bundle/CoreBundle/Calculator/PercentageDiscountPriceCalculator.php b/src/Sylius/Bundle/CoreBundle/Calculator/PercentageDiscountPriceCalculator.php index 38e92af5247..ac336b3dbfc 100644 --- a/src/Sylius/Bundle/CoreBundle/Calculator/PercentageDiscountPriceCalculator.php +++ b/src/Sylius/Bundle/CoreBundle/Calculator/PercentageDiscountPriceCalculator.php @@ -25,12 +25,22 @@ public function supports(CatalogPromotionActionInterface $action): bool public function calculate(ChannelPricingInterface $channelPricing, CatalogPromotionActionInterface $action): int { - $price = (int) ($channelPricing->getPrice() - ($channelPricing->getPrice() * $action->getConfiguration()['amount'])); + $price = (int)($channelPricing->getPrice() - ($channelPricing->getPrice() * $action->getConfiguration()['amount'])); - if ($price < $channelPricing->getMinimumPrice()) { - return $channelPricing->getMinimumPrice(); + $minimumPrice = $this->provideMinimumPrice($channelPricing); + if ($price < $minimumPrice) { + return $minimumPrice; } return $price; } + + private function provideMinimumPrice(ChannelPricingInterface $channelPricing): int + { + if ($channelPricing->getMinimumPrice() === null || $channelPricing->getMinimumPrice() <= 0) { + return 0; + } + + return $channelPricing->getMinimumPrice(); + } } diff --git a/src/Sylius/Bundle/CoreBundle/spec/Calculator/FixedDiscountPriceCalculatorSpec.php b/src/Sylius/Bundle/CoreBundle/spec/Calculator/FixedDiscountPriceCalculatorSpec.php index 0a47391d1a3..8971531e489 100644 --- a/src/Sylius/Bundle/CoreBundle/spec/Calculator/FixedDiscountPriceCalculatorSpec.php +++ b/src/Sylius/Bundle/CoreBundle/spec/Calculator/FixedDiscountPriceCalculatorSpec.php @@ -48,4 +48,30 @@ function it_calculates_price_for_given_channel_pricing_and_action( $this->calculate($channelPricing, $action)->shouldReturn(800); } + + function it_calculates_price_for_given_channel_pricing_and_action_with_taking_minimum_price_into_account( + ChannelPricingInterface $channelPricing, + CatalogPromotionActionInterface $action + ): void { + $action->getConfiguration()->willReturn(['WEB' => ['amount' => 600]]); + + $channelPricing->getChannelCode()->willReturn('WEB'); + $channelPricing->getPrice()->willReturn(1000); + $channelPricing->getMinimumPrice()->willReturn(500); + + $this->calculate($channelPricing, $action)->shouldReturn(500); + } + + function it_calculates_price_for_given_channel_pricing_and_action_without_minimum_price_specified( + ChannelPricingInterface $channelPricing, + CatalogPromotionActionInterface $action + ): void { + $action->getConfiguration()->willReturn(['WEB' => ['amount' => 1100]]); + + $channelPricing->getChannelCode()->willReturn('WEB'); + $channelPricing->getPrice()->willReturn(1000); + $channelPricing->getMinimumPrice()->willReturn(null); + + $this->calculate($channelPricing, $action)->shouldReturn(0); + } } diff --git a/src/Sylius/Bundle/CoreBundle/spec/Calculator/PercentageDiscountPriceCalculatorSpec.php b/src/Sylius/Bundle/CoreBundle/spec/Calculator/PercentageDiscountPriceCalculatorSpec.php index 01d14ffd865..4f84a883af4 100644 --- a/src/Sylius/Bundle/CoreBundle/spec/Calculator/PercentageDiscountPriceCalculatorSpec.php +++ b/src/Sylius/Bundle/CoreBundle/spec/Calculator/PercentageDiscountPriceCalculatorSpec.php @@ -47,4 +47,16 @@ function it_calculates_price_for_given_channel_pricing_and_action( $this->calculate($channelPricing, $action)->shouldReturn(700); } + + function it_calculates_price_for_given_channel_pricing_and_action_with_taking_minimum_price_into_account( + ChannelPricingInterface $channelPricing, + CatalogPromotionActionInterface $action + ): void { + $action->getConfiguration()->willReturn(['amount' => 0.7]); + + $channelPricing->getPrice()->willReturn(1000); + $channelPricing->getMinimumPrice()->willReturn(500); + + $this->calculate($channelPricing, $action)->shouldReturn(500); + } } diff --git a/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionAction/FixedDiscountActionConfigurationType.php b/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionAction/FixedDiscountActionConfigurationType.php index b15e8c27775..d7ca0e17a57 100644 --- a/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionAction/FixedDiscountActionConfigurationType.php +++ b/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionAction/FixedDiscountActionConfigurationType.php @@ -18,10 +18,6 @@ public function buildForm(FormBuilderInterface $builder, array $options): void $builder ->add('amount', MoneyType::class, [ 'label' => 'sylius.ui.amount', - 'constraints' => [ - new NotBlank(['groups' => ['sylius']]), - new Type(['type' => 'numeric', 'groups' => ['sylius']]), - ], 'currency' => $options['currency'], ]) ; diff --git a/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionActionType.php b/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionActionType.php index 3503dc5adc5..dbffc82b4dc 100644 --- a/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionActionType.php +++ b/src/Sylius/Bundle/PromotionBundle/Form/Type/CatalogPromotionActionType.php @@ -28,7 +28,6 @@ public function __construct( $this->actionConfigurationTypes[$type] = get_class($formType); $this->actionTypes['sylius.form.catalog_promotion.action.' . $type] = $type; } - ksort($this->actionTypes); } public function buildForm(FormBuilderInterface $builder, array $options) From ddc21f9831be3ad92d64b77f7c56365d9ddf2f34 Mon Sep 17 00:00:00 2001 From: Grzegorz Sadowski Date: Thu, 25 Nov 2021 08:57:34 +0100 Subject: [PATCH 10/11] [CatalogPromotion][UI] Fix changing type of scope --- src/Sylius/Bundle/AdminBundle/Resources/private/js/app.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Sylius/Bundle/AdminBundle/Resources/private/js/app.js b/src/Sylius/Bundle/AdminBundle/Resources/private/js/app.js index 042d6379589..2dc44a15067 100644 --- a/src/Sylius/Bundle/AdminBundle/Resources/private/js/app.js +++ b/src/Sylius/Bundle/AdminBundle/Resources/private/js/app.js @@ -81,13 +81,13 @@ $(document).ready(() => { } }); - $(document).loadCatalogPromotionActionConfiguration( - document.querySelector('#sylius_catalog_promotion_actions [data-form-collection="item"]:last-child') - ); - $(document).loadCatalogPromotionScopeConfiguration( document.querySelector('#sylius_catalog_promotion_scopes [data-form-collection="item"]:last-child') ); + + $(document).loadCatalogPromotionActionConfiguration( + document.querySelector('#sylius_catalog_promotion_actions [data-form-collection="item"]:last-child') + ); }); $(document).on('collection-form-update', () => { $('.sylius-autocomplete').each((index, element) => { From 969595ada478bc148467cbe111e9c5ed7b3f2a7f Mon Sep 17 00:00:00 2001 From: Grzegorz Sadowski Date: Thu, 25 Nov 2021 09:54:23 +0100 Subject: [PATCH 11/11] [CatalogPromotion] Fixes after rebasing with exclusive promotions feature --- src/Sylius/Behat/Context/Setup/CatalogPromotionContext.php | 4 ++-- .../spec/Applicator/CatalogPromotionApplicatorSpec.php | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Sylius/Behat/Context/Setup/CatalogPromotionContext.php b/src/Sylius/Behat/Context/Setup/CatalogPromotionContext.php index 52e8ea32986..bf249cb10e4 100644 --- a/src/Sylius/Behat/Context/Setup/CatalogPromotionContext.php +++ b/src/Sylius/Behat/Context/Setup/CatalogPromotionContext.php @@ -540,8 +540,8 @@ public function thereIsACatalogPromotionWithPriorityThatReducesPriceByFixedInThe 'configuration' => ['taxons' => [$taxon->getCode()]], ]], [[ - 'type' => CatalogPromotionActionInterface::TYPE_PERCENTAGE_DISCOUNT, - 'configuration' => ['amount' => $discount], + 'type' => CatalogPromotionActionInterface::TYPE_FIXED_DISCOUNT, + 'configuration' => [$channel->getCode() => ['amount' => $discount]], ]], $priority, ); diff --git a/src/Sylius/Bundle/CoreBundle/spec/Applicator/CatalogPromotionApplicatorSpec.php b/src/Sylius/Bundle/CoreBundle/spec/Applicator/CatalogPromotionApplicatorSpec.php index 559bb06e73c..4101bded8a4 100644 --- a/src/Sylius/Bundle/CoreBundle/spec/Applicator/CatalogPromotionApplicatorSpec.php +++ b/src/Sylius/Bundle/CoreBundle/spec/Applicator/CatalogPromotionApplicatorSpec.php @@ -89,6 +89,7 @@ function it_applies_percentage_discount_on_product_variant( } function it_applies_discount_on_product_variant_only_if_exclusive_promotion_is_not_already_applied( + CatalogPromotionPriceCalculatorInterface $priceCalculator, AppliedPromotionInformationFormatterInterface $appliedPromotionInformationFormatter, ProductVariantInterface $variant, CatalogPromotionInterface $catalogPromotion, @@ -108,7 +109,6 @@ function it_applies_discount_on_product_variant_only_if_exclusive_promotion_is_n $variant->getChannelPricingForChannel($secondChannel)->willReturn($secondChannelPricing); $appliedPromotionInformationFormatter->format($catalogPromotion)->willReturn(['winter_sale' => ['name' => 'Winter sale']]); - $catalogPromotionAction->getConfiguration()->willReturn(['amount' => 0.3]); $firstChannelPricing->hasExclusiveCatalogPromotionApplied()->willReturn(true); @@ -119,6 +119,9 @@ function it_applies_discount_on_product_variant_only_if_exclusive_promotion_is_n $secondChannelPricing->getOriginalPrice()->willReturn(null); $secondChannelPricing->getMinimumPrice()->willReturn(0); $secondChannelPricing->setOriginalPrice(1400)->shouldBeCalled(); + + $priceCalculator->calculate($secondChannelPricing, $catalogPromotionAction)->willReturn(980); + $secondChannelPricing->setPrice(980)->shouldBeCalled(); $secondChannelPricing->addAppliedPromotion(['winter_sale' => ['name' => 'Winter sale']])->shouldBeCalled(); $catalogPromotion->isExclusive()->willReturn(false);