diff --git a/UPGRADE-2.3.md b/UPGRADE-2.3.md
index 474fe538f5..d0514f239f 100644
--- a/UPGRADE-2.3.md
+++ b/UPGRADE-2.3.md
@@ -166,6 +166,29 @@
| `ShippingBundle\...\ShippingMethodCalculatorExists` | `invalidShippingCalculator` | `invalidShippingCalculatorMessage` |
| `ShippingBundle\...\ShippingMethodRule` | `invalidType` | `invalidTypeMessage` |
+## Promotion
+
+1. New **opt-in per-channel** promotion rule and action types have been added. They let a single
+ promotion rule/action hold independent configuration per channel, with the `configuration` array
+ keyed by channel code (e.g. `['WEB_US' => ['count' => 2], 'WEB_GB' => ['count' => 5]]`):
+
+ - Rules: `cart_quantity_per_channel`, `customer_group_per_channel`, `nth_order_per_channel`,
+ `shipping_country_per_channel`, `has_taxon_per_channel`, `contains_product_per_channel`.
+ - Actions: `order_percentage_discount_per_channel`, `shipping_percentage_discount_per_channel`.
+
+ These are **additional** types registered alongside the existing plain ones. Existing promotions
+ and the plain rule/action types are unchanged, and **no core service is replaced**, so the change
+ is fully backward compatible and requires no action.
+
+ Implementation: two generic decorators in `Sylius\Component\Core\Promotion` unwrap the current
+ channel's configuration slice and delegate to the standard checker/command:
+
+ - `Sylius\Component\Core\Promotion\Checker\Rule\PerChannelRuleChecker`
+ - `Sylius\Component\Core\Promotion\Action\PerChannelPromotionActionCommand`
+
+ The admin forms use new `ChannelBased*ConfigurationType` form types built on top of
+ `Sylius\Bundle\CoreBundle\Form\Type\ChannelCollectionType`.
+
## Deprecations
1. Passing a `Sylius\Component\Core\Calculator\ProductVariantPricesCalculatorInterface` directly to the following catalog-facing classes is deprecated since Sylius 2.3.
diff --git a/features/shop/promotion/receiving_discount/receiving_discount_with_per_channel_rules_and_actions.feature b/features/shop/promotion/receiving_discount/receiving_discount_with_per_channel_rules_and_actions.feature
new file mode 100644
index 0000000000..e03a417823
--- /dev/null
+++ b/features/shop/promotion/receiving_discount/receiving_discount_with_per_channel_rules_and_actions.feature
@@ -0,0 +1,55 @@
+@receiving_discount
+Feature: Receiving discount with promotion rules and actions configured independently per channel
+ In order to pay the proper amount while buying promoted goods with different rules and actions per channel
+ As a Customer
+ I want to have per-channel promotion rules and actions applied correctly in my current channel
+
+ Background:
+ Given the store operates on a channel named "Web-US" in "USD" currency and with hostname "united.states"
+ And the store operates on another channel named "Web-GB" in "GBP" currency and with hostname "great.britain"
+ And the store classifies its products as "T-Shirts" and "Mugs"
+ And the store has a product "PHP T-Shirt" priced at "$100.00" in "Web-US" channel
+ And this product is also priced at "£80.00" in "Web-GB" channel
+ And the product "PHP T-Shirt" belongs to taxon "T-Shirts"
+ And the store has a product "Symfony T-Shirt" priced at "$60.00" in "Web-US" channel
+ And this product is also priced at "£40.00" in "Web-GB" channel
+ And there is a promotion "Holiday promotion"
+ And I am a logged in customer
+
+ @api @todo-ui
+ Scenario: Receiving an order percentage discount configured independently per channel
+ Given the promotion gives "10%" discount to every order in the "Web-US" channel and "5%" discount to every order in the "Web-GB" channel
+ When I changed my current channel to "Web-US"
+ And I added product "PHP T-Shirt" to the cart
+ And I check the details of my cart
+ Then my cart total should be "$90.00"
+ And my discount should be "-$10.00"
+
+ @api @todo-ui
+ Scenario: Order percentage discount applies the value configured for the current channel
+ Given the promotion gives "10%" discount to every order in the "Web-US" channel and "5%" discount to every order in the "Web-GB" channel
+ When I changed my current channel to "Web-GB"
+ And I added product "PHP T-Shirt" to the cart
+ And I check the details of my cart
+ Then my cart total should be "£76.00"
+ And my discount should be "-£4.00"
+
+ @api @todo-ui
+ Scenario: Receiving a discount when the cart contains the product configured for the current channel
+ Given the promotion gives "$10.00" discount to every order in the "Web-US" channel and "£5.00" discount to every order in the "Web-GB" channel
+ And the promotion applies to orders containing product "PHP T-Shirt" in the "Web-US" channel and product "Symfony T-Shirt" in the "Web-GB" channel
+ When I changed my current channel to "Web-US"
+ And I added product "PHP T-Shirt" to the cart
+ And I check the details of my cart
+ Then my cart total should be "$90.00"
+ And my discount should be "-$10.00"
+
+ @api @todo-ui
+ Scenario: Receiving a discount when the cart has a product from the taxon configured for the current channel
+ Given the promotion gives "$10.00" discount to every order in the "Web-US" channel and "£5.00" discount to every order in the "Web-GB" channel
+ And the promotion applies to orders with a product from taxon "T-Shirts" in the "Web-US" channel and from taxon "Mugs" in the "Web-GB" channel
+ When I changed my current channel to "Web-US"
+ And I added product "PHP T-Shirt" to the cart
+ And I check the details of my cart
+ Then my cart total should be "$90.00"
+ And my discount should be "-$10.00"
diff --git a/src/Sylius/Behat/Context/Setup/PromotionContext.php b/src/Sylius/Behat/Context/Setup/PromotionContext.php
index 64a3a179b2..15d21f975f 100644
--- a/src/Sylius/Behat/Context/Setup/PromotionContext.php
+++ b/src/Sylius/Behat/Context/Setup/PromotionContext.php
@@ -404,6 +404,72 @@ final readonly class PromotionContext implements Context
$this->objectManager->flush();
}
+ #[Given('/^([^"]+) gives ("[^"]+%") discount to every order in the ("[^"]+" channel) and ("[^"]+%") discount to every order in the ("[^"]+" channel)$/')]
+ public function thisPromotionGivesPercentageDiscountToEveryOrderInTheChannelAndInTheChannel(
+ PromotionInterface $promotion,
+ float $firstDiscount,
+ ChannelInterface $firstChannel,
+ float $secondDiscount,
+ ChannelInterface $secondChannel,
+ ): void {
+ $action = $this->actionFactory->createNew();
+ $action->setType('order_percentage_discount_per_channel');
+ $action->setConfiguration([
+ $firstChannel->getCode() => ['percentage' => $firstDiscount],
+ $secondChannel->getCode() => ['percentage' => $secondDiscount],
+ ]);
+
+ $promotion->addChannel($firstChannel);
+ $promotion->addChannel($secondChannel);
+ $promotion->addAction($action);
+
+ $this->objectManager->flush();
+ }
+
+ #[Given('/^(the promotion) applies to orders containing (product "[^"]+") in the ("[^"]+" channel) and (product "[^"]+") in the ("[^"]+" channel)$/')]
+ public function thePromotionAppliesToOrdersContainingProductInTheChannelAndInTheChannel(
+ PromotionInterface $promotion,
+ ProductInterface $firstProduct,
+ ChannelInterface $firstChannel,
+ ProductInterface $secondProduct,
+ ChannelInterface $secondChannel,
+ ): void {
+ $rule = $this->ruleFactory->createNew();
+ $rule->setType('contains_product_per_channel');
+ $rule->setConfiguration([
+ $firstChannel->getCode() => ['product_code' => $firstProduct->getCode()],
+ $secondChannel->getCode() => ['product_code' => $secondProduct->getCode()],
+ ]);
+
+ $promotion->addChannel($firstChannel);
+ $promotion->addChannel($secondChannel);
+ $promotion->addRule($rule);
+
+ $this->objectManager->flush();
+ }
+
+ #[Given('/^(the promotion) applies to orders with a product from (taxon "[^"]+") in the ("[^"]+" channel) and from (taxon "[^"]+") in the ("[^"]+" channel)$/')]
+ public function thePromotionAppliesToOrdersWithAProductFromTaxonInTheChannelAndInTheChannel(
+ PromotionInterface $promotion,
+ TaxonInterface $firstTaxon,
+ ChannelInterface $firstChannel,
+ TaxonInterface $secondTaxon,
+ ChannelInterface $secondChannel,
+ ): void {
+ $rule = $this->ruleFactory->createNew();
+ $rule->setType('has_taxon_per_channel');
+ $rule->setConfiguration([
+ $firstChannel->getCode() => ['taxons' => [$firstTaxon->getCode()]],
+ $secondChannel->getCode() => ['taxons' => [$secondTaxon->getCode()]],
+ ]);
+
+ $promotion->addChannel($firstChannel);
+ $promotion->addChannel($secondChannel);
+ $promotion->addRule($rule);
+
+ $this->objectManager->flush();
+ }
+
#[Given('/^(this promotion) gives ("(?:€|£|\$)[^"]+") off on every product in the ("[^"]+" channel) and ("(?:€|£|\$)[^"]+") off in the ("[^"]+" channel)$/')]
public function thisPromotionGivesFixedDiscountOnEveryProductInTheChannelAndInTheChannel(
PromotionInterface $promotion,
diff --git a/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Action/ChannelBasedPercentageDiscountConfigurationType.php b/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Action/ChannelBasedPercentageDiscountConfigurationType.php
new file mode 100644
index 0000000000..ff562e01b3
--- /dev/null
+++ b/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Action/ChannelBasedPercentageDiscountConfigurationType.php
@@ -0,0 +1,38 @@
+setDefaults([
+ 'entry_type' => PercentageDiscountConfigurationType::class,
+ 'entry_options' => fn (ChannelInterface $channel) => [
+ 'label' => $channel->getName(),
+ ],
+ ]);
+ }
+
+ public function getParent(): string
+ {
+ return ChannelCollectionType::class;
+ }
+}
diff --git a/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Rule/ChannelBasedCartQuantityConfigurationType.php b/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Rule/ChannelBasedCartQuantityConfigurationType.php
new file mode 100644
index 0000000000..2cc9a87753
--- /dev/null
+++ b/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Rule/ChannelBasedCartQuantityConfigurationType.php
@@ -0,0 +1,38 @@
+setDefaults([
+ 'entry_type' => CartQuantityConfigurationType::class,
+ 'entry_options' => fn (ChannelInterface $channel) => [
+ 'label' => $channel->getName(),
+ ],
+ ]);
+ }
+
+ public function getParent(): string
+ {
+ return ChannelCollectionType::class;
+ }
+}
diff --git a/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Rule/ChannelBasedContainsProductConfigurationType.php b/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Rule/ChannelBasedContainsProductConfigurationType.php
new file mode 100644
index 0000000000..57ede6a21d
--- /dev/null
+++ b/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Rule/ChannelBasedContainsProductConfigurationType.php
@@ -0,0 +1,37 @@
+setDefaults([
+ 'entry_type' => ContainsProductConfigurationType::class,
+ 'entry_options' => fn (ChannelInterface $channel) => [
+ 'label' => $channel->getName(),
+ ],
+ ]);
+ }
+
+ public function getParent(): string
+ {
+ return ChannelCollectionType::class;
+ }
+}
diff --git a/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Rule/ChannelBasedCustomerGroupConfigurationType.php b/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Rule/ChannelBasedCustomerGroupConfigurationType.php
new file mode 100644
index 0000000000..84f2fbce7d
--- /dev/null
+++ b/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Rule/ChannelBasedCustomerGroupConfigurationType.php
@@ -0,0 +1,37 @@
+setDefaults([
+ 'entry_type' => CustomerGroupConfigurationType::class,
+ 'entry_options' => fn (ChannelInterface $channel) => [
+ 'label' => $channel->getName(),
+ ],
+ ]);
+ }
+
+ public function getParent(): string
+ {
+ return ChannelCollectionType::class;
+ }
+}
diff --git a/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Rule/ChannelBasedHasTaxonConfigurationType.php b/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Rule/ChannelBasedHasTaxonConfigurationType.php
new file mode 100644
index 0000000000..6763e48bf5
--- /dev/null
+++ b/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Rule/ChannelBasedHasTaxonConfigurationType.php
@@ -0,0 +1,37 @@
+setDefaults([
+ 'entry_type' => HasTaxonConfigurationType::class,
+ 'entry_options' => fn (ChannelInterface $channel) => [
+ 'label' => $channel->getName(),
+ ],
+ ]);
+ }
+
+ public function getParent(): string
+ {
+ return ChannelCollectionType::class;
+ }
+}
diff --git a/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Rule/ChannelBasedNthOrderConfigurationType.php b/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Rule/ChannelBasedNthOrderConfigurationType.php
new file mode 100644
index 0000000000..9b15ce040d
--- /dev/null
+++ b/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Rule/ChannelBasedNthOrderConfigurationType.php
@@ -0,0 +1,37 @@
+setDefaults([
+ 'entry_type' => NthOrderConfigurationType::class,
+ 'entry_options' => fn (ChannelInterface $channel) => [
+ 'label' => $channel->getName(),
+ ],
+ ]);
+ }
+
+ public function getParent(): string
+ {
+ return ChannelCollectionType::class;
+ }
+}
diff --git a/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Rule/ChannelBasedShippingCountryConfigurationType.php b/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Rule/ChannelBasedShippingCountryConfigurationType.php
new file mode 100644
index 0000000000..0141e1b2da
--- /dev/null
+++ b/src/Sylius/Bundle/CoreBundle/Form/Type/Promotion/Rule/ChannelBasedShippingCountryConfigurationType.php
@@ -0,0 +1,37 @@
+setDefaults([
+ 'entry_type' => ShippingCountryConfigurationType::class,
+ 'entry_options' => fn (ChannelInterface $channel) => [
+ 'label' => $channel->getName(),
+ ],
+ ]);
+ }
+
+ public function getParent(): string
+ {
+ return ChannelCollectionType::class;
+ }
+}
diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/app/sylius/sylius_promotion.yml b/src/Sylius/Bundle/CoreBundle/Resources/config/app/sylius/sylius_promotion.yml
index 47ac5ce8ad..983b0857c8 100644
--- a/src/Sylius/Bundle/CoreBundle/Resources/config/app/sylius/sylius_promotion.yml
+++ b/src/Sylius/Bundle/CoreBundle/Resources/config/app/sylius/sylius_promotion.yml
@@ -44,9 +44,15 @@ sylius_promotion:
order_percentage_discount:
- 'sylius'
- 'sylius_promotion_action_order_percentage_discount'
+ order_percentage_discount_per_channel:
+ - 'sylius'
+ - 'sylius_promotion_action_order_percentage_discount_per_channel'
shipping_percentage_discount:
- 'sylius'
- 'sylius_promotion_action_shipping_percentage_discount'
+ shipping_percentage_discount_per_channel:
+ - 'sylius'
+ - 'sylius_promotion_action_shipping_percentage_discount_per_channel'
order_fixed_discount:
- 'sylius'
- 'sylius_promotion_action_order_fixed_discount'
@@ -62,18 +68,36 @@ sylius_promotion:
customer_group:
- 'sylius'
- 'sylius_promotion_rule_customer_group'
+ customer_group_per_channel:
+ - 'sylius'
+ - 'sylius_promotion_rule_customer_group_per_channel'
nth_order:
- 'sylius'
- 'sylius_promotion_rule_nth_order'
+ nth_order_per_channel:
+ - 'sylius'
+ - 'sylius_promotion_rule_nth_order_per_channel'
shipping_country:
- 'sylius'
- 'sylius_promotion_rule_shipping_country'
+ shipping_country_per_channel:
+ - 'sylius'
+ - 'sylius_promotion_rule_shipping_country_per_channel'
has_taxon:
- 'sylius'
- 'sylius_promotion_rule_has_taxon'
+ has_taxon_per_channel:
+ - 'sylius'
+ - 'sylius_promotion_rule_has_taxon_per_channel'
total_of_items_from_taxon:
- 'sylius'
- 'sylius_promotion_rule_total_of_items_from_taxon'
contains_product:
- 'sylius'
- 'sylius_promotion_rule_contains_product'
+ contains_product_per_channel:
+ - 'sylius'
+ - 'sylius_promotion_rule_contains_product_per_channel'
+ cart_quantity_per_channel:
+ - 'sylius'
+ - 'sylius_promotion_rule_cart_quantity_per_channel'
diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/services/promotion.php b/src/Sylius/Bundle/CoreBundle/Resources/config/services/promotion.php
index aba1a809f8..47e1257050 100644
--- a/src/Sylius/Bundle/CoreBundle/Resources/config/services/promotion.php
+++ b/src/Sylius/Bundle/CoreBundle/Resources/config/services/promotion.php
@@ -15,9 +15,16 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use Sylius\Bundle\CoreBundle\Doctrine\ORM\Promotion\Modifier\AtomicOrderPromotionsUsageModifier;
use Sylius\Bundle\CoreBundle\Form\Type\Promotion\Action\ChannelBasedFixedDiscountConfigurationType;
+use Sylius\Bundle\CoreBundle\Form\Type\Promotion\Action\ChannelBasedPercentageDiscountConfigurationType;
use Sylius\Bundle\CoreBundle\Form\Type\Promotion\Action\ChannelBasedUnitFixedDiscountConfigurationType;
use Sylius\Bundle\CoreBundle\Form\Type\Promotion\Action\ChannelBasedUnitPercentageDiscountConfigurationType;
+use Sylius\Bundle\CoreBundle\Form\Type\Promotion\Rule\ChannelBasedCartQuantityConfigurationType;
+use Sylius\Bundle\CoreBundle\Form\Type\Promotion\Rule\ChannelBasedContainsProductConfigurationType;
+use Sylius\Bundle\CoreBundle\Form\Type\Promotion\Rule\ChannelBasedCustomerGroupConfigurationType;
+use Sylius\Bundle\CoreBundle\Form\Type\Promotion\Rule\ChannelBasedHasTaxonConfigurationType;
use Sylius\Bundle\CoreBundle\Form\Type\Promotion\Rule\ChannelBasedItemTotalConfigurationType;
+use Sylius\Bundle\CoreBundle\Form\Type\Promotion\Rule\ChannelBasedNthOrderConfigurationType;
+use Sylius\Bundle\CoreBundle\Form\Type\Promotion\Rule\ChannelBasedShippingCountryConfigurationType;
use Sylius\Bundle\CoreBundle\Form\Type\Promotion\Rule\ChannelBasedTotalOfItemsFromTaxonConfigurationType;
use Sylius\Bundle\CoreBundle\Form\Type\Promotion\Rule\ContainsProductConfigurationType;
use Sylius\Bundle\CoreBundle\Form\Type\Promotion\Rule\CustomerGroupConfigurationType;
@@ -32,6 +39,7 @@ use Sylius\Component\Core\Factory\PromotionRuleFactory;
use Sylius\Component\Core\Factory\PromotionRuleFactoryInterface;
use Sylius\Component\Core\Promotion\Action\FixedDiscountPromotionActionCommand;
use Sylius\Component\Core\Promotion\Action\PercentageDiscountPromotionActionCommand;
+use Sylius\Component\Core\Promotion\Action\PerChannelPromotionActionCommand;
use Sylius\Component\Core\Promotion\Action\ShippingPercentageDiscountPromotionActionCommand;
use Sylius\Component\Core\Promotion\Action\UnitFixedDiscountPromotionActionCommand;
use Sylius\Component\Core\Promotion\Action\UnitPercentageDiscountPromotionActionCommand;
@@ -46,6 +54,7 @@ use Sylius\Component\Core\Promotion\Checker\Rule\CustomerGroupRuleChecker;
use Sylius\Component\Core\Promotion\Checker\Rule\HasTaxonRuleChecker;
use Sylius\Component\Core\Promotion\Checker\Rule\ItemTotalRuleChecker;
use Sylius\Component\Core\Promotion\Checker\Rule\NthOrderRuleChecker;
+use Sylius\Component\Core\Promotion\Checker\Rule\PerChannelRuleChecker;
use Sylius\Component\Core\Promotion\Checker\Rule\ShippingCountryRuleChecker;
use Sylius\Component\Core\Promotion\Checker\Rule\TotalOfItemsFromTaxonRuleChecker;
use Sylius\Component\Core\Promotion\Checker\TaxonInPromotionRuleChecker;
@@ -85,23 +94,47 @@ return static function (ContainerConfigurator $container) {
->tag('sylius.promotion_rule_checker', ['type' => 'customer_group', 'label' => 'sylius.form.promotion_rule.customer_group', 'form_type' => CustomerGroupConfigurationType::class])
;
+ $services
+ ->set('sylius.checker.promotion_rule.customer_group_per_channel', PerChannelRuleChecker::class)
+ ->args([service('sylius.checker.promotion_rule.customer_group')])
+ ->tag('sylius.promotion_rule_checker', ['type' => 'customer_group_per_channel', 'label' => 'sylius.form.promotion_rule.customer_group_per_channel', 'form_type' => ChannelBasedCustomerGroupConfigurationType::class])
+ ;
+
$services
->set('sylius.checker.promotion_rule.nth_order', NthOrderRuleChecker::class)
->args([service('sylius.repository.order')])
->tag('sylius.promotion_rule_checker', ['type' => 'nth_order', 'label' => 'sylius.form.promotion_rule.nth_order', 'form_type' => NthOrderConfigurationType::class])
;
+ $services
+ ->set('sylius.checker.promotion_rule.nth_order_per_channel', PerChannelRuleChecker::class)
+ ->args([service('sylius.checker.promotion_rule.nth_order')])
+ ->tag('sylius.promotion_rule_checker', ['type' => 'nth_order_per_channel', 'label' => 'sylius.form.promotion_rule.nth_order_per_channel', 'form_type' => ChannelBasedNthOrderConfigurationType::class])
+ ;
+
$services
->set('sylius.checker.promotion_rule.shipping_country', ShippingCountryRuleChecker::class)
->args([service('sylius.repository.country')])
->tag('sylius.promotion_rule_checker', ['type' => 'shipping_country', 'label' => 'sylius.form.promotion_rule.shipping_country', 'form_type' => ShippingCountryConfigurationType::class])
;
+ $services
+ ->set('sylius.checker.promotion_rule.shipping_country_per_channel', PerChannelRuleChecker::class)
+ ->args([service('sylius.checker.promotion_rule.shipping_country')])
+ ->tag('sylius.promotion_rule_checker', ['type' => 'shipping_country_per_channel', 'label' => 'sylius.form.promotion_rule.shipping_country_per_channel', 'form_type' => ChannelBasedShippingCountryConfigurationType::class])
+ ;
+
$services
->set('sylius.checker.promotion_rule.has_taxon', HasTaxonRuleChecker::class)
->tag('sylius.promotion_rule_checker', ['type' => 'has_taxon', 'label' => 'sylius.form.promotion_rule.has_at_least_one_from_taxons', 'form_type' => HasTaxonConfigurationType::class])
;
+ $services
+ ->set('sylius.checker.promotion_rule.has_taxon_per_channel', PerChannelRuleChecker::class)
+ ->args([service('sylius.checker.promotion_rule.has_taxon')])
+ ->tag('sylius.promotion_rule_checker', ['type' => 'has_taxon_per_channel', 'label' => 'sylius.form.promotion_rule.has_at_least_one_from_taxons_per_channel', 'form_type' => ChannelBasedHasTaxonConfigurationType::class])
+ ;
+
$services
->set('sylius.checker.promotion_rule.total_of_items_from_taxon', TotalOfItemsFromTaxonRuleChecker::class)
->args([service('sylius.repository.taxon')])
@@ -113,6 +146,12 @@ return static function (ContainerConfigurator $container) {
->tag('sylius.promotion_rule_checker', ['type' => 'contains_product', 'label' => 'sylius.form.promotion_rule.contains_product', 'form_type' => ContainsProductConfigurationType::class])
;
+ $services
+ ->set('sylius.checker.promotion_rule.contains_product_per_channel', PerChannelRuleChecker::class)
+ ->args([service('sylius.checker.promotion_rule.contains_product')])
+ ->tag('sylius.promotion_rule_checker', ['type' => 'contains_product_per_channel', 'label' => 'sylius.form.promotion_rule.contains_product_per_channel', 'form_type' => ChannelBasedContainsProductConfigurationType::class])
+ ;
+
$services
->set('sylius.checker.promotion_rule.item_total', ItemTotalRuleChecker::class)
->tag('sylius.promotion_rule_checker', ['type' => 'item_total', 'label' => 'sylius.form.promotion_rule.item_total', 'form_type' => ChannelBasedItemTotalConfigurationType::class])
@@ -123,6 +162,12 @@ return static function (ContainerConfigurator $container) {
->tag('sylius.promotion_rule_checker', ['type' => 'cart_quantity', 'label' => 'sylius.form.promotion_rule.cart_quantity', 'form_type' => CartQuantityConfigurationType::class])
;
+ $services
+ ->set('sylius.checker.promotion_rule.cart_quantity_per_channel', PerChannelRuleChecker::class)
+ ->args([service('sylius.checker.promotion_rule.cart_quantity')])
+ ->tag('sylius.promotion_rule_checker', ['type' => 'cart_quantity_per_channel', 'label' => 'sylius.form.promotion_rule.cart_quantity_per_channel', 'form_type' => ChannelBasedCartQuantityConfigurationType::class])
+ ;
+
$services
->set('sylius.command.promotion_action.fixed_discount', FixedDiscountPromotionActionCommand::class)
->args([
@@ -156,6 +201,12 @@ return static function (ContainerConfigurator $container) {
->tag('sylius.promotion_action', ['type' => 'order_percentage_discount', 'label' => 'sylius.form.promotion_action.order_percentage_discount', 'form_type' => PercentageDiscountConfigurationType::class])
;
+ $services
+ ->set('sylius.command.promotion_action.percentage_discount_per_channel', PerChannelPromotionActionCommand::class)
+ ->args([service('sylius.command.promotion_action.percentage_discount')])
+ ->tag('sylius.promotion_action', ['type' => 'order_percentage_discount_per_channel', 'label' => 'sylius.form.promotion_action.order_percentage_discount_per_channel', 'form_type' => ChannelBasedPercentageDiscountConfigurationType::class])
+ ;
+
$services
->set('sylius.command.promotion_action.unit_percentage_discount', UnitPercentageDiscountPromotionActionCommand::class)
->args([
@@ -173,6 +224,12 @@ return static function (ContainerConfigurator $container) {
->tag('sylius.promotion_action', ['type' => 'shipping_percentage_discount', 'label' => 'sylius.form.promotion_action.shipping_percentage_discount', 'form_type' => PercentageDiscountConfigurationType::class])
;
+ $services
+ ->set('sylius.command.promotion_action.shipping_percentage_discount_per_channel', PerChannelPromotionActionCommand::class)
+ ->args([service('sylius.command.promotion_action.shipping_percentage_discount')])
+ ->tag('sylius.promotion_action', ['type' => 'shipping_percentage_discount_per_channel', 'label' => 'sylius.form.promotion_action.shipping_percentage_discount_per_channel', 'form_type' => ChannelBasedPercentageDiscountConfigurationType::class])
+ ;
+
$services
->set('sylius.checker.promotion.promotion_coupon_per_customer_usage_limit_eligibility', PromotionCouponPerCustomerUsageLimitEligibilityChecker::class)
->args([service('sylius.repository.order')])
diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/validation/PromotionAction.xml b/src/Sylius/Bundle/CoreBundle/Resources/config/validation/PromotionAction.xml
index 5f18de8d03..95b62cc7d7 100644
--- a/src/Sylius/Bundle/CoreBundle/Resources/config/validation/PromotionAction.xml
+++ b/src/Sylius/Bundle/CoreBundle/Resources/config/validation/PromotionAction.xml
@@ -280,6 +280,46 @@
+
+
+
+
+
+
+
diff --git a/src/Sylius/Bundle/CoreBundle/Resources/config/validation/PromotionRule.xml b/src/Sylius/Bundle/CoreBundle/Resources/config/validation/PromotionRule.xml
index b5ccc3aef5..078af69cf2 100644
--- a/src/Sylius/Bundle/CoreBundle/Resources/config/validation/PromotionRule.xml
+++ b/src/Sylius/Bundle/CoreBundle/Resources/config/validation/PromotionRule.xml
@@ -178,6 +178,163 @@
+
+