mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-05 20:57:12 +00:00
Add per-channel configuration for promotion rules and actions
This commit is contained in:
parent
96233aa29a
commit
6b327f4db8
18 changed files with 852 additions and 2 deletions
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\CoreBundle\Form\Type\Promotion\Action;
|
||||
|
||||
use Sylius\Bundle\CoreBundle\Form\Type\ChannelCollectionType;
|
||||
use Sylius\Bundle\PromotionBundle\Form\Type\Action\PercentageDiscountConfigurationType;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
final class ChannelBasedPercentageDiscountConfigurationType extends AbstractType
|
||||
{
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'entry_type' => PercentageDiscountConfigurationType::class,
|
||||
'entry_options' => fn (ChannelInterface $channel) => [
|
||||
'label' => $channel->getName(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function getParent(): string
|
||||
{
|
||||
return ChannelCollectionType::class;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\CoreBundle\Form\Type\Promotion\Rule;
|
||||
|
||||
use Sylius\Bundle\CoreBundle\Form\Type\ChannelCollectionType;
|
||||
use Sylius\Bundle\PromotionBundle\Form\Type\Rule\CartQuantityConfigurationType;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
final class ChannelBasedCartQuantityConfigurationType extends AbstractType
|
||||
{
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'entry_type' => CartQuantityConfigurationType::class,
|
||||
'entry_options' => fn (ChannelInterface $channel) => [
|
||||
'label' => $channel->getName(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function getParent(): string
|
||||
{
|
||||
return ChannelCollectionType::class;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\CoreBundle\Form\Type\Promotion\Rule;
|
||||
|
||||
use Sylius\Bundle\CoreBundle\Form\Type\ChannelCollectionType;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
final class ChannelBasedContainsProductConfigurationType extends AbstractType
|
||||
{
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'entry_type' => ContainsProductConfigurationType::class,
|
||||
'entry_options' => fn (ChannelInterface $channel) => [
|
||||
'label' => $channel->getName(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function getParent(): string
|
||||
{
|
||||
return ChannelCollectionType::class;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\CoreBundle\Form\Type\Promotion\Rule;
|
||||
|
||||
use Sylius\Bundle\CoreBundle\Form\Type\ChannelCollectionType;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
final class ChannelBasedCustomerGroupConfigurationType extends AbstractType
|
||||
{
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'entry_type' => CustomerGroupConfigurationType::class,
|
||||
'entry_options' => fn (ChannelInterface $channel) => [
|
||||
'label' => $channel->getName(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function getParent(): string
|
||||
{
|
||||
return ChannelCollectionType::class;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\CoreBundle\Form\Type\Promotion\Rule;
|
||||
|
||||
use Sylius\Bundle\CoreBundle\Form\Type\ChannelCollectionType;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
final class ChannelBasedHasTaxonConfigurationType extends AbstractType
|
||||
{
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'entry_type' => HasTaxonConfigurationType::class,
|
||||
'entry_options' => fn (ChannelInterface $channel) => [
|
||||
'label' => $channel->getName(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function getParent(): string
|
||||
{
|
||||
return ChannelCollectionType::class;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\CoreBundle\Form\Type\Promotion\Rule;
|
||||
|
||||
use Sylius\Bundle\CoreBundle\Form\Type\ChannelCollectionType;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
final class ChannelBasedNthOrderConfigurationType extends AbstractType
|
||||
{
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'entry_type' => NthOrderConfigurationType::class,
|
||||
'entry_options' => fn (ChannelInterface $channel) => [
|
||||
'label' => $channel->getName(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function getParent(): string
|
||||
{
|
||||
return ChannelCollectionType::class;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Bundle\CoreBundle\Form\Type\Promotion\Rule;
|
||||
|
||||
use Sylius\Bundle\CoreBundle\Form\Type\ChannelCollectionType;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
final class ChannelBasedShippingCountryConfigurationType extends AbstractType
|
||||
{
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'entry_type' => ShippingCountryConfigurationType::class,
|
||||
'entry_options' => fn (ChannelInterface $channel) => [
|
||||
'label' => $channel->getName(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function getParent(): string
|
||||
{
|
||||
return ChannelCollectionType::class;
|
||||
}
|
||||
}
|
||||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -123,6 +132,42 @@ 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.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_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_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_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.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.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([
|
||||
|
|
@ -173,6 +218,18 @@ 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.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.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')])
|
||||
|
|
|
|||
|
|
@ -280,6 +280,46 @@
|
|||
</constraint>
|
||||
</option>
|
||||
</constraint>
|
||||
<constraint name="Sylius\Bundle\CoreBundle\Validator\Constraints\ChannelCodeCollection">
|
||||
<option name="allowExtraFields">true</option>
|
||||
<option name="groups">
|
||||
<value>sylius_promotion_action_order_percentage_discount_per_channel</value>
|
||||
<value>sylius_promotion_action_shipping_percentage_discount_per_channel</value>
|
||||
</option>
|
||||
<option name="validateAgainstAllChannels">true</option>
|
||||
<option name="channelAwarePropertyPath">promotion</option>
|
||||
<option name="constraints">
|
||||
<constraint name="Collection">
|
||||
<option name="allowExtraFields">true</option>
|
||||
<option name="fields">
|
||||
<value key="percentage">
|
||||
<constraint name="NotBlank">
|
||||
<option name="groups">
|
||||
<value>sylius_promotion_action_order_percentage_discount_per_channel</value>
|
||||
<value>sylius_promotion_action_shipping_percentage_discount_per_channel</value>
|
||||
</option>
|
||||
</constraint>
|
||||
<constraint name="Type">
|
||||
<option name="type">numeric</option>
|
||||
<option name="groups">
|
||||
<value>sylius_promotion_action_order_percentage_discount_per_channel</value>
|
||||
<value>sylius_promotion_action_shipping_percentage_discount_per_channel</value>
|
||||
</option>
|
||||
</constraint>
|
||||
<constraint name="Range">
|
||||
<option name="min">0</option>
|
||||
<option name="max">1</option>
|
||||
<option name="notInRangeMessage">sylius.promotion_action.percentage_discount_configuration.not_in_range</option>
|
||||
<option name="groups">
|
||||
<value>sylius_promotion_action_order_percentage_discount_per_channel</value>
|
||||
<value>sylius_promotion_action_shipping_percentage_discount_per_channel</value>
|
||||
</option>
|
||||
</constraint>
|
||||
</value>
|
||||
</option>
|
||||
</constraint>
|
||||
</option>
|
||||
</constraint>
|
||||
<constraint name="Sylius\Bundle\CoreBundle\Validator\Constraints\PromotionConfigurationChannelCodes">
|
||||
<option name="groups">
|
||||
<value>sylius</value>
|
||||
|
|
|
|||
|
|
@ -178,6 +178,163 @@
|
|||
</constraint>
|
||||
</option>
|
||||
</constraint>
|
||||
<constraint name="Sylius\Bundle\CoreBundle\Validator\Constraints\ChannelCodeCollection">
|
||||
<option name="allowExtraFields">true</option>
|
||||
<option name="groups">sylius_promotion_rule_cart_quantity_per_channel</option>
|
||||
<option name="validateAgainstAllChannels">true</option>
|
||||
<option name="channelAwarePropertyPath">promotion</option>
|
||||
<option name="constraints">
|
||||
<constraint name="Collection">
|
||||
<option name="allowExtraFields">true</option>
|
||||
<option name="groups">sylius_promotion_rule_cart_quantity_per_channel</option>
|
||||
<option name="fields">
|
||||
<value key="count">
|
||||
<constraint name="NotBlank">
|
||||
<option name="groups">sylius_promotion_rule_cart_quantity_per_channel</option>
|
||||
</constraint>
|
||||
<constraint name="Type">
|
||||
<option name="type">numeric</option>
|
||||
<option name="groups">sylius_promotion_rule_cart_quantity_per_channel</option>
|
||||
</constraint>
|
||||
</value>
|
||||
</option>
|
||||
</constraint>
|
||||
</option>
|
||||
</constraint>
|
||||
|
||||
<constraint name="Sylius\Bundle\CoreBundle\Validator\Constraints\ChannelCodeCollection">
|
||||
<option name="allowExtraFields">true</option>
|
||||
<option name="groups">sylius_promotion_rule_customer_group_per_channel</option>
|
||||
<option name="validateAgainstAllChannels">true</option>
|
||||
<option name="channelAwarePropertyPath">promotion</option>
|
||||
<option name="constraints">
|
||||
<constraint name="Collection">
|
||||
<option name="allowExtraFields">true</option>
|
||||
<option name="groups">sylius_promotion_rule_customer_group_per_channel</option>
|
||||
<option name="fields">
|
||||
<value key="group_code">
|
||||
<constraint name="NotBlank">
|
||||
<option name="groups">sylius_promotion_rule_customer_group_per_channel</option>
|
||||
</constraint>
|
||||
<constraint name="Type">
|
||||
<option name="type">string</option>
|
||||
<option name="groups">sylius_promotion_rule_customer_group_per_channel</option>
|
||||
</constraint>
|
||||
<constraint name="Sylius\Bundle\CoreBundle\Validator\Constraints\CustomerGroupCodeExists">
|
||||
<option name="groups">sylius_promotion_rule_customer_group_per_channel</option>
|
||||
</constraint>
|
||||
</value>
|
||||
</option>
|
||||
</constraint>
|
||||
</option>
|
||||
</constraint>
|
||||
|
||||
<constraint name="Sylius\Bundle\CoreBundle\Validator\Constraints\ChannelCodeCollection">
|
||||
<option name="allowExtraFields">true</option>
|
||||
<option name="groups">sylius_promotion_rule_nth_order_per_channel</option>
|
||||
<option name="validateAgainstAllChannels">true</option>
|
||||
<option name="channelAwarePropertyPath">promotion</option>
|
||||
<option name="constraints">
|
||||
<constraint name="Collection">
|
||||
<option name="allowExtraFields">true</option>
|
||||
<option name="groups">sylius_promotion_rule_nth_order_per_channel</option>
|
||||
<option name="fields">
|
||||
<value key="nth">
|
||||
<constraint name="NotBlank">
|
||||
<option name="groups">sylius_promotion_rule_nth_order_per_channel</option>
|
||||
</constraint>
|
||||
<constraint name="Type">
|
||||
<option name="type">numeric</option>
|
||||
<option name="groups">sylius_promotion_rule_nth_order_per_channel</option>
|
||||
</constraint>
|
||||
</value>
|
||||
</option>
|
||||
</constraint>
|
||||
</option>
|
||||
</constraint>
|
||||
|
||||
<constraint name="Sylius\Bundle\CoreBundle\Validator\Constraints\ChannelCodeCollection">
|
||||
<option name="allowExtraFields">true</option>
|
||||
<option name="groups">sylius_promotion_rule_shipping_country_per_channel</option>
|
||||
<option name="validateAgainstAllChannels">true</option>
|
||||
<option name="channelAwarePropertyPath">promotion</option>
|
||||
<option name="constraints">
|
||||
<constraint name="Collection">
|
||||
<option name="allowExtraFields">true</option>
|
||||
<option name="groups">sylius_promotion_rule_shipping_country_per_channel</option>
|
||||
<option name="fields">
|
||||
<value key="country">
|
||||
<constraint name="NotBlank">
|
||||
<option name="groups">sylius_promotion_rule_shipping_country_per_channel</option>
|
||||
</constraint>
|
||||
<constraint name="Type">
|
||||
<option name="type">string</option>
|
||||
<option name="groups">sylius_promotion_rule_shipping_country_per_channel</option>
|
||||
</constraint>
|
||||
<constraint name="Sylius\Bundle\CoreBundle\Validator\Constraints\CountryCodeExists">
|
||||
<option name="groups">sylius_promotion_rule_shipping_country_per_channel</option>
|
||||
</constraint>
|
||||
</value>
|
||||
</option>
|
||||
</constraint>
|
||||
</option>
|
||||
</constraint>
|
||||
|
||||
<constraint name="Sylius\Bundle\CoreBundle\Validator\Constraints\ChannelCodeCollection">
|
||||
<option name="allowExtraFields">true</option>
|
||||
<option name="groups">sylius_promotion_rule_has_taxon_per_channel</option>
|
||||
<option name="validateAgainstAllChannels">true</option>
|
||||
<option name="channelAwarePropertyPath">promotion</option>
|
||||
<option name="constraints">
|
||||
<constraint name="Collection">
|
||||
<option name="allowExtraFields">true</option>
|
||||
<option name="groups">sylius_promotion_rule_has_taxon_per_channel</option>
|
||||
<option name="fields">
|
||||
<value key="taxons">
|
||||
<constraint name="All">
|
||||
<option name="groups">sylius_promotion_rule_has_taxon_per_channel</option>
|
||||
<option name="constraints">
|
||||
<constraint name="NotBlank">
|
||||
<option name="groups">sylius_promotion_rule_has_taxon_per_channel</option>
|
||||
</constraint>
|
||||
<constraint name="Sylius\Bundle\CoreBundle\Validator\Constraints\TaxonCodeExists">
|
||||
<option name="groups">sylius_promotion_rule_has_taxon_per_channel</option>
|
||||
</constraint>
|
||||
</option>
|
||||
</constraint>
|
||||
</value>
|
||||
</option>
|
||||
</constraint>
|
||||
</option>
|
||||
</constraint>
|
||||
|
||||
<constraint name="Sylius\Bundle\CoreBundle\Validator\Constraints\ChannelCodeCollection">
|
||||
<option name="allowExtraFields">true</option>
|
||||
<option name="groups">sylius_promotion_rule_contains_product_per_channel</option>
|
||||
<option name="validateAgainstAllChannels">true</option>
|
||||
<option name="channelAwarePropertyPath">promotion</option>
|
||||
<option name="constraints">
|
||||
<constraint name="Collection">
|
||||
<option name="allowExtraFields">true</option>
|
||||
<option name="groups">sylius_promotion_rule_contains_product_per_channel</option>
|
||||
<option name="fields">
|
||||
<value key="product_code">
|
||||
<constraint name="NotBlank">
|
||||
<option name="groups">sylius_promotion_rule_contains_product_per_channel</option>
|
||||
</constraint>
|
||||
<constraint name="Type">
|
||||
<option name="type">string</option>
|
||||
<option name="groups">sylius_promotion_rule_contains_product_per_channel</option>
|
||||
</constraint>
|
||||
<constraint name="Sylius\Bundle\CoreBundle\Validator\Constraints\ProductCodeExists">
|
||||
<option name="groups">sylius_promotion_rule_contains_product_per_channel</option>
|
||||
</constraint>
|
||||
</value>
|
||||
</option>
|
||||
</constraint>
|
||||
</option>
|
||||
</constraint>
|
||||
|
||||
<constraint name="Sylius\Bundle\CoreBundle\Validator\Constraints\PromotionConfigurationChannelCodes">
|
||||
<option name="groups">
|
||||
<value>sylius</value>
|
||||
|
|
|
|||
|
|
@ -35,7 +35,9 @@ sylius:
|
|||
item_percentage_discount: Item percentage discount
|
||||
order_fixed_discount: Order fixed discount
|
||||
order_percentage_discount: Order percentage discount
|
||||
order_percentage_discount_per_channel: Order percentage discount (per channel)
|
||||
shipping_percentage_discount: Shipping percentage discount
|
||||
shipping_percentage_discount_per_channel: Shipping percentage discount (per channel)
|
||||
filters: Filters
|
||||
fixed_discount_configuration:
|
||||
amount: Amount
|
||||
|
|
@ -56,12 +58,18 @@ sylius:
|
|||
price_range: Price range filter
|
||||
promotion_rule:
|
||||
cart_quantity: Cart quantity
|
||||
cart_quantity_per_channel: Cart quantity (per channel)
|
||||
contains_product: Contains product
|
||||
contains_product_per_channel: Contains product (per channel)
|
||||
customer_group: Customer group
|
||||
customer_group_per_channel: Customer group (per channel)
|
||||
has_at_least_one_from_taxons: Has at least one from taxons
|
||||
has_at_least_one_from_taxons_per_channel: Has at least one from taxons (per channel)
|
||||
item_total: Item total
|
||||
nth_order: Nth order
|
||||
nth_order_per_channel: Nth order (per channel)
|
||||
shipping_country: Shipping country
|
||||
shipping_country_per_channel: Shipping country (per channel)
|
||||
taxonomy: Taxonomy
|
||||
total_price_of_items_from_taxon: Total price of items from taxon
|
||||
cart_quantity_configuration:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Component\Core\Promotion\Action;
|
||||
|
||||
use Sylius\Component\Core\Model\OrderInterface;
|
||||
use Sylius\Component\Promotion\Action\PromotionActionCommandInterface;
|
||||
use Sylius\Component\Promotion\Model\PromotionInterface;
|
||||
use Sylius\Component\Promotion\Model\PromotionSubjectInterface;
|
||||
use Sylius\Resource\Exception\UnexpectedTypeException;
|
||||
|
||||
final class PerChannelPromotionActionCommand implements PromotionActionCommandInterface
|
||||
{
|
||||
public function __construct(private PromotionActionCommandInterface $decorated)
|
||||
{
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $configuration */
|
||||
public function execute(PromotionSubjectInterface $subject, array $configuration, PromotionInterface $promotion): bool
|
||||
{
|
||||
if (!$subject instanceof OrderInterface) {
|
||||
throw new UnexpectedTypeException($subject, OrderInterface::class);
|
||||
}
|
||||
|
||||
$channelCode = $subject->getChannel()->getCode();
|
||||
if (!isset($configuration[$channelCode])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->decorated->execute($subject, $configuration[$channelCode], $promotion);
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $configuration */
|
||||
public function revert(PromotionSubjectInterface $subject, array $configuration, PromotionInterface $promotion): void
|
||||
{
|
||||
if (!$subject instanceof OrderInterface) {
|
||||
throw new UnexpectedTypeException($subject, OrderInterface::class);
|
||||
}
|
||||
|
||||
$channelCode = $subject->getChannel()->getCode();
|
||||
if (!isset($configuration[$channelCode])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->decorated->revert($subject, $configuration[$channelCode], $promotion);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sylius\Component\Core\Promotion\Checker\Rule;
|
||||
|
||||
use Sylius\Component\Core\Model\OrderInterface;
|
||||
use Sylius\Component\Promotion\Checker\Rule\RuleCheckerInterface;
|
||||
use Sylius\Component\Promotion\Exception\UnsupportedTypeException;
|
||||
use Sylius\Component\Promotion\Model\PromotionSubjectInterface;
|
||||
|
||||
final class PerChannelRuleChecker implements RuleCheckerInterface
|
||||
{
|
||||
public function __construct(private RuleCheckerInterface $decorated)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $configuration
|
||||
*
|
||||
* @throws UnsupportedTypeException
|
||||
*/
|
||||
public function isEligible(PromotionSubjectInterface $subject, array $configuration): bool
|
||||
{
|
||||
if (!$subject instanceof OrderInterface) {
|
||||
throw new UnsupportedTypeException($subject, OrderInterface::class);
|
||||
}
|
||||
|
||||
$channelCode = $subject->getChannel()->getCode();
|
||||
if (!isset($configuration[$channelCode])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->decorated->isEligible($subject, $configuration[$channelCode]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Sylius\Component\Core\Promotion\Action;
|
||||
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Model\OrderInterface;
|
||||
use Sylius\Component\Core\Promotion\Action\PerChannelPromotionActionCommand;
|
||||
use Sylius\Component\Promotion\Action\PromotionActionCommandInterface;
|
||||
use Sylius\Component\Promotion\Model\PromotionInterface;
|
||||
use Sylius\Component\Promotion\Model\PromotionSubjectInterface;
|
||||
use Sylius\Resource\Exception\UnexpectedTypeException;
|
||||
|
||||
#[CoversClass(PerChannelPromotionActionCommand::class)]
|
||||
final class PerChannelPromotionActionCommandTest extends TestCase
|
||||
{
|
||||
private MockObject&PromotionActionCommandInterface $decorated;
|
||||
|
||||
private ChannelInterface&MockObject $channel;
|
||||
|
||||
private MockObject&OrderInterface $order;
|
||||
|
||||
private MockObject&PromotionInterface $promotion;
|
||||
|
||||
private PerChannelPromotionActionCommand $command;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->decorated = $this->createMock(PromotionActionCommandInterface::class);
|
||||
$this->channel = $this->createMock(ChannelInterface::class);
|
||||
$this->order = $this->createMock(OrderInterface::class);
|
||||
$this->promotion = $this->createMock(PromotionInterface::class);
|
||||
$this->command = new PerChannelPromotionActionCommand($this->decorated);
|
||||
}
|
||||
|
||||
public function testShouldImplementPromotionActionCommandInterface(): void
|
||||
{
|
||||
$this->assertInstanceOf(PromotionActionCommandInterface::class, $this->command);
|
||||
}
|
||||
|
||||
public function testShouldExecuteDecoratedCommandWithTheChannelConfiguration(): void
|
||||
{
|
||||
$this->order->expects($this->once())->method('getChannel')->willReturn($this->channel);
|
||||
$this->channel->expects($this->once())->method('getCode')->willReturn('WEB_US');
|
||||
$this->decorated->expects($this->once())
|
||||
->method('execute')
|
||||
->with($this->order, ['percentage' => 0.2], $this->promotion)
|
||||
->willReturn(true)
|
||||
;
|
||||
|
||||
$this->assertTrue($this->command->execute($this->order, ['WEB_US' => ['percentage' => 0.2]], $this->promotion));
|
||||
}
|
||||
|
||||
public function testShouldNotExecuteDecoratedCommandIfThereIsNoConfigurationForTheOrderChannel(): void
|
||||
{
|
||||
$this->order->expects($this->once())->method('getChannel')->willReturn($this->channel);
|
||||
$this->channel->expects($this->once())->method('getCode')->willReturn('WEB_US');
|
||||
$this->decorated->expects($this->never())->method('execute');
|
||||
|
||||
$this->assertFalse($this->command->execute($this->order, ['WEB_EU' => ['percentage' => 0.2]], $this->promotion));
|
||||
}
|
||||
|
||||
public function testShouldThrowExceptionWhenExecutingIfPromotionSubjectIsNotOrder(): void
|
||||
{
|
||||
$this->expectException(UnexpectedTypeException::class);
|
||||
|
||||
$this->command->execute($this->createMock(PromotionSubjectInterface::class), [], $this->promotion);
|
||||
}
|
||||
|
||||
public function testShouldRevertDecoratedCommandWithTheChannelConfiguration(): void
|
||||
{
|
||||
$this->order->expects($this->once())->method('getChannel')->willReturn($this->channel);
|
||||
$this->channel->expects($this->once())->method('getCode')->willReturn('WEB_US');
|
||||
$this->decorated->expects($this->once())
|
||||
->method('revert')
|
||||
->with($this->order, ['percentage' => 0.2], $this->promotion)
|
||||
;
|
||||
|
||||
$this->command->revert($this->order, ['WEB_US' => ['percentage' => 0.2]], $this->promotion);
|
||||
}
|
||||
|
||||
public function testShouldNotRevertDecoratedCommandIfThereIsNoConfigurationForTheOrderChannel(): void
|
||||
{
|
||||
$this->order->expects($this->once())->method('getChannel')->willReturn($this->channel);
|
||||
$this->channel->expects($this->once())->method('getCode')->willReturn('WEB_US');
|
||||
$this->decorated->expects($this->never())->method('revert');
|
||||
|
||||
$this->command->revert($this->order, ['WEB_EU' => ['percentage' => 0.2]], $this->promotion);
|
||||
}
|
||||
|
||||
public function testShouldThrowExceptionWhenRevertingIfPromotionSubjectIsNotOrder(): void
|
||||
{
|
||||
$this->expectException(UnexpectedTypeException::class);
|
||||
|
||||
$this->command->revert($this->createMock(PromotionSubjectInterface::class), [], $this->promotion);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Sylius package.
|
||||
*
|
||||
* (c) Sylius Sp. z o.o.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Sylius\Component\Core\Promotion\Checker\Rule;
|
||||
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Sylius\Component\Core\Model\ChannelInterface;
|
||||
use Sylius\Component\Core\Model\OrderInterface;
|
||||
use Sylius\Component\Core\Promotion\Checker\Rule\PerChannelRuleChecker;
|
||||
use Sylius\Component\Promotion\Checker\Rule\RuleCheckerInterface;
|
||||
use Sylius\Component\Promotion\Exception\UnsupportedTypeException;
|
||||
use Sylius\Component\Promotion\Model\PromotionSubjectInterface;
|
||||
|
||||
#[CoversClass(PerChannelRuleChecker::class)]
|
||||
final class PerChannelRuleCheckerTest extends TestCase
|
||||
{
|
||||
private MockObject&RuleCheckerInterface $decorated;
|
||||
|
||||
private ChannelInterface&MockObject $channel;
|
||||
|
||||
private MockObject&OrderInterface $order;
|
||||
|
||||
private PerChannelRuleChecker $ruleChecker;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->decorated = $this->createMock(RuleCheckerInterface::class);
|
||||
$this->channel = $this->createMock(ChannelInterface::class);
|
||||
$this->order = $this->createMock(OrderInterface::class);
|
||||
$this->ruleChecker = new PerChannelRuleChecker($this->decorated);
|
||||
}
|
||||
|
||||
public function testShouldImplementRuleCheckerInterface(): void
|
||||
{
|
||||
$this->assertInstanceOf(RuleCheckerInterface::class, $this->ruleChecker);
|
||||
}
|
||||
|
||||
public function testShouldDelegateToDecoratedCheckerWithTheChannelConfiguration(): void
|
||||
{
|
||||
$this->order->expects($this->once())->method('getChannel')->willReturn($this->channel);
|
||||
$this->channel->expects($this->once())->method('getCode')->willReturn('WEB_US');
|
||||
$this->decorated->expects($this->once())
|
||||
->method('isEligible')
|
||||
->with($this->order, ['nth' => 5])
|
||||
->willReturn(true)
|
||||
;
|
||||
|
||||
$this->assertTrue($this->ruleChecker->isEligible($this->order, ['WEB_US' => ['nth' => 5]]));
|
||||
}
|
||||
|
||||
public function testShouldReturnDecoratedCheckerResultForTheOrderChannel(): void
|
||||
{
|
||||
$this->order->expects($this->once())->method('getChannel')->willReturn($this->channel);
|
||||
$this->channel->expects($this->once())->method('getCode')->willReturn('WEB_US');
|
||||
$this->decorated->expects($this->once())
|
||||
->method('isEligible')
|
||||
->with($this->order, ['nth' => 5])
|
||||
->willReturn(false)
|
||||
;
|
||||
|
||||
$this->assertFalse($this->ruleChecker->isEligible($this->order, ['WEB_US' => ['nth' => 5]]));
|
||||
}
|
||||
|
||||
public function testShouldReturnFalseAndNotDelegateIfThereIsNoConfigurationForTheOrderChannel(): void
|
||||
{
|
||||
$this->order->expects($this->once())->method('getChannel')->willReturn($this->channel);
|
||||
$this->channel->expects($this->once())->method('getCode')->willReturn('WEB_US');
|
||||
$this->decorated->expects($this->never())->method('isEligible');
|
||||
|
||||
$this->assertFalse($this->ruleChecker->isEligible($this->order, ['WEB_EU' => ['nth' => 5]]));
|
||||
}
|
||||
|
||||
public function testShouldThrowExceptionIfPromotionSubjectIsNotOrder(): void
|
||||
{
|
||||
$this->expectException(UnsupportedTypeException::class);
|
||||
|
||||
$this->ruleChecker->isEligible($this->createMock(PromotionSubjectInterface::class), []);
|
||||
}
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
},
|
||||
{
|
||||
"propertyPath": "actions[5].type",
|
||||
"message": "Promotion action type is invalid. Available action types are order_fixed_discount, unit_fixed_discount, order_percentage_discount, unit_percentage_discount, shipping_percentage_discount.",
|
||||
"message": "Promotion action type is invalid. Available action types are order_fixed_discount, unit_fixed_discount, order_percentage_discount, unit_percentage_discount, shipping_percentage_discount, order_percentage_discount_per_channel, shipping_percentage_discount_per_channel.",
|
||||
"code": null
|
||||
}
|
||||
],
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@
|
|||
},
|
||||
{
|
||||
"propertyPath": "rules[8].type",
|
||||
"message": "Promotion rule type is invalid. Available rule types are customer_group, nth_order, shipping_country, has_taxon, total_of_items_from_taxon, contains_product, item_total, cart_quantity.",
|
||||
"message": "Promotion rule type is invalid. Available rule types are customer_group, nth_order, shipping_country, has_taxon, total_of_items_from_taxon, contains_product, item_total, cart_quantity, customer_group_per_channel, nth_order_per_channel, shipping_country_per_channel, has_taxon_per_channel, contains_product_per_channel, cart_quantity_per_channel.",
|
||||
"code": null
|
||||
}
|
||||
],
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue