diff --git a/features/admin/promotion/managing_promotions/adding_promotion.feature b/features/admin/promotion/managing_promotions/adding_promotion.feature index a96941ac760..cc1a9b96e27 100644 --- a/features/admin/promotion/managing_promotions/adding_promotion.feature +++ b/features/admin/promotion/managing_promotions/adding_promotion.feature @@ -8,7 +8,7 @@ Feature: Adding a new promotion Given the store operates on a single channel in "United States" And I am logged in as an administrator - @api @todo @ui + @api @ui Scenario: Adding a new promotion When I want to create a new promotion And I specify its code as "FULL_METAL_PROMOTION" @@ -17,7 +17,7 @@ Feature: Adding a new promotion Then I should be notified that it has been successfully created And the "Full metal promotion" promotion should appear in the registry - @api @todo @ui + @api @ui Scenario: Adding a new promotion with usage limit When I want to create a new promotion And I specify its code as "FULL_METAL_PROMOTION" @@ -27,7 +27,7 @@ Feature: Adding a new promotion Then I should be notified that it has been successfully created And the "Full metal promotion" promotion should be available to be used only 50 times - @api @todo @ui + @api @ui Scenario: Adding a new exclusive promotion When I want to create a new promotion And I specify its code as "FULL_METAL_PROMOTION" @@ -37,7 +37,7 @@ Feature: Adding a new promotion Then I should be notified that it has been successfully created And the "Full metal promotion" promotion should be exclusive - @api @todo @ui + @api @ui Scenario: Adding a new coupon based promotion When I want to create a new promotion And I specify its code as "FULL_METAL_PROMOTION" @@ -47,7 +47,7 @@ Feature: Adding a new promotion Then I should be notified that it has been successfully created And the "Full metal promotion" promotion should be coupon based - @api @todo @ui + @api @ui Scenario: Adding a new channels promotion When I want to create a new promotion And I specify its code as "FULL_METAL_PROMOTION" @@ -57,7 +57,7 @@ Feature: Adding a new promotion Then I should be notified that it has been successfully created And the "Full metal promotion" promotion should be applicable for the "United States" channel - @api @todo @ui + @api @ui Scenario: Adding a promotion with start and end date When I want to create a new promotion And I specify its code as "FULL_METAL_PROMOTION" @@ -66,7 +66,7 @@ Feature: Adding a new promotion And I add it Then I should be notified that it has been successfully created - @api @todo @ui + @api @ui Scenario: Adding a promotion not applies to discounted by catalog promotion items When I want to create a new promotion And I specify its code as "FULL_METAL_PROMOTION" @@ -75,3 +75,11 @@ Feature: Adding a new promotion And I add it Then I should be notified that it has been successfully created And the "Full metal promotion" promotion should not applies to discounted items + + @ui @javascript @no-api + Scenario: Seeing rule and action configuration forms + When I want to create a new promotion + And I add a new rule + And I add a new action + Then I should see the rule configuration form + And I should see the action configuration form diff --git a/features/admin/promotion/managing_promotions/adding_promotion_in_different_languages.feature b/features/admin/promotion/managing_promotions/adding_promotion_in_different_languages.feature index 341ee1305f9..12bcaed0682 100644 --- a/features/admin/promotion/managing_promotions/adding_promotion_in_different_languages.feature +++ b/features/admin/promotion/managing_promotions/adding_promotion_in_different_languages.feature @@ -9,7 +9,7 @@ Feature: Adding promotion in different languages And that channel allows to shop using "English (United States)" and "Polish (Poland)" locales And I am logged in as an administrator - @api @todo @ui @javascript + @api @ui Scenario: Adding a promotion with a label in a different language When I want to create a new promotion And I specify its code as "FULL_METAL_PROMOTION" diff --git a/features/admin/promotion/managing_promotions/adding_promotion_with_action.feature b/features/admin/promotion/managing_promotions/adding_promotion_with_action.feature index 576b564e378..9aed8f50609 100644 --- a/features/admin/promotion/managing_promotions/adding_promotion_with_action.feature +++ b/features/admin/promotion/managing_promotions/adding_promotion_with_action.feature @@ -8,7 +8,7 @@ Feature: Adding a new promotion with action Given the store operates on a single channel in "United States" And I am logged in as an administrator - @api @todo @ui @javascript + @api @ui @javascript Scenario: Adding a new promotion with fixed discount When I want to create a new promotion And I specify its code as "10_for_all_products" @@ -18,7 +18,7 @@ Feature: Adding a new promotion with action Then I should be notified that it has been successfully created And the "$10.00 for all products!" promotion should appear in the registry - @api @todo @ui @javascript + @api @ui @javascript Scenario: Adding a promotion with item percentage discount When I want to create a new promotion And I specify its code as "promotion_for_all_product_items" diff --git a/features/admin/promotion/managing_promotions/adding_promotion_with_action_in_different_channels.feature b/features/admin/promotion/managing_promotions/adding_promotion_with_action_in_different_channels.feature index 4574ee79767..3620dcd099d 100644 --- a/features/admin/promotion/managing_promotions/adding_promotion_with_action_in_different_channels.feature +++ b/features/admin/promotion/managing_promotions/adding_promotion_with_action_in_different_channels.feature @@ -9,7 +9,7 @@ Feature: Adding a new promotion with action configured in different channels And the store also operates on another channel named "Web-GB" in "GBP" currency And I am logged in as an administrator - @todo @ui @mink:chromedriver @api + @ui @mink:chromedriver @api Scenario: Adding a new promotion with item fixed discount When I want to create a new promotion And I specify its code as "20_for_all_products" @@ -17,5 +17,4 @@ Feature: Adding a new promotion with action configured in different channels And I add the "Item fixed discount" action configured with amount of "$10.00" for "United States" channel And it is also configured with amount of "£16.00" for "Web-GB" channel And I add it - Then I should be notified that it has been successfully created - And the "Item fixed discount for all products!" promotion should appear in the registry + Then the "Item fixed discount for all products!" promotion should be successfully created diff --git a/features/admin/promotion/managing_promotions/adding_promotion_with_filter.feature b/features/admin/promotion/managing_promotions/adding_promotion_with_filter.feature index 8ab12d48539..cce21ace28d 100644 --- a/features/admin/promotion/managing_promotions/adding_promotion_with_filter.feature +++ b/features/admin/promotion/managing_promotions/adding_promotion_with_filter.feature @@ -8,7 +8,7 @@ Feature: Adding promotion with filter Given the store operates on a single channel in "United States" And I am logged in as an administrator - @api @todo @ui @mink:chromedriver + @api @ui @mink:chromedriver Scenario: Adding a promotion with item fixed discount only for products over 10 When I want to create a new promotion And I specify its code as "10_for_all_products_over_10" @@ -16,10 +16,9 @@ Feature: Adding promotion with filter And I add the "Item fixed discount" action configured with amount of "$10.00" for "United States" channel And I specify that on "United States" channel this action should be applied to items with price greater than "$10.00" And I add it - Then I should be notified that it has been successfully created - And the "$10 discount for all products over $10!" promotion should appear in the registry + Then the "$10 discount for all products over $10!" promotion should be successfully created - @api @todo @ui @javascript + @api @ui @javascript Scenario: Adding a promotion with item fixed discount only for products between 10 and 100 When I want to create a new promotion And I specify its code as "10_for_all_products_over_10" @@ -30,31 +29,31 @@ Feature: Adding promotion with filter Then I should be notified that it has been successfully created And the "$10 discount for (almost) all products!" promotion should appear in the registry - @api @todo @ui @javascript + @api @ui @mink:chromedriver Scenario: Adding a promotion with fixed discount for all t-shirts Given the store classifies its products as "T-Shirts" and "Mugs" When I want to create a new promotion And I specify its code as "10_for_all_t_shirts" And I name it "$10 discount for all T-Shirts!" And I add the "Item fixed discount" action configured with amount of "$10.00" for "United States" channel - And I specify that this action should be applied to items from "T-Shirts" category + And I specify that this action should be applied to items from "T-Shirts" category for "United States" channel And I add it Then I should be notified that it has been successfully created And the "$10 discount for all T-Shirts!" promotion should appear in the registry - @api @todo @ui @javascript + @api @ui @mink:chromedriver Scenario: Adding a promotion with fixed discount for PHP T-Shirt Given the store has a product "PHP T-Shirt" priced at "$100.00" When I want to create a new promotion And I specify its code as "10_for_php_t_shirt" And I name it "$10 discount for PHP T-Shirts!" And I add the "Item fixed discount" action configured with amount of "$10.00" for "United States" channel - And I specify that this action should be applied to the "PHP T-Shirt" product + And I specify that this action should be applied to the "PHP T-Shirt" product for "United States" channel And I add it Then I should be notified that it has been successfully created And the "$10 discount for PHP T-Shirts!" promotion should appear in the registry - @api @todo @ui @javascript + @api @ui @javascript Scenario: Adding a promotion with item percentage discount only for products over 10 When I want to create a new promotion And I specify its code as "10_for_all_products_over_10" @@ -65,7 +64,7 @@ Feature: Adding promotion with filter Then I should be notified that it has been successfully created And the "$10 discount for all products over $10!" promotion should appear in the registry - @api @todo @ui @javascript + @api @ui @javascript Scenario: Adding a promotion with item percentage discount only for products between 10 and 100 When I want to create a new promotion And I specify its code as "10_for_all_products_over_10" @@ -76,26 +75,26 @@ Feature: Adding promotion with filter Then I should be notified that it has been successfully created And the "$10 discount for (almost) all products!" promotion should appear in the registry - @api @todo @ui @javascript + @api @ui @mink:chromedriver Scenario: Adding a promotion with 10% percentage discount for all t-shirts Given the store classifies its products as "T-Shirts" and "Mugs" When I want to create a new promotion And I specify its code as "10_for_all_t_shirts" And I name it "$10 discount for all T-Shirts!" And I add the "Item percentage discount" action configured with a percentage value of "10%" for "United States" channel - And I specify that this action should be applied to items from "T-Shirts" category + And I specify that this action should be applied to items from "T-Shirts" category for "United States" channel And I add it Then I should be notified that it has been successfully created And the "$10 discount for all T-Shirts!" promotion should appear in the registry - @api @todo @ui @javascript + @api @ui @mink:chromedriver Scenario: Adding a promotion with 10% percentage discount for PHP T-Shirt Given the store has a product "PHP T-Shirt" priced at "$100.00" When I want to create a new promotion And I specify its code as "10_for_php_t_shirt" And I name it "10% discount for PHP T-Shirts!" And I add the "Item percentage discount" action configured with a percentage value of "10%" for "United States" channel - And I specify that this action should be applied to the "PHP T-Shirt" product + And I specify that this action should be applied to the "PHP T-Shirt" product for "United States" channel And I add it Then I should be notified that it has been successfully created And the "10% discount for PHP T-Shirts!" promotion should appear in the registry diff --git a/features/admin/promotion/managing_promotions/adding_promotion_with_rule.feature b/features/admin/promotion/managing_promotions/adding_promotion_with_rule.feature index 5582400607f..74127af5b0e 100644 --- a/features/admin/promotion/managing_promotions/adding_promotion_with_rule.feature +++ b/features/admin/promotion/managing_promotions/adding_promotion_with_rule.feature @@ -9,7 +9,7 @@ Feature: Adding a new promotion with rule And the store classifies its products as "T-Shirts" and "Mugs" And I am logged in as an administrator - @api @todo @ui @javascript + @api @ui @mink:chromedriver Scenario: Adding a new promotion with taxon rule When I want to create a new promotion And I specify its code as "HOLIDAY_SALE" @@ -19,7 +19,7 @@ Feature: Adding a new promotion with rule Then I should be notified that it has been successfully created And the "Holiday sale" promotion should appear in the registry - @api @todo @ui @javascript + @api @ui @mink:chromedriver Scenario: Adding a new promotion with total price of items from taxon rule When I want to create a new promotion And I specify its code as "100_MUGS_PROMOTION" @@ -29,7 +29,7 @@ Feature: Adding a new promotion with rule Then I should be notified that it has been successfully created And the "100 Mugs promotion" promotion should appear in the registry - @api @todo @ui @javascript + @api @ui @mink:chromedriver Scenario: Adding a new promotion with contains product rule Given the store has a product "PHP T-Shirt" priced at "$100.00" When I want to create a new promotion @@ -40,7 +40,7 @@ Feature: Adding a new promotion with rule Then I should be notified that it has been successfully created And the "PHP T-Shirt promotion" promotion should appear in the registry - @api @todo @ui @javascript + @api @ui @javascript Scenario: Adding a new group based promotion Given the store has a customer group "Wholesale" When I want to create a new promotion @@ -50,11 +50,3 @@ Feature: Adding a new promotion with rule And I add it Then I should be notified that it has been successfully created And the "Wholesale promotion" promotion should appear in the registry - - @todo @ui @javascript @no-api - Scenario: Adding a new promotion of default type with one action - When I want to create a new promotion - And I add a new rule - And I add a new action - Then I should see the rule configuration form - And I should see the action configuration form diff --git a/features/admin/promotion/managing_promotions/adding_promotion_with_rule_in_different_channels.feature b/features/admin/promotion/managing_promotions/adding_promotion_with_rule_in_different_channels.feature index ea880f49a1f..35be86e7e17 100644 --- a/features/admin/promotion/managing_promotions/adding_promotion_with_rule_in_different_channels.feature +++ b/features/admin/promotion/managing_promotions/adding_promotion_with_rule_in_different_channels.feature @@ -9,12 +9,11 @@ Feature: Adding a new promotion with rule configured in different channels And the store operates on a channel named "Web-GB" in "GBP" currency And I am logged in as an administrator - @todo @api @ui @mink:chromedriver + @api @ui @mink:chromedriver Scenario: Adding a new promotion with total price of items from taxon rule When I want to create a new promotion And I specify its code as "100_IN_EVERY_CURRENCY" And I name it "100 in every currency" And I add the "Item total" rule configured with "€100.00" amount for "United States" channel and "£100.00" amount for "Web-GB" channel And I add it - Then I should be notified that it has been successfully created - And the "100 in every currency" promotion should appear in the registry + Then the "100 in every currency" promotion should be successfully created diff --git a/features/admin/promotion/managing_promotions/deleting_promotion.feature b/features/admin/promotion/managing_promotions/deleting_promotion.feature index 27374d22385..b032fde706f 100644 --- a/features/admin/promotion/managing_promotions/deleting_promotion.feature +++ b/features/admin/promotion/managing_promotions/deleting_promotion.feature @@ -9,7 +9,7 @@ Feature: Deleting a promotion And there is a promotion "Christmas sale" And I am logged in as an administrator - @api @todo @ui + @api @ui Scenario: Deleted promotion should disappear from the registry When I delete a "Christmas sale" promotion Then I should be notified that it has been successfully deleted diff --git a/features/admin/promotion/managing_promotions/editing_promotion.feature b/features/admin/promotion/managing_promotions/editing_promotion.feature index 09198dbfd14..965214a267f 100644 --- a/features/admin/promotion/managing_promotions/editing_promotion.feature +++ b/features/admin/promotion/managing_promotions/editing_promotion.feature @@ -10,12 +10,12 @@ Feature: Editing promotion And there is a promotion "Holiday sale" with priority 1 And I am logged in as an administrator - @api @todo @ui + @api @ui Scenario: Being unable to change code of promotion When I want to modify a "Christmas sale" promotion Then I should not be able to edit its code - @api @todo @ui + @api @ui Scenario: Editing promotions usage limit When I want to modify a "Christmas sale" promotion And I set its usage limit to 50 @@ -23,7 +23,7 @@ Feature: Editing promotion Then I should be notified that it has been successfully edited And the "Christmas sale" promotion should be available to be used only 50 times - @api @todo @ui + @api @ui Scenario: Editing promotion exclusiveness When I want to modify a "Christmas sale" promotion And I set it as exclusive @@ -31,7 +31,7 @@ Feature: Editing promotion Then I should be notified that it has been successfully edited And the "Christmas sale" promotion should be exclusive - @api @todo @ui + @api @ui Scenario: Editing promotions coupon based option When I want to modify a "Christmas sale" promotion And I make it coupon based @@ -39,7 +39,7 @@ Feature: Editing promotion Then I should be notified that it has been successfully edited And the "Christmas sale" promotion should be coupon based - @api @todo @ui + @api @ui Scenario: Editing promotions channels When I want to modify a "Christmas sale" promotion And I make it applicable for the "United States" channel @@ -47,7 +47,7 @@ Feature: Editing promotion Then I should be notified that it has been successfully edited And the "Christmas sale" promotion should be applicable for the "United States" channel - @api @todo @ui + @api @ui Scenario: Editing a promotion with start and end date When I want to modify a "Christmas sale" promotion And I make it available from "12.12.2017" to "24.12.2017" @@ -55,24 +55,34 @@ Feature: Editing promotion Then I should be notified that it has been successfully edited And the "Christmas sale" promotion should be available from "12.12.2017" to "24.12.2017" - @api @todo @ui + @api @ui Scenario: Editing promotion after adding a new channel Given this promotion gives "$10.00" discount to every order When the store also operates on another channel named "EU-WEB" Then I should be able to modify a "Christmas sale" promotion - @todo @ui @no-api - Scenario: Remove priority from existing promotion + @ui @no-api + Scenario: Removing priority from existing promotion When I want to modify a "Christmas sale" promotion And I remove its priority And I save my changes Then I should be notified that it has been successfully edited And the "Christmas sale" promotion should have priority 1 - @api @todo @ui + @api @ui Scenario: Setting promotion to the lowest priority When I want to modify a "Christmas sale" promotion And I set its priority to "-1" And I save my changes Then I should be notified that it has been successfully edited And the "Christmas sale" promotion should have priority 1 + + @ui @javascript + Scenario: Removing rule and action from existing promotion + Given the promotion gives "$10.00" discount to every order with quantity at least 1 + When I want to modify a "Holiday sale" promotion + And I remove its last rule + And I remove its last action + And I save my changes + Then I should not see the rule configuration form + And I should not see the action configuration form diff --git a/features/admin/promotion/managing_promotions/preventing_deletion_of_promotions_in_use.feature b/features/admin/promotion/managing_promotions/preventing_deletion_of_promotions_in_use.feature index bbe9656b046..44250833742 100644 --- a/features/admin/promotion/managing_promotions/preventing_deletion_of_promotions_in_use.feature +++ b/features/admin/promotion/managing_promotions/preventing_deletion_of_promotions_in_use.feature @@ -16,7 +16,7 @@ Feature: Prevent deletion of promotions applied to order And the customer chose "Free" shipping method to "United States" with "Cash on Delivery" payment And I am logged in as an administrator - @api @todo @ui + @api @ui Scenario: Being unable to delete a promotion that was applied to an order When I try to delete a "Christmas sale" promotion Then I should be notified that it is in use and cannot be deleted diff --git a/features/admin/promotion/managing_promotions/promotion_unique_code_validation.feature b/features/admin/promotion/managing_promotions/promotion_unique_code_validation.feature index c70932ea7b9..7f290f63f06 100644 --- a/features/admin/promotion/managing_promotions/promotion_unique_code_validation.feature +++ b/features/admin/promotion/managing_promotions/promotion_unique_code_validation.feature @@ -9,7 +9,7 @@ Feature: Promotion unique code validation And there is a promotion "No-VAT promotion" identified by "NO_VAT" code And I am logged in as an administrator - @api @todo @ui + @api @ui Scenario: Trying to add promotion with taken code When I want to create a new promotion And I specify its code as "NO_VAT" diff --git a/features/admin/promotion/managing_promotions/promotion_validation.feature b/features/admin/promotion/managing_promotions/promotion_validation.feature index 615f4968489..b0e22f895f9 100644 --- a/features/admin/promotion/managing_promotions/promotion_validation.feature +++ b/features/admin/promotion/managing_promotions/promotion_validation.feature @@ -17,7 +17,7 @@ Feature: Promotion validation And I try to save my changes Then I should be notified that the locale is not available - @api @todo @ui + @api @ui Scenario: Trying to add a new promotion without specifying its code When I want to create a new promotion And I name it "No-VAT promotion" @@ -26,7 +26,7 @@ Feature: Promotion validation Then I should be notified that code is required And promotion with name "No-VAT promotion" should not be added - @api @todo @ui + @api @ui Scenario: Trying to add a new promotion with a too long code When I want to create a new promotion And I name it "No-VAT promotion" @@ -34,7 +34,7 @@ Feature: Promotion validation And I try to add it Then I should be notified that code is too long - @api @todo @ui + @api @ui Scenario: Trying to add a new promotion without specifying its name When I want to create a new promotion And I specify its code as "no_vat_promotion" @@ -43,7 +43,7 @@ Feature: Promotion validation Then I should be notified that name is required And promotion with code "no_vat_promotion" should not be added - @api @todo @ui + @api @ui Scenario: Adding a promotion with start date set up after end date When I want to create a new promotion And I specify its code as "FULL_METAL_PROMOTION" @@ -52,7 +52,7 @@ Feature: Promotion validation And I try to add it Then I should be notified that promotion cannot end before it starts - @api @todo @ui + @api @ui Scenario: Trying to remove name from existing promotion Given there is a promotion "Christmas sale" When I want to modify this promotion @@ -61,7 +61,7 @@ Feature: Promotion validation Then I should be notified that name is required And this promotion should still be named "Christmas sale" - @api @todo @ui + @api @ui Scenario: Trying to add start later then end date for existing promotion Given there is a promotion "Christmas sale" When I want to modify this promotion @@ -69,7 +69,7 @@ Feature: Promotion validation And I try to save my changes Then I should be notified that promotion cannot end before it starts - @api @todo @ui @mink:chromedriver + @api @ui Scenario: Adding a promotion with label exceeding 255 characters Given there is a promotion "Christmas sale" When I want to modify this promotion @@ -77,7 +77,7 @@ Feature: Promotion validation And I try to save my changes Then I should be notified that promotion label in "Polish (Poland)" locale is too long - @api @todo @ui @javascript + @api @ui @javascript Scenario: Trying to add a new promotion without specifying a order percentage discount When I want to create a new promotion And I specify its code as "christmas_sale" @@ -87,7 +87,7 @@ Feature: Promotion validation Then I should be notified that this value should not be blank And promotion with name "Christmas sale" should not be added - @api @todo @ui @javascript + @api @ui @javascript Scenario: Trying to add a new promotion without specifying an item percentage discount When I want to create a new promotion And I specify its code as "christmas_sale" @@ -97,7 +97,7 @@ Feature: Promotion validation Then I should be notified that this value should not be blank And promotion with name "Christmas sale" should not be added - @api @todo @ui @javascript + @api @ui @javascript Scenario: Trying to add a new promotion with a wrong order percentage discount When I want to create a new promotion And I specify its code as "christmas_sale" @@ -107,7 +107,7 @@ Feature: Promotion validation Then I should be notified that a percentage discount value must be between 0% and 100% And promotion with name "Christmas sale" should not be added - @api @todo @ui @javascript + @api @ui @javascript Scenario: Trying to add a new promotion with a wrong item percentage discount When I want to create a new promotion And I specify its code as "christmas_sale" diff --git a/features/admin/promotion/managing_promotions/promotions_filter_validation.feature b/features/admin/promotion/managing_promotions/promotions_filter_validation.feature index d4e7cef4956..6fe1bd30444 100644 --- a/features/admin/promotion/managing_promotions/promotions_filter_validation.feature +++ b/features/admin/promotion/managing_promotions/promotions_filter_validation.feature @@ -8,7 +8,7 @@ Feature: Promotion filters validation Given the store operates on a single channel in "United States" And I am logged in as an administrator - @api @todo @ui @javascript + @api @ui @javascript Scenario: Adding a promotion with wrong minimum price on price range filter When I want to create a new promotion And I specify its code as "10_for_all_products_over_10" @@ -19,7 +19,7 @@ Feature: Promotion filters validation Then I should be notified that a minimum value should be a numeric value And promotion with name "$10 discount for all products over $10!" should not be added - @api @todo @ui @javascript + @api @ui @javascript Scenario: Adding a promotion with wrong maximum price on price range filter When I want to create a new promotion And I specify its code as "10_for_all_products_over_10" diff --git a/features/admin/promotion/managing_promotions/seeing_correct_percantage_discounts_while_editing_promotion_with_action_and_decimal_places.feature b/features/admin/promotion/managing_promotions/seeing_correct_percentage_discounts_while_editing_promotion_with_action_and_decimal_places.feature similarity index 96% rename from features/admin/promotion/managing_promotions/seeing_correct_percantage_discounts_while_editing_promotion_with_action_and_decimal_places.feature rename to features/admin/promotion/managing_promotions/seeing_correct_percentage_discounts_while_editing_promotion_with_action_and_decimal_places.feature index 25d5349e7f9..33a6bb9fdfb 100644 --- a/features/admin/promotion/managing_promotions/seeing_correct_percantage_discounts_while_editing_promotion_with_action_and_decimal_places.feature +++ b/features/admin/promotion/managing_promotions/seeing_correct_percentage_discounts_while_editing_promotion_with_action_and_decimal_places.feature @@ -10,7 +10,7 @@ Feature: Seeing correct percentage discounts while editing promotion with action And this promotion gives "12.00%" discount to every order And I am logged in as an administrator - @api @todo @ui + @api @ui Scenario: Seeing an accurate percentage amount after editing the promotion including the value up to one decimal place When I want to modify a "Cheap Stuff" promotion And I edit this promotion percentage action to have "2.5%" @@ -18,7 +18,7 @@ Feature: Seeing correct percentage discounts while editing promotion with action Then I should be notified that it has been successfully edited And it should have "2.50%" of order percentage discount - @api @todo @ui + @api @ui Scenario: Seeing an accurate percentage amount after editing the promotion including the value up to two decimal places When I want to modify a "Cheap Stuff" promotion And I edit this promotion percentage action to have "2.56%" @@ -26,7 +26,7 @@ Feature: Seeing correct percentage discounts while editing promotion with action Then I should be notified that it has been successfully edited And it should have "2.56%" of order percentage discount - @todo @ui @no-api + @ui @no-api Scenario: Seeing an accurate percentage amount after using a comma as a decimal separator When I want to modify a "Cheap Stuff" promotion And I edit this promotion percentage action to have "2,56%" diff --git a/src/Sylius/Behat/Context/Api/Admin/ManagingPromotionsContext.php b/src/Sylius/Behat/Context/Api/Admin/ManagingPromotionsContext.php index 7e97d2ddfb1..f96533cfa61 100644 --- a/src/Sylius/Behat/Context/Api/Admin/ManagingPromotionsContext.php +++ b/src/Sylius/Behat/Context/Api/Admin/ManagingPromotionsContext.php @@ -321,25 +321,27 @@ public function iAddAMinMaxPriceFilterRangeForChannel(ChannelInterface $channel, } /** - * @When I specify that this action should be applied to items from :taxon category + * @When I specify that this action should be applied to items from :taxon category for :channel channel */ - public function iSpecifyThatThisActionShouldBeAppliedToItemsFromCategory(TaxonInterface $taxon): void - { + public function iSpecifyThatThisActionShouldBeAppliedToItemsFromCategory( + TaxonInterface $taxon, + ChannelInterface $channel, + ): void { $actions = $this->getActions(); - $channelCode = key($actions[0]['configuration']); - $actions[0]['configuration'][$channelCode]['filters']['taxons_filter']['taxons'] = [$taxon->getCode()]; + $actions[0]['configuration'][$channel->getCode()]['filters']['taxons_filter']['taxons'] = [$taxon->getCode()]; $this->client->addRequestData('actions', $actions); } /** - * @When I specify that this action should be applied to the :product product + * @When I specify that this action should be applied to the :product product for :channel channel */ - public function iSpecifyThatThisActionShouldBeAppliedToTheProduct(ProductInterface $product): void - { + public function iSpecifyThatThisActionShouldBeAppliedToTheProduct( + ProductInterface $product, + ChannelInterface $channel, + ): void { $actions = $this->getActions(); - $channelCode = key($actions[0]['configuration']); - $actions[0]['configuration'][$channelCode]['filters']['products_filter']['products'] = [$product->getCode()]; + $actions[0]['configuration'][$channel->getCode()]['filters']['products_filter']['products'] = [$product->getCode()]; $this->client->addRequestData('actions', $actions); } @@ -562,6 +564,15 @@ public function promotionShouldNotExistInTheRegistry(PromotionInterface $promoti ); } + /** + * @Then the :promotionName promotion should be successfully created + */ + public function thePromotionShouldBeSuccessfullyCreated(string $promotionName): void + { + $this->iShouldBeNotifiedThatItHasBeenSuccessfullyCreated(); + $this->thePromotionShouldAppearInTheRegistry($promotionName); + } + /** * @Then I should be notified that it has been successfully created */ diff --git a/src/Sylius/Behat/Context/Ui/Admin/ManagingPromotionsContext.php b/src/Sylius/Behat/Context/Ui/Admin/ManagingPromotionsContext.php index 54b92f67362..e92a9cd8fdb 100644 --- a/src/Sylius/Behat/Context/Ui/Admin/ManagingPromotionsContext.php +++ b/src/Sylius/Behat/Context/Ui/Admin/ManagingPromotionsContext.php @@ -58,7 +58,7 @@ public function iWantToCreateANewPromotion(): void * @When I want to browse promotions * @When I browse promotions */ - public function iWantToBrowsePromotions() + public function iWantToBrowsePromotions(): void { $this->indexPage->open(); } @@ -88,7 +88,7 @@ public function iNameIt($name = null) */ public function iRemoveItsPriority(?int $priority = null): void { - $this->formElement->prioritizeIt($priority); + $this->formElement->setPriority($priority); } /** @@ -118,7 +118,7 @@ public function iAddIt() */ public function iSpecifyItsLabelInLocaleCode(string $label, string $localeCode): void { - $this->createPage->specifyLabel($label, $localeCode); + $this->formElement->setLabel($label, $localeCode); } /** @@ -126,7 +126,7 @@ public function iSpecifyItsLabelInLocaleCode(string $label, string $localeCode): */ public function iSpecifyItsLabelWithAStringExceedingTheLimitInLocale(string $localeCode): void { - $this->createPage->specifyLabel(str_repeat('a', 256), $localeCode); + $this->formElement->setLabel(str_repeat('a', 256), $localeCode); } /** @@ -135,7 +135,7 @@ public function iSpecifyItsLabelWithAStringExceedingTheLimitInLocale(string $loc public function thePromotionShouldHaveLabelInLocale(PromotionInterface $promotion, string $label, string $localeCode): void { $this->updatePage->open(['id' => $promotion->getId()]); - $this->createPage->hasLabel($label, $localeCode); + $this->formElement->hasLabel($label, $localeCode); } /** @@ -144,19 +144,19 @@ public function thePromotionShouldHaveLabelInLocale(PromotionInterface $promotio */ public function iAddTheHasTaxonRuleConfiguredWith(string ...$taxons): void { - $this->createPage->addRule('Has at least one from taxons'); + $this->formElement->addRule('Has at least one from taxons'); - $this->createPage->selectAutocompleteRuleOption('Taxons', $taxons, true); + $this->formElement->selectAutocompleteRuleOptions($taxons); } /** * @When /^I add the "Total price of items from taxon" rule configured with "([^"]+)" taxon and "(?:€|£|\$)([^"]+)" amount for ("[^"]+" channel)$/ */ - public function iAddTheRuleConfiguredWith($taxonName, $amount, ChannelInterface $channel) + public function iAddTheRuleConfiguredWith(string $taxonName, $amount, ChannelInterface $channel): void { - $this->createPage->addRule('Total price of items from taxon'); - $this->createPage->selectAutocompleteRuleOption('Taxon', $taxonName); - $this->createPage->fillRuleOptionForChannel($channel->getCode(), 'Amount', $amount); + $this->formElement->addRule('Total price of items from taxon'); + $this->formElement->selectAutocompleteRuleOptions([$taxonName], $channel->getCode()); + $this->formElement->fillRuleOptionForChannel($channel->getCode(), 'Amount', $amount); } /** @@ -168,9 +168,9 @@ public function iAddTheItemTotalRuleConfiguredWithTwoChannel( $secondAmount, ChannelInterface $secondChannel, ) { - $this->createPage->addRule('Item total'); - $this->createPage->fillRuleOptionForChannel($firstChannel->getCode(), 'Amount', $firstAmount); - $this->createPage->fillRuleOptionForChannel($secondChannel->getCode(), 'Amount', $secondAmount); + $this->formElement->addRule('Item total'); + $this->formElement->fillRuleOptionForChannel($firstChannel->getCode(), 'Amount', $firstAmount); + $this->formElement->fillRuleOptionForChannel($secondChannel->getCode(), 'Amount', $secondAmount); } /** @@ -178,8 +178,8 @@ public function iAddTheItemTotalRuleConfiguredWithTwoChannel( */ public function iAddTheActionConfiguredWithAmountForChannel($actionType, $amount, ChannelInterface $channel) { - $this->createPage->addAction($actionType); - $this->createPage->fillActionOptionForChannel($channel->getCode(), 'Amount', $amount); + $this->formElement->addAction($actionType); + $this->formElement->fillActionOptionForChannel($channel->getCode(), 'Amount', $amount); } /** @@ -187,7 +187,7 @@ public function iAddTheActionConfiguredWithAmountForChannel($actionType, $amount */ public function itIsConfiguredWithAmountForChannel($amount, ChannelInterface $channel) { - $this->createPage->fillActionOptionForChannel($channel->getCode(), 'Amount', $amount); + $this->formElement->fillActionOptionForChannel($channel->getCode(), 'Amount', $amount); } /** @@ -195,7 +195,7 @@ public function itIsConfiguredWithAmountForChannel($amount, ChannelInterface $ch */ public function iAddAMinPriceFilterRangeForChannel(ChannelInterface $channel, $minimum) { - $this->createPage->fillActionOptionForChannel($channel->getCode(), 'Min', $minimum); + $this->formElement->fillActionOptionForChannel($channel->getCode(), 'Min', $minimum); } /** @@ -203,7 +203,7 @@ public function iAddAMinPriceFilterRangeForChannel(ChannelInterface $channel, $m */ public function iAddAMaxPriceFilterRangeForChannel(ChannelInterface $channel, $maximum) { - $this->createPage->fillActionOptionForChannel($channel->getCode(), 'Max', $maximum); + $this->formElement->fillActionOptionForChannel($channel->getCode(), 'Max', $maximum); } /** @@ -216,11 +216,11 @@ public function iAddAMinMaxPriceFilterRangeForChannel(ChannelInterface $channel, } /** - * @When I specify that this action should be applied to items from :taxonName category + * @When I specify that this action should be applied to items from :taxonName category for :channel channel */ - public function iSpecifyThatThisActionShouldBeAppliedToItemsFromCategory($taxonName) + public function iSpecifyThatThisActionShouldBeAppliedToItemsFromCategory(string $taxonName, ChannelInterface $channel): void { - $this->createPage->selectAutoCompleteFilterOption('Taxons', $taxonName); + $this->formElement->selectAutocompleteActionFilterOptions([$taxonName], $channel->getCode(), 'taxons'); } /** @@ -231,8 +231,8 @@ public function iAddTheActionConfiguredWithAPercentageValueForChannel( string $percentage, ChannelInterface $channel, ): void { - $this->createPage->addAction($actionType); - $this->createPage->fillActionOptionForChannel($channel->getCode(), 'Percentage', $percentage); + $this->formElement->addAction($actionType); + $this->formElement->fillActionOptionForChannel($channel->getCode(), 'Percentage', $percentage); } /** @@ -242,8 +242,8 @@ public function iAddTheActionConfiguredWithoutAPercentageValueForChannel( string $actionType, ChannelInterface $channel, ): void { - $this->createPage->addAction($actionType); - $this->createPage->fillActionOptionForChannel($channel->getCode(), 'Percentage', ''); + $this->formElement->addAction($actionType); + $this->formElement->fillActionOptionForChannel($channel->getCode(), 'Percentage', ''); } /** @@ -252,8 +252,8 @@ public function iAddTheActionConfiguredWithoutAPercentageValueForChannel( */ public function iAddTheActionConfiguredWithAPercentageValue($actionType, $percentage = null) { - $this->createPage->addAction($actionType); - $this->createPage->fillActionOption('Percentage', $percentage ?? ''); + $this->formElement->addAction($actionType); + $this->formElement->fillActionOption('Percentage', $percentage ?? ''); } /** @@ -261,8 +261,8 @@ public function iAddTheActionConfiguredWithAPercentageValue($actionType, $percen */ public function iAddTheCustomerGroupRuleConfiguredForGroup($customerGroupName) { - $this->createPage->addRule('Customer group'); - $this->createPage->selectRuleOption('Customer group', $customerGroupName); + $this->formElement->addRule('Customer group'); + $this->formElement->selectRuleOption('Customer group', $customerGroupName); } /** @@ -273,6 +273,22 @@ public function iCheckThePromotion(string $promotionName): void $this->indexPage->checkResourceOnPage(['name' => $promotionName]); } + /** + * @When I remove its last rule + */ + public function iRemoveItsLastRule(): void + { + $this->formElement->removeLastRule(); + } + + /** + * @When I remove its last action + */ + public function iRemoveItsLastAction(): void + { + $this->formElement->removeLastAction(); + } + /** * @When I delete them */ @@ -354,7 +370,7 @@ public function iShouldBeNotifiedThatAMinimalValueShouldBeNumeric($element) */ public function iShouldBeNotifiedThatPromotionWithThisCodeAlreadyExists() { - Assert::same($this->createPage->getValidationMessage('code'), 'The promotion with given code already exists.'); + Assert::same($this->formElement->getValidationMessage('code'), 'The promotion with given code already exists.'); } /** @@ -380,18 +396,15 @@ public function thereShouldStillBeOnlyOnePromotionWith($element, $value) /** * @When I set its usage limit to :usageLimit */ - public function iSetItsUsageLimitTo($usageLimit) + public function iSetItsUsageLimitTo(int $usageLimit): void { - /** @var CreatePageInterface|UpdatePageInterface $currentPage */ - $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]); - - $currentPage->fillUsageLimit($usageLimit); + $this->formElement->setUsageLimit($usageLimit); } /** * @Then the :promotion promotion should be available to be used only :usageLimit times */ - public function thePromotionShouldBeAvailableToUseOnlyTimes(PromotionInterface $promotion, $usageLimit) + public function thePromotionShouldBeAvailableToUseOnlyTimes(PromotionInterface $promotion, int $usageLimit): void { $this->iWantToModifyAPromotion($promotion); @@ -403,10 +416,7 @@ public function thePromotionShouldBeAvailableToUseOnlyTimes(PromotionInterface $ */ public function iSetItAsExclusive(): void { - /** @var CreatePageInterface|UpdatePageInterface $currentPage */ - $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]); - - $currentPage->makeExclusive(); + $this->formElement->makeExclusive(); } /** @@ -414,16 +424,13 @@ public function iSetItAsExclusive(): void */ public function iSetItAsNotAppliesToDiscountedByCatalogPromotionItems(): void { - /** @var CreatePageInterface|UpdatePageInterface $currentPage */ - $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]); - - $currentPage->makeNotAppliesToDiscountedItem(); + $this->formElement->makeNotAppliesToDiscountedItem(); } /** * @Then the :promotion promotion should be exclusive */ - public function thePromotionShouldBeExclusive(PromotionInterface $promotion) + public function thePromotionShouldBeExclusive(PromotionInterface $promotion): void { $this->assertIfFieldIsTrue($promotion, 'exclusive'); } @@ -439,18 +446,15 @@ public function thePromotionShouldNotAppliesToDiscountedItems(PromotionInterface /** * @When I make it coupon based */ - public function iMakeItCouponBased() + public function iMakeItCouponBased(): void { - /** @var CreatePageInterface|UpdatePageInterface $currentPage */ - $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]); - - $currentPage->checkCouponBased(); + $this->formElement->makeCouponBased(); } /** * @Then the :promotion promotion should be coupon based */ - public function thePromotionShouldBeCouponBased(PromotionInterface $promotion) + public function thePromotionShouldBeCouponBased(PromotionInterface $promotion): void { $this->assertIfFieldIsTrue($promotion, 'coupon_based'); } @@ -458,18 +462,15 @@ public function thePromotionShouldBeCouponBased(PromotionInterface $promotion) /** * @When I make it applicable for the :channelName channel */ - public function iMakeItApplicableForTheChannel($channelName) + public function iMakeItApplicableForTheChannel(string $channelName): void { - /** @var CreatePageInterface|UpdatePageInterface $currentPage */ - $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]); - - $currentPage->checkChannel($channelName); + $this->formElement->checkChannel($channelName); } /** * @Then the :promotion promotion should be applicable for the :channelName channel */ - public function thePromotionShouldBeApplicableForTheChannel(PromotionInterface $promotion, $channelName) + public function thePromotionShouldBeApplicableForTheChannel(PromotionInterface $promotion, string $channelName): void { $this->iWantToModifyAPromotion($promotion); @@ -549,13 +550,10 @@ public function iShouldBeNotifiedOfFailure() /** * @When I make it available from :startsDate to :endsDate */ - public function iMakeItAvailableFromTo(\DateTimeInterface $startsDate, \DateTimeInterface $endsDate) + public function iMakeItAvailableFromTo(\DateTimeInterface $startsDate, \DateTimeInterface $endsDate): void { - /** @var CreatePageInterface|UpdatePageInterface $currentPage */ - $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]); - - $currentPage->setStartsAt($startsDate); - $currentPage->setEndsAt($endsDate); + $this->formElement->setStartsAt($startsDate); + $this->formElement->setEndsAt($endsDate); } /** @@ -566,7 +564,6 @@ public function thePromotionShouldBeAvailableFromTo(PromotionInterface $promotio $this->iWantToModifyAPromotion($promotion); Assert::true($this->updatePage->hasStartsAt($startsDate)); - Assert::true($this->updatePage->hasEndsAt($endsDate)); } @@ -575,19 +572,16 @@ public function thePromotionShouldBeAvailableFromTo(PromotionInterface $promotio */ public function iShouldBeNotifiedThatPromotionCannotEndBeforeItsEvenStarts(): void { - /** @var CreatePageInterface|UpdatePageInterface $currentPage */ - $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]); - - Assert::same($currentPage->getValidationMessage('ends_at'), 'End date cannot be set prior start date.'); + Assert::same($this->formElement->getValidationMessage('ends_at_date'), 'End date cannot be set prior start date.'); } /** * @Then I should be notified that this value should not be blank */ - public function iShouldBeNotifiedThatThisValueShouldNotBeBlank() + public function iShouldBeNotifiedThatThisValueShouldNotBeBlank(): void { Assert::same( - $this->createPage->getValidationMessageForAction(), + $this->formElement->getValidationMessageForAction(), 'This value should not be blank.', ); } @@ -600,7 +594,7 @@ public function iShouldBeNotifiedThatThisValueShouldNotBeBlank() public function iShouldBeNotifiedThatPercentageDiscountShouldBeBetween(): void { Assert::same( - $this->createPage->getValidationMessageForAction(), + $this->formElement->getValidationMessageForAction(), 'The percentage discount must be between 0% and 100%.', ); } @@ -621,18 +615,18 @@ public function thePromotionShouldBeUsedTime(PromotionInterface $promotion, $usa /** * @When I add the "Contains product" rule configured with the :productName product */ - public function iAddTheRuleConfiguredWithTheProduct($productName) + public function iAddTheRuleConfiguredWithTheProduct(string $productName): void { - $this->createPage->addRule('Contains product'); - $this->createPage->selectAutocompleteRuleOption('Product code', $productName); + $this->formElement->addRule('Contains product'); + $this->formElement->selectAutocompleteRuleOptions([$productName]); } /** - * @When I specify that this action should be applied to the :productName product + * @When I specify that this action should be applied to the :productName product for :channel channel */ - public function iSpecifyThatThisActionShouldBeAppliedToTheProduct($productName) + public function iSpecifyThatThisActionShouldBeAppliedToTheProduct(string $productName, ChannelInterface $channel): void { - $this->createPage->selectAutoCompleteFilterOption('Products', $productName); + $this->formElement->selectAutocompleteActionFilterOptions([$productName], $channel->getCode(), 'products'); } /** @@ -686,7 +680,7 @@ public function thePromotionsShouldHavePriority(PromotionInterface $promotion, i { $this->iWantToModifyAPromotion($promotion); - Assert::same($this->updatePage->getPriority(), $priority); + Assert::same($this->formElement->getPriority(), $priority); } /** @@ -759,15 +753,15 @@ public function iFilterPromotionsByCouponCodeEqual(string $value): void */ public function iAddANewRule() { - $this->createPage->addRule(null); + $this->formElement->addRule(null); } /** * @When I add a new action */ - public function iAddANewAction() + public function iAddANewAction(): void { - $this->createPage->addAction(null); + $this->formElement->addAction(null); } /** @@ -789,9 +783,17 @@ public function iRemoveTheRuleAmountForChannel(ChannelInterface $channel): void /** * @Then I should see the rule configuration form */ - public function iShouldSeeTheRuleConfigurationForm() + public function iShouldSeeTheRuleConfigurationForm(): void { - Assert::true($this->createPage->checkIfRuleConfigurationFormIsVisible(), 'Cart promotion rule configuration form is not visible.'); + Assert::true($this->formElement->checkIfRuleConfigurationFormIsVisible(), 'Cart promotion rule configuration form is not visible.'); + } + + /** + * @Then I should not see the rule configuration form + */ + public function iShouldNotSeeTheRuleConfigurationForm(): void + { + Assert::false($this->formElement->checkIfRuleConfigurationFormIsVisible(), 'Cart promotion rule configuration form is visible.'); } /** @@ -813,9 +815,17 @@ public function itShouldHaveOfItemPercentageDiscount(string $amount, ChannelInte /** * @Then I should see the action configuration form */ - public function iShouldSeeTheActionConfigurationForm() + public function iShouldSeeTheActionConfigurationForm(): void { - Assert::true($this->createPage->checkIfActionConfigurationFormIsVisible(), 'Cart promotion action configuration form is not visible.'); + Assert::true($this->formElement->checkIfActionConfigurationFormIsVisible(), 'Cart promotion action configuration form is not visible.'); + } + + /** + * @Then I should not see the action configuration form + */ + public function iShouldNotSeeTheActionConfigurationForm(): void + { + Assert::false($this->formElement->checkIfActionConfigurationFormIsVisible(), 'Cart promotion action configuration form is visible.'); } /** @@ -850,11 +860,8 @@ public function iShouldBeNotifiedThatPromotionsHaveBeenUpdated(PromotionInterfac */ public function iShouldBeNotifiedThatPromotionLabelIsTooLong(string $localeCode): void { - /** @var CreatePageInterface|UpdatePageInterface $currentPage */ - $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]); - Assert::same( - $currentPage->getValidationMessageForTranslation('label', $localeCode), + $this->formElement->getValidationMessageForTranslation('label', $localeCode), 'This value is too long. It should have 255 characters or less.', ); } @@ -883,12 +890,17 @@ public function iShouldBeViewingNonArchivalPromotions(): void Assert::false($this->indexPage->isArchivalFilterEnabled()); } - private function assertFieldValidationMessage(string $element, string $expectedMessage) + /** + * @Then the :promotion promotion should be successfully created + */ + public function thePromotionShouldBeSuccessfullyCreated(PromotionInterface $promotion): void { - /** @var CreatePageInterface|UpdatePageInterface $currentPage */ - $currentPage = $this->currentPageResolver->getCurrentPageWithForm([$this->createPage, $this->updatePage]); + $this->updatePage->verify(['id' => $promotion->getId()]); + } - Assert::same($currentPage->getValidationMessage($element), $expectedMessage); + private function assertFieldValidationMessage(string $element, string $expectedMessage) + { + Assert::same($this->formElement->getValidationMessage($element), $expectedMessage); } /** diff --git a/src/Sylius/Behat/Element/Admin/Crud/FormElement.php b/src/Sylius/Behat/Element/Admin/Crud/FormElement.php new file mode 100644 index 00000000000..1b41c771819 --- /dev/null +++ b/src/Sylius/Behat/Element/Admin/Crud/FormElement.php @@ -0,0 +1,47 @@ +getFieldElement($element); + if (null === $foundElement) { + throw new ElementNotFoundException($this->getSession(), 'Field element'); + } + + $validationMessage = $foundElement->find('css', '.invalid-feedback'); + if (null === $validationMessage) { + throw new ElementNotFoundException($this->getSession(), 'Validation message', 'css', '.invalid-feedback'); + } + + return $validationMessage->getText(); + } + + /** @throws ElementNotFoundException */ + private function getFieldElement(string $element): ?NodeElement + { + $element = $this->getElement($element); + while (null !== $element && !$element->hasClass('field')) { + $element = $element->getParent(); + } + + return $element; + } +} diff --git a/src/Sylius/Behat/Element/Admin/Crud/FormElementInterface.php b/src/Sylius/Behat/Element/Admin/Crud/FormElementInterface.php new file mode 100644 index 00000000000..364e3ec3731 --- /dev/null +++ b/src/Sylius/Behat/Element/Admin/Crud/FormElementInterface.php @@ -0,0 +1,22 @@ +getElement('priority')->getValue(); + } + + public function setPriority(?int $priority): void { $this->getElement('priority')->setValue($priority); } + public function setStartsAt(\DateTimeInterface $dateTime): void + { + $timestamp = $dateTime->getTimestamp(); + + $this->getElement('starts_at_date')->setValue(date('Y-m-d', $timestamp)); + $this->getElement('starts_at_time')->setValue(date('H:i', $timestamp)); + } + + public function setEndsAt(\DateTimeInterface $dateTime): void + { + $timestamp = $dateTime->getTimestamp(); + + $this->getElement('ends_at_date')->setValue(date('Y-m-d', $timestamp)); + $this->getElement('ends_at_time')->setValue(date('H:i', $timestamp)); + } + + public function setUsageLimit(int $limit): void + { + $this->getElement('usage_limit')->setValue($limit); + } + + public function makeExclusive(): void + { + $this->getElement('exclusive')->check(); + } + + public function makeNotAppliesToDiscountedItem(): void + { + $this->getElement('applies_to_discounted')->uncheck(); + } + + public function makeCouponBased(): void + { + $this->getElement('coupon_based')->check(); + } + + public function checkChannel(string $name): void + { + $this->getElement('channels')->checkField($name); + } + + public function setLabel(string $label, string $localeCode): void + { + $this->getElement('label', ['%locale_code%' => $localeCode])->setValue($label); + } + + public function hasLabel(string $label, string $localeCode): bool + { + return $label === $this->getElement('label', ['%locale_code%' => $localeCode])->getValue(); + } + + public function addAction(?string $actionName): void + { + $this->getElement('add_action_button')->press(); + $this->waitForFormUpdate(); + + if (null !== $actionName) { + $this->selectActionOption('Type', $actionName); + $this->waitForFormUpdate(); + } + } + + public function removeLastAction(): void + { + $this->getLastAction()->find('css', 'button[data-test-delete-action]')->press(); + $this->waitForFormUpdate(); + } + + public function fillActionOption(string $option, string $value): void + { + $this->getLastAction()->fillField($option, $value); + } + + public function fillActionOptionForChannel(string $channelCode, string $option, string $value): void + { + $lastAction = $this->getChannelConfigurationOfLastAction($channelCode); + $lastAction->fillField($option, $value); + } + + public function selectActionOption(string $option, string $value, bool $multiple = false): void + { + $this->getLastAction()->find('named', ['select', $option])->selectOption($value, $multiple); + } + + public function addRule(?string $ruleName): void + { + $this->getElement('add_rule_button')->press(); + $this->waitForFormUpdate(); + + if (null !== $ruleName) { + $this->selectRuleOption('Type', $ruleName); + $this->waitForFormUpdate(); + } + } + + public function removeLastRule(): void + { + $this->getLastRule()->find('css', 'button[data-test-delete-rule]')->press(); + $this->waitForFormUpdate(); + } + + public function selectRuleOption(string $option, string $value, bool $multiple = false): void + { + $this->getLastRule()->find('named', ['select', $option])->selectOption($value, $multiple); + } + + public function fillRuleOption(string $option, string $value): void + { + $this->getLastRule()->fillField($option, $value); + } + + public function fillRuleOptionForChannel(string $channelCode, string $option, string $value): void + { + $lastRule = $this->getChannelConfigurationOfLastRule($channelCode); + $lastRule->fillField($option, $value); + } + + public function selectAutocompleteRuleOptions(array $values, ?string $channelCode = null): void + { + $count = count($this->getElement('rules')->findAll('css', '[data-test-promotion-rule]')); + $locator = $channelCode ? + sprintf('#sylius_promotion_rules_%d_configuration_%s select', $count - 1, $channelCode) : + sprintf('#sylius_promotion_rules_%d_configuration select', $count - 1) + ; + foreach ($values as $value) { + $this->autocompleteHelper->selectByName( + $this->getDriver(), + $this->getLastRule()->find('css', $locator)->getXpath(), + $value, + ); + $this->waitForFormUpdate(); + } + } + + public function selectAutocompleteActionFilterOptions(array $values, string $channelCode, string $filterType): void + { + $count = count($this->getElement('actions')->findAll('css', '[data-test-promotion-action]')); + $locator = sprintf('#sylius_promotion_actions_%d_configuration_%s_filters_%s_filter select', $count - 1, $channelCode, $filterType); + foreach ($values as $value) { + $this->autocompleteHelper->selectByName( + $this->getDriver(), + $this->getLastAction()->find('css', $locator)->getXpath(), + $value, + ); + } + + $this->waitForFormUpdate(); + } + + public function checkIfRuleConfigurationFormIsVisible(): bool + { + return $this->hasElement('rule_count'); + } + + public function checkIfActionConfigurationFormIsVisible(): bool + { + return $this->hasElement('action_amount'); + } + + public function getValidationMessageForAction(): string + { + $actionForm = $this->getLastAction(); + + $foundElement = $actionForm->find('css', '.invalid-feedback'); + if (null === $foundElement) { + throw new ElementNotFoundException($this->getSession(), 'Tag', 'css', '.invalid-feedback'); + } + + return $foundElement->getText(); + } + + public function getValidationMessageForTranslation(string $element, string $localeCode): string + { + $foundElement = $this->getElement($element, ['%locale_code%' => $localeCode])->getParent(); + + $validationMessage = $foundElement->find('css', '.invalid-feedback'); + if (null === $validationMessage) { + throw new ElementNotFoundException($this->getSession(), 'Validation message', 'css', '.invalid-feedback'); + } + + return $validationMessage->getText(); + } + protected function getDefinedElements(): array { return array_merge(parent::getDefinedElements(), [ + 'action_amount' => '#sylius_promotion_actions_0_configuration_WEB-US_amount', + 'actions' => '#sylius_promotion_actions', + 'add_action_button' => '#sylius_promotion_actions_add', + 'add_rule_button' => '#sylius_promotion_rules_add', + 'applies_to_discounted' => '#sylius_promotion_appliesToDiscounted', + 'channels' => '#sylius_promotion_channels', + 'code' => '#sylius_promotion_code', + 'coupon_based' => '#sylius_promotion_couponBased', + 'ends_at_date' => '#sylius_promotion_endsAt_date', + 'ends_at_time' => '#sylius_promotion_endsAt_time', + 'exclusive' => '#sylius_promotion_exclusive', + 'form' => '[data-live-name-value="sylius_admin:promotion:form"]', + 'label' => '[name="sylius_promotion[translations][%locale_code%][label]"]', + 'minimum' => '#sylius_promotion_actions_0_configuration_WEB-US_filters_price_range_filter_min', + 'maximum' => '#sylius_promotion_actions_0_configuration_WEB-US_filters_price_range_filter_max', + 'name' => '#sylius_promotion_name', 'priority' => '#sylius_promotion_priority', + 'rule_count' => '#sylius_promotion_rules_0_configuration_count', + 'rules' => '#sylius_promotion_rules', + 'starts_at_date' => '#sylius_promotion_startsAt_date', + 'starts_at_time' => '#sylius_promotion_startsAt_time', + 'translation_tab' => '[data-test-promotion-translations-accordion="%locale_code%"]', + 'usage_limit' => '#sylius_promotion_usageLimit', ]); } + + private function getLastAction(): NodeElement + { + $items = $this->getElement('actions')->findAll('css', '[data-test-promotion-action]'); + Assert::notEmpty($items); + + return end($items); + } + + private function getChannelConfigurationOfLastAction(string $channelCode): NodeElement + { + $lastAction = $this->getLastAction(); + + TabsHelper::switchTab($this->getSession(), $lastAction, $channelCode); + + return $lastAction + ->find('css', sprintf('[id^="sylius_promotion_actions_"][id$="_configuration_%s"]', $channelCode)) + ; + } + + private function getLastRule(): NodeElement + { + $items = $this->getElement('rules')->findAll('css', '[data-test-promotion-rule]'); + Assert::notEmpty($items); + + return end($items); + } + + private function getChannelConfigurationOfLastRule(string $channelCode): NodeElement + { + $lastRule = $this->getLastRule(); + + TabsHelper::switchTab($this->getSession(), $lastRule, $channelCode); + + return $lastRule->find( + 'css', + sprintf('[id^="sylius_promotion_rules_"][id$="_configuration_%s"]', $channelCode), + ); + } + + private function waitForFormUpdate(): void + { + $form = $this->getElement('form'); + sleep(1); // we need to sleep, as sometimes the check below is executed faster than the form sets the busy attribute + $form->waitFor(1500, function () use ($form) { + return !$form->hasAttribute('busy'); + }); + } } diff --git a/src/Sylius/Behat/Element/Admin/Promotion/FormElementInterface.php b/src/Sylius/Behat/Element/Admin/Promotion/FormElementInterface.php index 55c792c454e..bdabe4b5b4b 100644 --- a/src/Sylius/Behat/Element/Admin/Promotion/FormElementInterface.php +++ b/src/Sylius/Behat/Element/Admin/Promotion/FormElementInterface.php @@ -13,7 +13,61 @@ namespace Sylius\Behat\Element\Admin\Promotion; -interface FormElementInterface +use Sylius\Behat\Element\Admin\Crud\FormElementInterface as BaseFormElementInterface; + +interface FormElementInterface extends BaseFormElementInterface { - public function prioritizeIt(?int $priority): void; + public function setPriority(?int $priority): void; + + public function getPriority(): int; + + public function setStartsAt(\DateTimeInterface $dateTime): void; + + public function setEndsAt(\DateTimeInterface $dateTime): void; + + public function setUsageLimit(int $limit): void; + + public function makeExclusive(): void; + + public function makeNotAppliesToDiscountedItem(): void; + + public function makeCouponBased(): void; + + public function checkChannel(string $name): void; + + public function setLabel(string $label, string $localeCode): void; + + public function hasLabel(string $label, string $localeCode): bool; + + public function addAction(?string $actionName): void; + + public function removeLastAction(): void; + + public function fillActionOption(string $option, string $value): void; + + public function fillActionOptionForChannel(string $channelCode, string $option, string $value): void; + + public function selectActionOption(string $option, string $value, bool $multiple = false): void; + + public function addRule(?string $ruleName): void; + + public function removeLastRule(): void; + + public function selectRuleOption(string $option, string $value, bool $multiple = false): void; + + public function fillRuleOption(string $option, string $value): void; + + public function fillRuleOptionForChannel(string $channelCode, string $option, string $value): void; + + public function selectAutocompleteRuleOptions(array $values, ?string $channelCode = null): void; + + public function selectAutocompleteActionFilterOptions(array $values, string $channelCode, string $filterType): void; + + public function checkIfRuleConfigurationFormIsVisible(): bool; + + public function checkIfActionConfigurationFormIsVisible(): bool; + + public function getValidationMessageForAction(): string; + + public function getValidationMessageForTranslation(string $element, string $localeCode): string; } diff --git a/src/Sylius/Behat/Page/Admin/Product/FormTrait.php b/src/Sylius/Behat/Page/Admin/Product/FormTrait.php index f5008bffc79..d0cfa1ba282 100644 --- a/src/Sylius/Behat/Page/Admin/Product/FormTrait.php +++ b/src/Sylius/Behat/Page/Admin/Product/FormTrait.php @@ -43,7 +43,7 @@ public function getDefinedFormElements(): array 'product_attribute_input' => 'input[name="product_attributes"]', 'product_attribute_tab' => '[data-test-product-attribute-tab="%name%"]', 'product_options_autocomplete' => '[data-test-product-options-autocomplete]', - 'product_translation_accordion' => '[data-test-product-translation-accordion="%localeCode%"]', + 'product_translation_accordion' => '[data-test-product-translations-accordion="%localeCode%"]', 'side_navigation_tab' => '[data-test-side-navigation-tab="%name%"]', ]; } diff --git a/src/Sylius/Behat/Page/Admin/Promotion/CreatePage.php b/src/Sylius/Behat/Page/Admin/Promotion/CreatePage.php index 7347b83199e..4c735f29702 100644 --- a/src/Sylius/Behat/Page/Admin/Promotion/CreatePage.php +++ b/src/Sylius/Behat/Page/Admin/Promotion/CreatePage.php @@ -18,249 +18,44 @@ use Sylius\Behat\Behaviour\NamesIt; use Sylius\Behat\Behaviour\SpecifiesItsField; use Sylius\Behat\Page\Admin\Crud\CreatePage as BaseCreatePage; -use Sylius\Behat\Service\AutocompleteHelper; -use Sylius\Behat\Service\TabsHelper; -use Webmozart\Assert\Assert; class CreatePage extends BaseCreatePage implements CreatePageInterface { use NamesIt; use SpecifiesItsField; - public function specifyLabel(string $label, string $localeCode): void + public function getValidationMessage(string $element): string { - $this->getDocument()->find('css', 'div[data-locale="' . $localeCode . '"]')->click(); - - $this->getDocument()->fillField(sprintf('sylius_promotion_translations_%s_label', $localeCode), $label); - } - - public function addRule(?string $ruleName): void - { - $count = count($this->getCollectionItems('rules')); - - $this->getDocument()->clickLink('Add rule'); - - $this->getDocument()->waitFor(5, fn () => $count + 1 === count($this->getCollectionItems('rules'))); - - if (null !== $ruleName) { - $this->selectRuleOption('Type', $ruleName); - } - } - - public function selectRuleOption(string $option, string $value, bool $multiple = false): void - { - $this->getLastCollectionItem('rules')->find('named', ['select', $option])->selectOption($value, $multiple); - } - - public function selectAutocompleteRuleOption(string $option, $value, bool $multiple = false): void - { - $option = strtolower(str_replace(' ', '_', $option)); - - $ruleAutocomplete = $this - ->getLastCollectionItem('rules') - ->find('css', sprintf('input[type="hidden"][name*="[%s]"]', $option)) - ->getParent() - ; - - if ($multiple && is_array($value)) { - AutocompleteHelper::chooseValues($this->getSession(), $ruleAutocomplete, $value); - - return; - } - - AutocompleteHelper::chooseValue($this->getSession(), $ruleAutocomplete, $value); - } - - public function fillRuleOption(string $option, string $value): void - { - $this->getLastCollectionItem('rules')->fillField($option, $value); - } - - public function fillRuleOptionForChannel(string $channelCode, string $option, string $value): void - { - $lastAction = $this->getChannelConfigurationOfLastRule($channelCode); - $lastAction->fillField($option, $value); - } - - public function addAction(?string $actionName): void - { - $count = count($this->getCollectionItems('actions')); - - $this->getDocument()->clickLink('Add action'); - - $this->getDocument()->waitFor(5, fn () => $count + 1 === count($this->getCollectionItems('actions'))); - - if (null !== $actionName) { - $this->selectActionOption('Type', $actionName); - } - } - - public function selectActionOption(string $option, string $value, bool $multiple = false): void - { - $this->getLastCollectionItem('actions')->find('named', ['select', $option])->selectOption($value, $multiple); - } - - public function fillActionOption(string $option, string $value): void - { - $this->getLastCollectionItem('actions')->fillField($option, $value); - } - - public function fillActionOptionForChannel(string $channelCode, string $option, string $value): void - { - $lastAction = $this->getChannelConfigurationOfLastAction($channelCode); - $lastAction->fillField($option, $value); - } - - public function fillUsageLimit(string $limit): void - { - $this->getDocument()->fillField('Usage limit', $limit); - } - - public function makeExclusive(): void - { - $this->getDocument()->checkField('Exclusive'); - } - - public function makeNotAppliesToDiscountedItem(): void - { - $this->getDocument()->unCheckField('Applies to already discounted order items'); - } - - public function checkCouponBased(): void - { - $this->getDocument()->checkField('Coupon based'); - } - - public function checkChannel(string $name): void - { - $this->getDocument()->checkField($name); - } - - public function setStartsAt(\DateTimeInterface $dateTime): void - { - $timestamp = $dateTime->getTimestamp(); - - $this->getDocument()->fillField('sylius_promotion_startsAt_date', date('Y-m-d', $timestamp)); - $this->getDocument()->fillField('sylius_promotion_startsAt_time', date('H:i', $timestamp)); - } - - public function setEndsAt(\DateTimeInterface $dateTime): void - { - $timestamp = $dateTime->getTimestamp(); - - $this->getDocument()->fillField('sylius_promotion_endsAt_date', date('Y-m-d', $timestamp)); - $this->getDocument()->fillField('sylius_promotion_endsAt_time', date('H:i', $timestamp)); - } - - public function getValidationMessageForAction(): string - { - $actionForm = $this->getLastCollectionItem('actions'); - - $foundElement = $actionForm->find('css', '.sylius-validation-error'); + $foundElement = $this->getFieldElement($element); if (null === $foundElement) { - throw new ElementNotFoundException($this->getSession(), 'Tag', 'css', '.sylius-validation-error'); + throw new ElementNotFoundException($this->getSession(), 'Field element'); } - return $foundElement->getText(); - } - - public function selectAutoCompleteFilterOption(string $option, $value, bool $multiple = false): void - { - $option = strtolower(str_replace(' ', '_', $option)); - - $filterAutocomplete = $this - ->getLastCollectionItem('actions') - ->find('css', sprintf('input[type="hidden"][name*="[%s_filter]"]', $option)) - ->getParent() - ; - - if ($multiple && is_array($value)) { - AutocompleteHelper::chooseValues($this->getSession(), $filterAutocomplete, $value); - - return; + $validationMessage = $foundElement->find('css', '.invalid-feedback'); + if (null === $validationMessage) { + throw new ElementNotFoundException($this->getSession(), 'Validation message', 'css', '.invalid-feedback'); } - AutocompleteHelper::chooseValue($this->getSession(), $filterAutocomplete, $value); - } - - public function checkIfRuleConfigurationFormIsVisible(): bool - { - return $this->hasElement('count'); - } - - public function checkIfActionConfigurationFormIsVisible(): bool - { - return $this->hasElement('amount'); - } - - public function hasLabel(string $label, string $localeCode): bool - { - $this->getDocument()->find('css', 'div[data-locale="' . $localeCode . '"]')->click(); - - $labelElement = $this->getDocument()->find('css', sprintf('label:contains("%s")', $label)); - if (null === $labelElement) { - return false; - } - - return $labelElement->hasClass(sprintf('sylius-locale-%s', $localeCode)); + return $validationMessage->getText(); } protected function getDefinedElements(): array { - return [ - 'actions' => '#sylius_promotion_actions', + return array_merge(parent::getDefinedElements(), [ 'code' => '#sylius_promotion_code', - 'ends_at' => '#sylius_promotion_endsAt', - 'minimum' => '#sylius_promotion_actions_0_configuration_WEB-US_filters_price_range_filter_min', - 'maximum' => '#sylius_promotion_actions_0_configuration_WEB-US_filters_price_range_filter_max', - 'name' => '#sylius_promotion_name', + 'form' => '[data-live-name-value="sylius_admin:promotion:form"]', 'rules' => '#sylius_promotion_rules', - 'starts_at' => '#sylius_promotion_startsAt', - 'count' => '#sylius_promotion_rules_0_configuration_count', - 'amount' => '#sylius_promotion_actions_0_configuration_WEB-US_amount', - ]; - } - - private function getChannelConfigurationOfLastAction(string $channelCode): NodeElement - { - $lastAction = $this->getLastCollectionItem('actions'); - - TabsHelper::switchTab($this->getSession(), $lastAction, $channelCode); - - return $lastAction - ->find('css', sprintf('[id^="sylius_promotion_actions_"][id$="_configuration_%s"]', $channelCode)) - ; + ]); } - private function getChannelConfigurationOfLastRule(string $channelCode): NodeElement + /** @throws ElementNotFoundException */ + private function getFieldElement(string $element): ?NodeElement { - $lastRule = $this->getLastCollectionItem('rules'); - - TabsHelper::switchTab($this->getSession(), $lastRule, $channelCode); - - return $lastRule - ->find('css', sprintf('[id^="sylius_promotion_rules_"][id$="_configuration_%s"]', $channelCode)) - ; - } - - private function getLastCollectionItem(string $collection): NodeElement - { - $items = $this->getCollectionItems($collection); - - Assert::notEmpty($items); - - return end($items); - } - - /** - * @return NodeElement[] - */ - private function getCollectionItems(string $collection): array - { - $items = $this->getElement($collection)->findAll('css', 'div[data-form-collection="item"]'); - - Assert::isArray($items); + $element = $this->getElement($element); + while (null !== $element && !$element->hasClass('field')) { + $element = $element->getParent(); + } - return $items; + return $element; } } diff --git a/src/Sylius/Behat/Page/Admin/Promotion/CreatePageInterface.php b/src/Sylius/Behat/Page/Admin/Promotion/CreatePageInterface.php index 30a0fb094a2..c1102bfd662 100644 --- a/src/Sylius/Behat/Page/Admin/Promotion/CreatePageInterface.php +++ b/src/Sylius/Behat/Page/Admin/Promotion/CreatePageInterface.php @@ -13,65 +13,13 @@ namespace Sylius\Behat\Page\Admin\Promotion; -use Behat\Mink\Exception\ElementNotFoundException; use Sylius\Behat\Page\Admin\Crud\CreatePageInterface as BaseCreatePageInterface; interface CreatePageInterface extends BaseCreatePageInterface { public function specifyCode(string $code): void; - public function specifyLabel(string $label, string $localeCode): void; - public function nameIt(string $name): void; - public function addRule(?string $ruleName): void; - - public function selectRuleOption(string $option, string $value, bool $multiple = false): void; - - /** - * @param string|string[] $value - */ - public function selectAutocompleteRuleOption(string $option, array|string $value, bool $multiple = false): void; - - public function fillRuleOption(string $option, string $value): void; - - public function fillRuleOptionForChannel(string $channelCode, string $option, string $value): void; - - public function addAction(?string $actionName): void; - - public function selectActionOption(string $option, string $value, bool $multiple = false): void; - - public function fillActionOption(string $option, string $value): void; - - public function fillActionOptionForChannel(string $channelCode, string $option, string $value): void; - - public function fillUsageLimit(string $limit): void; - - public function makeExclusive(): void; - - public function makeNotAppliesToDiscountedItem(): void; - - public function checkCouponBased(): void; - - public function checkChannel(string $name): void; - - public function setStartsAt(\DateTimeInterface $dateTime): void; - - public function setEndsAt(\DateTimeInterface $dateTime): void; - - /** - * @throws ElementNotFoundException - */ - public function getValidationMessageForAction(): string; - - /** - * @param string|string[] $value - */ - public function selectAutoCompleteFilterOption(string $option, array|string $value, bool $multiple = false): void; - - public function checkIfRuleConfigurationFormIsVisible(): bool; - - public function checkIfActionConfigurationFormIsVisible(): bool; - - public function hasLabel(string $label, string $localeCode): bool; + public function getValidationMessage(string $element): string; } diff --git a/src/Sylius/Behat/Page/Admin/Promotion/UpdatePage.php b/src/Sylius/Behat/Page/Admin/Promotion/UpdatePage.php index f9072e2889e..ec46e6cf4ef 100644 --- a/src/Sylius/Behat/Page/Admin/Promotion/UpdatePage.php +++ b/src/Sylius/Behat/Page/Admin/Promotion/UpdatePage.php @@ -14,7 +14,6 @@ namespace Sylius\Behat\Page\Admin\Promotion; use Behat\Mink\Element\NodeElement; -use Behat\Mink\Exception\ElementNotFoundException; use Sylius\Behat\Behaviour\ChecksCodeImmutability; use Sylius\Behat\Behaviour\CountsChannelBasedErrors; use Sylius\Behat\Behaviour\NamesIt; @@ -26,16 +25,6 @@ class UpdatePage extends BaseUpdatePage implements UpdatePageInterface use CountsChannelBasedErrors; use NamesIt; - public function setPriority(?int $priority): void - { - $this->getDocument()->fillField('Priority', $priority); - } - - public function getPriority(): int - { - return (int) $this->getElement('priority')->getValue(); - } - public function checkChannelsState(string $channelName): bool { $field = $this->getDocument()->findField($channelName); @@ -43,42 +32,6 @@ public function checkChannelsState(string $channelName): bool return (bool) $field->getValue(); } - public function fillUsageLimit(string $limit): void - { - $this->getDocument()->fillField('Usage limit', $limit); - } - - public function makeExclusive(): void - { - $this->getDocument()->checkField('Exclusive'); - } - - public function checkCouponBased(): void - { - $this->getDocument()->checkField('Coupon based'); - } - - public function checkChannel(string $name): void - { - $this->getDocument()->checkField($name); - } - - public function setStartsAt(\DateTimeInterface $dateTime): void - { - $timestamp = $dateTime->getTimestamp(); - - $this->getDocument()->fillField('sylius_promotion_startsAt_date', date('Y-m-d', $timestamp)); - $this->getDocument()->fillField('sylius_promotion_startsAt_time', date('H:i', $timestamp)); - } - - public function setEndsAt(\DateTimeInterface $dateTime): void - { - $timestamp = $dateTime->getTimestamp(); - - $this->getDocument()->fillField('sylius_promotion_endsAt_date', date('Y-m-d', $timestamp)); - $this->getDocument()->fillField('sylius_promotion_endsAt_time', date('H:i', $timestamp)); - } - public function hasStartsAt(\DateTimeInterface $dateTime): bool { $timestamp = $dateTime->getTimestamp(); @@ -167,21 +120,6 @@ public function getRuleValidationErrorsCount(string $channelCode): int return $this->countChannelErrors($this->getElement('rules'), $channelCode); } - /** - * @throws ElementNotFoundException - */ - public function getValidationMessageForTranslation(string $element, string $localeCode): string - { - $foundElement = $this->getElement($element, ['%localeCode%' => $localeCode])->getParent(); - - $validationMessage = $foundElement->find('css', '.sylius-validation-error'); - if (null === $validationMessage) { - throw new ElementNotFoundException($this->getSession(), 'Validation message', 'css', '.sylius-validation-error'); - } - - return $validationMessage->getText(); - } - protected function getCodeElement(): NodeElement { return $this->getElement('code'); @@ -189,26 +127,25 @@ protected function getCodeElement(): NodeElement protected function getDefinedElements(): array { - return [ + return array_merge(parent::getDefinedElements(), [ 'action_field' => '[id^="sylius_promotion_actions_"][id$="_configuration_%channelCode%_%field%"]', 'actions' => '#actions', 'applies_to_discounted' => '#sylius_promotion_appliesToDiscounted', 'code' => '#sylius_promotion_code', - 'coupon_based' => '#sylius_promotion_couponBased', 'ends_at' => '#sylius_promotion_endsAt', 'ends_at_date' => '#sylius_promotion_endsAt_date', 'ends_at_time' => '#sylius_promotion_endsAt_time', 'exclusive' => '#sylius_promotion_exclusive', - 'label' => '#sylius_promotion_translations_%localeCode%_label', + 'coupon_based' => '#sylius_promotion_couponBased', 'name' => '#sylius_promotion_name', 'order_percentage_action_field' => '[id^="sylius_promotion_actions_"][id$="_configuration_percentage"]', 'priority' => '#sylius_promotion_priority', 'rule_amount' => '[id^="sylius_promotion_rules_"][id$="_configuration_%channelCode%_amount"]', 'rules' => '#rules', + 'usage_limit' => '#sylius_promotion_usageLimit', 'starts_at' => '#sylius_promotion_startsAt', 'starts_at_date' => '#sylius_promotion_startsAt_date', 'starts_at_time' => '#sylius_promotion_startsAt_time', - 'usage_limit' => '#sylius_promotion_usageLimit', - ]; + ]); } } diff --git a/src/Sylius/Behat/Page/Admin/Promotion/UpdatePageInterface.php b/src/Sylius/Behat/Page/Admin/Promotion/UpdatePageInterface.php index 3f3804b1ff4..be0d90f9675 100644 --- a/src/Sylius/Behat/Page/Admin/Promotion/UpdatePageInterface.php +++ b/src/Sylius/Behat/Page/Admin/Promotion/UpdatePageInterface.php @@ -13,33 +13,16 @@ namespace Sylius\Behat\Page\Admin\Promotion; -use Behat\Mink\Exception\ElementNotFoundException; use Sylius\Behat\Page\Admin\Crud\UpdatePageInterface as BaseUpdatePageInterface; interface UpdatePageInterface extends BaseUpdatePageInterface { - public function setPriority(?int $priority): void; - - public function getPriority(): int; - public function nameIt(string $name): void; public function checkChannelsState(string $channelName): bool; public function isCodeDisabled(): bool; - public function fillUsageLimit(string $limit): void; - - public function makeExclusive(): void; - - public function checkCouponBased(): void; - - public function checkChannel(string $name): void; - - public function setStartsAt(\DateTimeInterface $dateTime): void; - - public function setEndsAt(\DateTimeInterface $dateTime): void; - public function hasStartsAt(\DateTimeInterface $dateTime): bool; public function hasEndsAt(\DateTimeInterface $dateTime): bool; @@ -65,9 +48,4 @@ public function removeRuleAmount(string $channelCode): void; public function getActionValidationErrorsCount(string $channelCode): int; public function getRuleValidationErrorsCount(string $channelCode): int; - - /** - * @throws ElementNotFoundException - */ - public function getValidationMessageForTranslation(string $element, string $localeCode): string; } diff --git a/src/Sylius/Behat/Resources/config/services/elements/admin.xml b/src/Sylius/Behat/Resources/config/services/elements/admin.xml index 65c413b8487..b9c3ccbe71a 100644 --- a/src/Sylius/Behat/Resources/config/services/elements/admin.xml +++ b/src/Sylius/Behat/Resources/config/services/elements/admin.xml @@ -16,6 +16,8 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd" > + + @@ -55,7 +57,9 @@ + parent="sylius.behat.element.admin.crud.form" + > + + diff --git a/src/Sylius/Behat/Service/TabsHelper.php b/src/Sylius/Behat/Service/TabsHelper.php index 8adf746d2b9..50597bf6e79 100644 --- a/src/Sylius/Behat/Service/TabsHelper.php +++ b/src/Sylius/Behat/Service/TabsHelper.php @@ -27,15 +27,13 @@ public static function switchTab(Session $session, NodeElement $tabsContainer, s return; } - $tab = $tabsContainer->find('css', sprintf('.item[data-tab*="%s"]', $dataTabHook)); + $tab = $tabsContainer->find('css', sprintf('[data-test-tab*="%s"]', $dataTabHook)); if ($tab->hasClass('active')) { return; } $tab->click(); - $tabContent = $tabsContainer->find('css', sprintf('.tab[data-tab*="%s"]', $dataTabHook)); - - $session->getPage()->waitFor(5, fn () => $tabContent->isVisible()); + $session->getPage()->waitFor(5, fn () => $tab->hasClass('active')); } } diff --git a/src/Sylius/Bundle/AdminBundle/Autocompleter/ProductAutocompleter.php b/src/Sylius/Bundle/AdminBundle/Autocompleter/ProductAutocompleter.php new file mode 100644 index 00000000000..17530dbcd81 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Autocompleter/ProductAutocompleter.php @@ -0,0 +1,58 @@ +productClass; + } + + /** @param EntityRepository $repository */ + public function createFilteredQueryBuilder(EntityRepository $repository, string $query): QueryBuilder + { + return $repository->createQueryBuilder('o'); + } + + public function getLabel(object $entity): string + { + return $entity->getName(); + } + + public function getValue(object $entity): mixed + { + return $entity->getCode(); + } + + public function isGranted(Security $security): bool + { + return true; + } + + /** @param array $options */ + public function setOptions(array $options): void + { + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Autocompleter/TaxonAutocompleter.php b/src/Sylius/Bundle/AdminBundle/Autocompleter/TaxonAutocompleter.php index 8ccc9b868b4..4d247b79ffc 100644 --- a/src/Sylius/Bundle/AdminBundle/Autocompleter/TaxonAutocompleter.php +++ b/src/Sylius/Bundle/AdminBundle/Autocompleter/TaxonAutocompleter.php @@ -38,7 +38,7 @@ public function createFilteredQueryBuilder(EntityRepository $repository, string public function getLabel(object $entity): string { - return $entity->getFullName(); + return $entity->getFullname(); } public function getValue(object $entity): mixed diff --git a/src/Sylius/Bundle/AdminBundle/Form/Extension/Promotion/Filter/ProductFilterConfigurationTypeExtension.php b/src/Sylius/Bundle/AdminBundle/Form/Extension/Promotion/Filter/ProductFilterConfigurationTypeExtension.php new file mode 100644 index 00000000000..8556def5fd0 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Form/Extension/Promotion/Filter/ProductFilterConfigurationTypeExtension.php @@ -0,0 +1,47 @@ + $productsToCodesTransformer */ + public function __construct(private readonly DataTransformerInterface $productsToCodesTransformer) + { + } + + /** @param array $options */ + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder + ->add('products', ProductAutocompleteChoiceType::class, [ + 'label' => 'sylius.form.promotion_filter.products', + 'multiple' => true, + ]) + ->get('products')->addModelTransformer($this->productsToCodesTransformer) + ; + } + + /** @return iterable */ + public static function getExtendedTypes(): iterable + { + return [ProductFilterConfigurationType::class]; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Form/Extension/Promotion/Filter/TaxonFilterConfigurationTypeExtension.php b/src/Sylius/Bundle/AdminBundle/Form/Extension/Promotion/Filter/TaxonFilterConfigurationTypeExtension.php new file mode 100644 index 00000000000..da550dd432b --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Form/Extension/Promotion/Filter/TaxonFilterConfigurationTypeExtension.php @@ -0,0 +1,48 @@ + $taxonsToCodesTransformer */ + public function __construct(private readonly DataTransformerInterface $taxonsToCodesTransformer) + { + } + + /** @param array $options */ + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder + ->add('taxons', TaxonAutocompleteChoiceType::class, [ + 'label' => 'sylius.form.promotion_filter.taxons', + 'multiple' => true, + 'required' => false, + ]) + ->get('taxons')->addModelTransformer($this->taxonsToCodesTransformer) + ; + } + + /** @return iterable */ + public static function getExtendedTypes(): iterable + { + return [TaxonFilterConfigurationType::class]; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Form/Extension/Promotion/Rule/ContainsProductConfigurationTypeExtension.php b/src/Sylius/Bundle/AdminBundle/Form/Extension/Promotion/Rule/ContainsProductConfigurationTypeExtension.php new file mode 100644 index 00000000000..325572bd0b1 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Form/Extension/Promotion/Rule/ContainsProductConfigurationTypeExtension.php @@ -0,0 +1,53 @@ + $productRepository */ + public function __construct(private readonly ProductRepositoryInterface $productRepository) + { + } + + /** @param array $options */ + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder + ->add('product_code', ProductAutocompleteChoiceType::class, [ + 'label' => 'sylius.form.promotion_action.add_product_configuration.product', + 'row_attr' => [ + 'data-skip-morph' => '', + ], + ]) + ->get('product_code')->addModelTransformer( + new ReversedTransformer(new ResourceToIdentifierTransformer($this->productRepository, 'code')), + ) + ; + } + + /** @return iterable */ + public static function getExtendedTypes(): iterable + { + return [ContainsProductConfigurationType::class]; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Form/Extension/Promotion/Rule/HasTaxonConfigurationTypeExtension.php b/src/Sylius/Bundle/AdminBundle/Form/Extension/Promotion/Rule/HasTaxonConfigurationTypeExtension.php new file mode 100644 index 00000000000..9699171ceda --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Form/Extension/Promotion/Rule/HasTaxonConfigurationTypeExtension.php @@ -0,0 +1,50 @@ + $taxonsToCodesTransformer */ + public function __construct(private readonly DataTransformerInterface $taxonsToCodesTransformer) + { + } + + /** @param array $options */ + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder + ->add('taxons', TaxonAutocompleteChoiceType::class, [ + 'label' => 'sylius.form.promotion_rule.has_taxon.taxons', + 'multiple' => true, + 'row_attr' => [ + 'data-skip-morph' => '', + ], + ]) + ->get('taxons')->addModelTransformer($this->taxonsToCodesTransformer) + ; + } + + /** @return iterable */ + public static function getExtendedTypes(): iterable + { + return [HasTaxonConfigurationType::class]; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Form/Extension/Promotion/Rule/TotalOfItemsFromTaxonConfigurationTypeExtension.php b/src/Sylius/Bundle/AdminBundle/Form/Extension/Promotion/Rule/TotalOfItemsFromTaxonConfigurationTypeExtension.php new file mode 100644 index 00000000000..750643c6261 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Form/Extension/Promotion/Rule/TotalOfItemsFromTaxonConfigurationTypeExtension.php @@ -0,0 +1,53 @@ + $taxonRepository */ + public function __construct(private readonly TaxonRepositoryInterface $taxonRepository) + { + } + + /** @param array $options */ + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder + ->add('taxon', TaxonAutocompleteChoiceType::class, [ + 'label' => 'sylius.form.promotion_rule.total_of_items_from_taxon.taxon', + 'row_attr' => [ + 'data-skip-morph' => '', + ], + ]) + ->get('taxon')->addModelTransformer( + new ReversedTransformer(new ResourceToIdentifierTransformer($this->taxonRepository, 'code')), + ) + ; + } + + /** @return iterable */ + public static function getExtendedTypes(): iterable + { + return [TotalOfItemsFromTaxonConfigurationType::class]; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Form/Extension/PromotionTypeExtension.php b/src/Sylius/Bundle/AdminBundle/Form/Extension/PromotionTypeExtension.php new file mode 100644 index 00000000000..e03af88e38b --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Form/Extension/PromotionTypeExtension.php @@ -0,0 +1,55 @@ + $options */ + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder + ->add('rules', LiveCollectionType::class, [ + 'entry_type' => PromotionRuleType::class, + 'allow_add' => true, + 'allow_delete' => true, + 'by_reference' => false, + 'button_add_options' => [ + 'label' => 'sylius.ui.add_rule', + ], + ]) + ->add('actions', LiveCollectionType::class, [ + 'entry_type' => PromotionActionType::class, + 'allow_add' => true, + 'allow_delete' => true, + 'by_reference' => false, + 'button_add_options' => [ + 'label' => 'sylius.ui.add_action', + ], + ]) + ; + } + + /** @return iterable */ + public static function getExtendedTypes(): iterable + { + return [PromotionType::class]; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Form/Type/ProductAutocompleteChoiceType.php b/src/Sylius/Bundle/AdminBundle/Form/Type/ProductAutocompleteChoiceType.php new file mode 100644 index 00000000000..73d14128fbc --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Form/Type/ProductAutocompleteChoiceType.php @@ -0,0 +1,47 @@ +setDefaults([ + 'class' => $this->productClass, + 'choice_name' => 'name', + 'choice_value' => 'code', + ]); + } + + public function getBlockPrefix(): string + { + return 'sylius_admin_product_autocomplete_choice'; + } + + public function getParent(): string + { + return BaseEntityAutocompleteType::class; + } +} diff --git a/src/Sylius/Bundle/AdminBundle/Resources/config/app/config.yml b/src/Sylius/Bundle/AdminBundle/Resources/config/app/config.yml index 4014da4db92..344389d905d 100644 --- a/src/Sylius/Bundle/AdminBundle/Resources/config/app/config.yml +++ b/src/Sylius/Bundle/AdminBundle/Resources/config/app/config.yml @@ -58,7 +58,7 @@ sylius_admin: show: '@SyliusAdmin/Shared/Grid/ItemAction/_show.html.twig' update: '@SyliusAdmin/Shared/Grid/ItemAction/_update.html.twig' ship_with_tracking_code: '@SyliusAdmin/Shipment/Grid/Action/shipWithTrackingCode.html.twig' - delete_catalog_promotion: "@SyliusAdmin/Promotion/Grid/Action/deleteCatalogPromotion.html.twig" + delete_catalog_promotion: "@SyliusAdmin/CatalogPromotion/Grid/Action/deleteCatalogPromotion.html.twig" sylius_grid: templates: diff --git a/src/Sylius/Bundle/AdminBundle/Resources/config/app/twig_hooks/promotion/create.yaml b/src/Sylius/Bundle/AdminBundle/Resources/config/app/twig_hooks/promotion/create.yaml new file mode 100644 index 00000000000..f0469c40d66 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/config/app/twig_hooks/promotion/create.yaml @@ -0,0 +1,62 @@ +twig_hooks: + hooks: + 'sylius_admin.promotion.create.content': + form: + component: 'sylius_admin:promotion:form' + data: + resource: '@=resource' + form: '@=form' + parentMainHook: '@=parent_main_hook' + parentFallbackHook: '@=parent_fallback_hook' + configuration: + render_rest: false + + 'sylius_admin.promotion.create.content.form': + sections: + template: '@SyliusAdmin/promotion/form/sections.html.twig' + + 'sylius_admin.promotion.create.content.form.sections': + general: + template: '@SyliusAdmin/promotion/form/sections/general.html.twig' + configuration: + template: '@SyliusAdmin/promotion/form/sections/configuration.html.twig' + translations: + template: '@SyliusAdmin/promotion/form/sections/translations.html.twig' + rules_and_actions: + template: '@SyliusAdmin/promotion/form/sections/rules_and_actions.html.twig' + + 'sylius_admin.promotion.create.content.form.sections.general': + _name: + template: '@SyliusAdmin/promotion/form/sections/general/name.html.twig' + code: + template: '@SyliusAdmin/promotion/form/sections/general/code.html.twig' + description: + template: '@SyliusAdmin/promotion/form/sections/general/description.html.twig' + channels: + template: '@SyliusAdmin/promotion/form/sections/general/channels.html.twig' + + 'sylius_admin.promotion.create.content.form.sections.configuration#left': + usage_limit: + template: '@SyliusAdmin/promotion/form/sections/configuration/usage_limit.html.twig' + priority: + template: '@SyliusAdmin/promotion/form/sections/configuration/priority.html.twig' + starts_at: + template: '@SyliusAdmin/promotion/form/sections/configuration/starts_at.html.twig' + ends_at: + template: '@SyliusAdmin/promotion/form/sections/configuration/ends_at.html.twig' + + 'sylius_admin.promotion.create.content.form.sections.configuration#right': + header: + template: '@SyliusAdmin/promotion/form/sections/configuration/header.html.twig' + coupon_based: + template: '@SyliusAdmin/promotion/form/sections/configuration/coupon_based.html.twig' + exclusive: + template: '@SyliusAdmin/promotion/form/sections/configuration/exclusive.html.twig' + applies_to_discounted: + template: '@SyliusAdmin/promotion/form/sections/configuration/applies_to_discounted.html.twig' + + 'sylius_admin.promotion.create.content.form.sections.rules_and_actions': + rules: + template: '@SyliusAdmin/promotion/form/sections/rules_and_actions/rules.html.twig' + actions: + template: '@SyliusAdmin/promotion/form/sections/rules_and_actions/actions.html.twig' diff --git a/src/Sylius/Bundle/AdminBundle/Resources/config/app/twig_hooks/promotion/update.yaml b/src/Sylius/Bundle/AdminBundle/Resources/config/app/twig_hooks/promotion/update.yaml new file mode 100644 index 00000000000..4e84c8dfea2 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/config/app/twig_hooks/promotion/update.yaml @@ -0,0 +1,62 @@ +twig_hooks: + hooks: + 'sylius_admin.promotion.update.content': + form: + component: 'sylius_admin:promotion:form' + data: + resource: '@=resource' + form: '@=form' + parentMainHook: '@=parent_main_hook' + parentFallbackHook: '@=parent_fallback_hook' + configuration: + method: 'PUT' + + 'sylius_admin.promotion.update.content.form': + sections: + template: '@SyliusAdmin/promotion/form/sections.html.twig' + + 'sylius_admin.promotion.update.content.form.sections': + general: + template: '@SyliusAdmin/promotion/form/sections/general.html.twig' + configuration: + template: '@SyliusAdmin/promotion/form/sections/configuration.html.twig' + translations: + template: '@SyliusAdmin/promotion/form/sections/translations.html.twig' + rules_and_actions: + template: '@SyliusAdmin/promotion/form/sections/rules_and_actions.html.twig' + + 'sylius_admin.promotion.update.content.form.sections.general': + _name: + template: '@SyliusAdmin/promotion/form/sections/general/name.html.twig' + code: + template: '@SyliusAdmin/promotion/form/sections/general/code.html.twig' + description: + template: '@SyliusAdmin/promotion/form/sections/general/description.html.twig' + channels: + template: '@SyliusAdmin/promotion/form/sections/general/channels.html.twig' + + 'sylius_admin.promotion.update.content.form.sections.configuration#left': + usage_limit: + template: '@SyliusAdmin/promotion/form/sections/configuration/usage_limit.html.twig' + priority: + template: '@SyliusAdmin/promotion/form/sections/configuration/priority.html.twig' + starts_at: + template: '@SyliusAdmin/promotion/form/sections/configuration/starts_at.html.twig' + ends_at: + template: '@SyliusAdmin/promotion/form/sections/configuration/ends_at.html.twig' + + 'sylius_admin.promotion.update.content.form.sections.configuration#right': + header: + template: '@SyliusAdmin/promotion/form/sections/configuration/header.html.twig' + coupon_based: + template: '@SyliusAdmin/promotion/form/sections/configuration/coupon_based.html.twig' + exclusive: + template: '@SyliusAdmin/promotion/form/sections/configuration/exclusive.html.twig' + applies_to_discounted: + template: '@SyliusAdmin/promotion/form/sections/configuration/applies_to_discounted.html.twig' + + 'sylius_admin.promotion.update.content.form.sections.rules_and_actions': + rules: + template: '@SyliusAdmin/promotion/form/sections/rules_and_actions/rules.html.twig' + actions: + template: '@SyliusAdmin/promotion/form/sections/rules_and_actions/actions.html.twig' diff --git a/src/Sylius/Bundle/AdminBundle/Resources/config/grids/catalog_promotion.yaml b/src/Sylius/Bundle/AdminBundle/Resources/config/grids/catalog_promotion.yaml index dffae96555a..402729249c9 100644 --- a/src/Sylius/Bundle/AdminBundle/Resources/config/grids/catalog_promotion.yaml +++ b/src/Sylius/Bundle/AdminBundle/Resources/config/grids/catalog_promotion.yaml @@ -44,7 +44,7 @@ sylius_grid: label: sylius.ui.state path: state options: - template: '@SyliusAdmin/Promotion/Grid/Field/state.html.twig' + template: '@SyliusAdmin/CatalogPromotion/Grid/Field/state.html.twig' enabled: type: twig label: sylius.ui.enabled diff --git a/src/Sylius/Bundle/AdminBundle/Resources/config/grids/promotion.yml b/src/Sylius/Bundle/AdminBundle/Resources/config/grids/promotion.yml index 7687e1f6f3a..da45cc3281c 100644 --- a/src/Sylius/Bundle/AdminBundle/Resources/config/grids/promotion.yml +++ b/src/Sylius/Bundle/AdminBundle/Resources/config/grids/promotion.yml @@ -13,7 +13,7 @@ sylius_grid: label: sylius.ui.priority sortable: ~ options: - template: "@SyliusAdmin/Promotion/Grid/Field/priority.html.twig" + template: "@SyliusAdmin/promotion/grid/field/priority.html.twig" code: type: string label: sylius.ui.code @@ -36,7 +36,7 @@ sylius_grid: path: . sortable: used options: - template: "@SyliusAdmin/Promotion/Grid/Field/usage.html.twig" + template: "@SyliusAdmin/promotion/grid/field/usage.html.twig" filters: search: type: string @@ -62,6 +62,13 @@ sylius_grid: create: type: create item: + update: + type: update + delete: + type: delete + archive: + type: archive + subitem: coupons: type: links label: sylius.ui.manage_coupons @@ -87,12 +94,6 @@ sylius_grid: route: sylius_admin_promotion_coupon_generate parameters: promotionId: resource.id - update: - type: update - delete: - type: delete - archive: - type: archive bulk: delete: type: delete diff --git a/src/Sylius/Bundle/AdminBundle/Resources/config/services/autocompleter.xml b/src/Sylius/Bundle/AdminBundle/Resources/config/services/autocompleter.xml index c82510f975b..d0625a30423 100644 --- a/src/Sylius/Bundle/AdminBundle/Resources/config/services/autocompleter.xml +++ b/src/Sylius/Bundle/AdminBundle/Resources/config/services/autocompleter.xml @@ -13,11 +13,16 @@ - + %sylius.model.product_attribute.class% + + %sylius.model.product.class% + + + %sylius.model.taxon.class% diff --git a/src/Sylius/Bundle/AdminBundle/Resources/config/services/form.xml b/src/Sylius/Bundle/AdminBundle/Resources/config/services/form.xml index f49c9601086..b5182efcd07 100644 --- a/src/Sylius/Bundle/AdminBundle/Resources/config/services/form.xml +++ b/src/Sylius/Bundle/AdminBundle/Resources/config/services/form.xml @@ -67,6 +67,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %sylius.model.product.class% + + + + + %sylius.model.taxon.class% + + + %sylius.form.type.admin.password_reset_request.validation_groups% diff --git a/src/Sylius/Bundle/AdminBundle/Resources/config/services/twig/component.xml b/src/Sylius/Bundle/AdminBundle/Resources/config/services/twig/component.xml index c4144f00de2..49c270a0df8 100644 --- a/src/Sylius/Bundle/AdminBundle/Resources/config/services/twig/component.xml +++ b/src/Sylius/Bundle/AdminBundle/Resources/config/services/twig/component.xml @@ -73,6 +73,13 @@ + + + Sylius\Bundle\PromotionBundle\Form\Type\PromotionType + %sylius.promotion_rules% + %sylius.promotion_actions% + + Sylius\Bundle\OrderBundle\Form\Type\OrderType diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/Promotion/Grid/Action/deleteCatalogPromotion.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/CatalogPromotion/Grid/Action/deleteCatalogPromotion.html.twig similarity index 100% rename from src/Sylius/Bundle/AdminBundle/Resources/views/Promotion/Grid/Action/deleteCatalogPromotion.html.twig rename to src/Sylius/Bundle/AdminBundle/Resources/views/CatalogPromotion/Grid/Action/deleteCatalogPromotion.html.twig diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/Promotion/Grid/Field/state.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/CatalogPromotion/Grid/Field/state.html.twig similarity index 100% rename from src/Sylius/Bundle/AdminBundle/Resources/views/Promotion/Grid/Field/state.html.twig rename to src/Sylius/Bundle/AdminBundle/Resources/views/CatalogPromotion/Grid/Field/state.html.twig diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/Product/Form/Sections/_translations.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/Product/Form/Sections/_translations.html.twig index 4f00bd30279..2a6899b0721 100644 --- a/src/Sylius/Bundle/AdminBundle/Resources/views/Product/Form/Sections/_translations.html.twig +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/Product/Form/Sections/_translations.html.twig @@ -39,6 +39,6 @@ {% endverbatim %} {% endset %} - {{ _translations.default(hookable_data.form.translations, body) }} + {{ _translations.default(hookable_data.form.translations, body, {accordionId: 'product-translations'}) }} diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/ProductVariant/Form/Sections/_translations.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/ProductVariant/Form/Sections/_translations.html.twig index b0a803f3c61..a3a6480dd4f 100644 --- a/src/Sylius/Bundle/AdminBundle/Resources/views/ProductVariant/Form/Sections/_translations.html.twig +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/ProductVariant/Form/Sections/_translations.html.twig @@ -11,6 +11,6 @@ {% endverbatim %} {% endset %} - {{ _translations.default(hookable_data.form.translations, body) }} + {{ _translations.default(hookable_data.form.translations, body, {accordionId: 'product-variant-translations'}) }} diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/Shared/Helper/translations.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/Shared/Helper/translations.html.twig index 76c894b5483..31897a1c82c 100644 --- a/src/Sylius/Bundle/AdminBundle/Resources/views/Shared/Helper/translations.html.twig +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/Shared/Helper/translations.html.twig @@ -1,8 +1,4 @@ {% macro default(translationFields, body = null, options = {}) %} - {% set options = { - accordionId: 'product-translations' - }|merge(options) %} -
{% for locale, translationForm in translationFields %}
@@ -14,13 +10,13 @@ data-bs-target="#translation-{{ locale }}" aria-expanded="{% if loop.first %}true{% else %}false{% endif %}" aria-controls="translation-{{ locale }}" - {{ sylius_test_html_attribute('product-translation-accordion', locale) }} + {{ sylius_test_html_attribute(options.accordionId ~ '-accordion', locale) }} > {{ locale|sylius_locale_name }} -
+
{% if body is not null %} {{ include(template_from_string(body), { locale: locale, translationForm: translationForm }) }} diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/Shared/form_theme.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/Shared/form_theme.html.twig index 535cd24d411..4090011d01c 100644 --- a/src/Sylius/Bundle/AdminBundle/Resources/views/Shared/form_theme.html.twig +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/Shared/form_theme.html.twig @@ -24,3 +24,56 @@ {{- form_errors(form) -}}
{% endblock %} + +{% block sylius_promotion_action_row %} + +
+ {{- form_label(form) -}} + {{- form_widget(form) -}} + {{- form_errors(form) -}} + {{- form_row(button_delete, sylius_test_form_attribute('delete-action')) -}} +
+
+{% endblock %} + +{% block sylius_promotion_rule_row %} +
+
+ {{- form_label(form) -}} + {{- form_widget(form) -}} + {{- form_errors(form) -}} + {{- form_row(button_delete, sylius_test_form_attribute('delete-rule')) -}} +
+
+{% endblock %} + +{% block sylius_channel_collection_row %} + +
+ +
+
+
+ {% for channelCode, channelConfiguration in form %} +
+ {{ form_row(channelConfiguration, sylius_test_form_attribute('tab-content')|merge({'label': false})) }} +
+ {% endfor %} +
+
+
+{% endblock %} diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/Taxon/Form/Sections/_translations.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/Taxon/Form/Sections/_translations.html.twig index f2389ad5438..8a595c0fcb7 100644 --- a/src/Sylius/Bundle/AdminBundle/Resources/views/Taxon/Form/Sections/_translations.html.twig +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/Taxon/Form/Sections/_translations.html.twig @@ -21,5 +21,5 @@
{% endverbatim %} {% endset %} - {{ _translations.default(hookable_data.form.translations, body) }} + {{ _translations.default(hookable_data.form.translations, body, {accordionId: 'taxon-translations'}) }}
diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form.html.twig new file mode 100644 index 00000000000..a2eafe3d017 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form.html.twig @@ -0,0 +1,23 @@ +{# Rendered with \Sylius\Bundle\AdminBundle\TwigComponent\Promotion\FormComponent #} + +{% form_theme form '@SyliusAdmin/Shared/form_theme.html.twig' %} + +{% set main_hook = hook_name(parent_main_hook, 'form') %} +{% set fallback_hook = hook_name(parent_fallback_hook, 'form') %} + +
+ {{ form_start(form, {'attr': {'class': 'ui loadable form', 'novalidate': 'novalidate', 'id': form.vars.id}}) }} + {% if hookable_configuration.method is defined %} + + {% endif %} + {{ form_errors(form) }} + {{ form_widget(form._token) }} + + {% hook [main_hook, fallback_hook] with { + parent_main_hook: main_hook, + parent_fallback_hook: fallback_hook, + form, + } %} + + {{ form_end(form, {render_rest: hookable.configuration.render_rest|default(false)}) }} +
diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections.html.twig new file mode 100644 index 00000000000..535e2592eb2 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections.html.twig @@ -0,0 +1,7 @@ +{% set main_hook = hook_name(hookable_data.parent_main_hook, 'sections') %} +{% set fallback_hook = hook_name(hookable_data.parent_fallback_hook, 'sections') %} + +{% hook [main_hook, fallback_hook] with hookable_data|merge({ + parent_main_hook: main_hook, + parent_fallback_hook: fallback_hook, +}) %} diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration.html.twig new file mode 100644 index 00000000000..b2e6fb9b4e7 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration.html.twig @@ -0,0 +1,32 @@ +{% set main_hook = hook_name(hookable_data.parent_main_hook, 'configuration') %} +{% set fallback_hook = hook_name(hookable_data.parent_fallback_hook, 'configuration') %} + +
+
+
+ {{ 'sylius.ui.configuration'|trans }} +
+
+
+
+ {% hook [main_hook, fallback_hook] with hookable_data|merge({ + parent_main_hook: main_hook, + parent_fallback_hook: fallback_hook, + }) %} + +
+ {% hook [main_hook ~ '#left', fallback_hook] with hookable_data|merge({ + parent_main_hook: main_hook, + parent_fallback_hook: fallback_hook, + }) %} +
+ +
+ {% hook [main_hook ~ '#right', fallback_hook] with hookable_data|merge({ + parent_main_hook: main_hook, + parent_fallback_hook: fallback_hook, + }) %} +
+
+
+
diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/applies_to_discounted.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/applies_to_discounted.html.twig new file mode 100644 index 00000000000..2cb40aef5e5 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/applies_to_discounted.html.twig @@ -0,0 +1,3 @@ +
+ {{ form_row(hookable_data.form.appliesToDiscounted, {help_attr: { 'class': 'form-hint' }}) }} +
diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/coupon_based.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/coupon_based.html.twig new file mode 100644 index 00000000000..8abbd3cef63 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/coupon_based.html.twig @@ -0,0 +1,3 @@ +
+ {{ form_row(hookable_data.form.couponBased) }} +
diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/ends_at.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/ends_at.html.twig new file mode 100644 index 00000000000..814105436ce --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/ends_at.html.twig @@ -0,0 +1,3 @@ +
+ {{ form_row(hookable_data.form.endsAt) }} +
diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/exclusive.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/exclusive.html.twig new file mode 100644 index 00000000000..5f510eef4ad --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/exclusive.html.twig @@ -0,0 +1,3 @@ +
+ {{ form_row(hookable_data.form.exclusive) }} +
diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/header.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/header.html.twig new file mode 100644 index 00000000000..923f5d4a1c1 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/header.html.twig @@ -0,0 +1 @@ +
{{ 'sylius.ui.options'|trans }}
diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/priority.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/priority.html.twig new file mode 100644 index 00000000000..2dad0259c58 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/priority.html.twig @@ -0,0 +1,3 @@ +
+ {{ form_row(hookable_data.form.priority) }} +
diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/starts_at.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/starts_at.html.twig new file mode 100644 index 00000000000..90baf7f80c8 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/starts_at.html.twig @@ -0,0 +1,3 @@ +
+ {{ form_row(hookable_data.form.startsAt) }} +
diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/usage_limit.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/usage_limit.html.twig new file mode 100644 index 00000000000..b31d710ba3a --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/configuration/usage_limit.html.twig @@ -0,0 +1,3 @@ +
+ {{ form_row(hookable_data.form.usageLimit, sylius_test_form_attribute('usage-limit')) }} +
diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/general.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/general.html.twig new file mode 100644 index 00000000000..cc2c9c63db2 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/general.html.twig @@ -0,0 +1,18 @@ +{% set main_hook = hook_name(hookable_data.parent_main_hook, 'general') %} +{% set fallback_hook = hook_name(hookable_data.parent_fallback_hook, 'general') %} + +
+
+
+ {{ 'sylius.ui.general'|trans }} +
+
+
+
+ {% hook [main_hook, fallback_hook] with hookable_data|merge({ + parent_main_hook: main_hook, + parent_fallback_hook: fallback_hook, + }) %} +
+
+
diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/general/channels.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/general/channels.html.twig new file mode 100644 index 00000000000..ffc1c22ff96 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/general/channels.html.twig @@ -0,0 +1,3 @@ +
+ {{ form_row(hookable_data.form.channels) }} +
diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/general/code.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/general/code.html.twig new file mode 100644 index 00000000000..0e2050b5ace --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/general/code.html.twig @@ -0,0 +1,3 @@ +
+ {{ form_row(hookable_data.form.code) }} +
diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/general/description.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/general/description.html.twig new file mode 100644 index 00000000000..dc40c7de1f1 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/general/description.html.twig @@ -0,0 +1 @@ +{{ form_row(hookable_data.form.description, sylius_test_form_attribute('description')) }} diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/general/name.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/general/name.html.twig new file mode 100644 index 00000000000..9f648f706b5 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/general/name.html.twig @@ -0,0 +1,3 @@ +
+ {{ form_row(hookable_data.form.name) }} +
diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/rules_and_actions.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/rules_and_actions.html.twig new file mode 100644 index 00000000000..f6daf4aee46 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/rules_and_actions.html.twig @@ -0,0 +1,9 @@ +{% set main_hook = hook_name(hookable_data.parent_main_hook, 'rules_and_actions') %} +{% set fallback_hook = hook_name(hookable_data.parent_fallback_hook, 'rules_and_actions') %} + +
+ {% hook [main_hook, fallback_hook] with hookable_data|merge({ + parent_main_hook: main_hook, + parent_fallback_hook: fallback_hook, + }) %} +
diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/rules_and_actions/actions.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/rules_and_actions/actions.html.twig new file mode 100644 index 00000000000..f7640a0a5dc --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/rules_and_actions/actions.html.twig @@ -0,0 +1,22 @@ +{% set main_hook = hook_name(hookable_data.parent_main_hook, 'actions') %} +{% set fallback_hook = hook_name(hookable_data.parent_fallback_hook, 'actions') %} + +
+
+
+
+ {{ 'sylius.ui.actions'|trans }} +
+
+
+
+ {{ form_row(hookable_data.form.actions, {label: false}) }} + + {% hook [main_hook, fallback_hook] with hookable_data|merge({ + parent_main_hook: main_hook, + parent_fallback_hook: fallback_hook, + }) %} +
+
+
+
diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/rules_and_actions/rules.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/rules_and_actions/rules.html.twig new file mode 100644 index 00000000000..4a3b6a73e45 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/rules_and_actions/rules.html.twig @@ -0,0 +1,22 @@ +{% set main_hook = hook_name(hookable_data.parent_main_hook, 'rules') %} +{% set fallback_hook = hook_name(hookable_data.parent_fallback_hook, 'rules') %} + +
+
+
+
+ {{ 'sylius.ui.rules'|trans }} +
+
+
+
+ {{ form_row(hookable_data.form.rules, {label: false}) }} + + {% hook [main_hook, fallback_hook] with hookable_data|merge({ + parent_main_hook: main_hook, + parent_fallback_hook: fallback_hook, + }) %} +
+
+
+
diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/translations.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/translations.html.twig new file mode 100644 index 00000000000..befa61d45ad --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/form/sections/translations.html.twig @@ -0,0 +1,5 @@ +{% import '@SyliusAdmin/Shared/Helper/translations.html.twig' as _translations %} + +
+ {{ _translations.default(hookable_data.form.translations, null, {accordionId: 'promotion-translations'}) }} +
diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/Promotion/Grid/Field/priority.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/grid/field/priority.html.twig similarity index 100% rename from src/Sylius/Bundle/AdminBundle/Resources/views/Promotion/Grid/Field/priority.html.twig rename to src/Sylius/Bundle/AdminBundle/Resources/views/promotion/grid/field/priority.html.twig diff --git a/src/Sylius/Bundle/AdminBundle/Resources/views/Promotion/Grid/Field/usage.html.twig b/src/Sylius/Bundle/AdminBundle/Resources/views/promotion/grid/field/usage.html.twig similarity index 100% rename from src/Sylius/Bundle/AdminBundle/Resources/views/Promotion/Grid/Field/usage.html.twig rename to src/Sylius/Bundle/AdminBundle/Resources/views/promotion/grid/field/usage.html.twig diff --git a/src/Sylius/Bundle/AdminBundle/TwigComponent/Promotion/FormComponent.php b/src/Sylius/Bundle/AdminBundle/TwigComponent/Promotion/FormComponent.php new file mode 100644 index 00000000000..3facda289a0 --- /dev/null +++ b/src/Sylius/Bundle/AdminBundle/TwigComponent/Promotion/FormComponent.php @@ -0,0 +1,92 @@ + $ruleTypes + * @param array $actionTypes + */ + public function __construct( + private readonly FormFactoryInterface $formFactory, + private readonly string $formClass, + private readonly array $ruleTypes, + private readonly array $actionTypes, + ) { + } + + #[LiveAction] + public function addCollectionItem(PropertyAccessorInterface $propertyAccessor, #[LiveArg] string $name): void + { + $propertyPath = $this->fieldNameToPropertyPath($name, $this->formName); + $data = $propertyAccessor->getValue($this->formValues, $propertyPath); + + if (!\is_array($data)) { + $propertyAccessor->setValue($this->formValues, $propertyPath, []); + $data = []; + } + + $propertyAccessor->setValue( + $this->formValues, + sprintf('%s[%d]', $propertyPath, $this->resolveItemIndex($data)), + ['type' => $this->provideItemType($name)], + ); + } + + protected function instantiateForm(): FormInterface + { + return $this->formFactory->create($this->formClass, $this->resource); + } + + private function provideItemType(string $name): string + { + if (str_contains($name, 'rules')) { + return array_key_first($this->ruleTypes); + } + + if (str_contains($name, 'actions')) { + return array_key_first($this->actionTypes); + } + + return ''; + } + + /** @param array> $data */ + private function resolveItemIndex(array $data): int + { + return [] !== $data ? max(array_keys($data)) + 1 : 0; + } +} diff --git a/src/Sylius/Bundle/PromotionBundle/Form/Type/AbstractConfigurablePromotionElementType.php b/src/Sylius/Bundle/PromotionBundle/Form/Type/AbstractConfigurablePromotionElementType.php index 61edeb4dd59..adb68e8d461 100644 --- a/src/Sylius/Bundle/PromotionBundle/Form/Type/AbstractConfigurablePromotionElementType.php +++ b/src/Sylius/Bundle/PromotionBundle/Form/Type/AbstractConfigurablePromotionElementType.php @@ -53,7 +53,8 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event): void { $data = $event->getData(); - if (!isset($data['type'])) { + if (!isset($data['type']) || $data['type'] === '') { + return; } diff --git a/src/Sylius/Bundle/PromotionBundle/Form/Type/PromotionType.php b/src/Sylius/Bundle/PromotionBundle/Form/Type/PromotionType.php index 931976bba23..d584f0c40c6 100644 --- a/src/Sylius/Bundle/PromotionBundle/Form/Type/PromotionType.php +++ b/src/Sylius/Bundle/PromotionBundle/Form/Type/PromotionType.php @@ -52,6 +52,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ]) ->add('appliesToDiscounted', CheckboxType::class, [ 'label' => 'sylius.form.promotion.applies_to_discounted', + 'help' => 'sylius.form.promotion.applies_to_discounted_details', ]) ->add('usageLimit', IntegerType::class, [ 'label' => 'sylius.form.promotion.usage_limit', diff --git a/src/Sylius/Bundle/UiBundle/Resources/translations/messages.en.yml b/src/Sylius/Bundle/UiBundle/Resources/translations/messages.en.yml index 9b73e159431..5d2d9f3a0bb 100644 --- a/src/Sylius/Bundle/UiBundle/Resources/translations/messages.en.yml +++ b/src/Sylius/Bundle/UiBundle/Resources/translations/messages.en.yml @@ -684,6 +684,7 @@ sylius: products_categorized_under_taxon_taxon: 'Products Categorized under "%taxon%" taxon' products_index_of_all_products_in_your_store: 'Products Index of all products in your store' profile: 'Profile' + promotion: 'Cart promotion' promotion_configuration: 'Promotion configuration' promotion_coupons: 'Coupons' promotion_details: 'Promotion details' diff --git a/src/Sylius/Component/Core/Factory/PromotionRuleFactoryInterface.php b/src/Sylius/Component/Core/Factory/PromotionRuleFactoryInterface.php index e06844ba482..73aed301241 100644 --- a/src/Sylius/Component/Core/Factory/PromotionRuleFactoryInterface.php +++ b/src/Sylius/Component/Core/Factory/PromotionRuleFactoryInterface.php @@ -29,7 +29,6 @@ public function createItemTotal(string $channelCode, int $amount): PromotionRule public function createHasTaxon(array $taxons): PromotionRuleInterface; - public function createItemsFromTaxonTotal(string $channelCode, string $taxonCode, int $amount): PromotionRuleInterface; public function createNthOrder(int $nth): PromotionRuleInterface;