diff --git a/code_samples/data_migration/examples/discounts/discount_code_create.yaml b/code_samples/data_migration/examples/discounts/discount_code_create.yaml new file mode 100644 index 0000000000..7b2358d364 --- /dev/null +++ b/code_samples/data_migration/examples/discounts/discount_code_create.yaml @@ -0,0 +1,7 @@ +type: discount_code +mode: create +code: summer10 +global_usage_limit: 100 # Optional +user_usage_limit: 5 # Optional +created_at: '2023-01-01T12:00:00+00:00' # Optional +creator_id: 42 # Optional diff --git a/code_samples/discounts/src/Command/ManageDiscountsCommand.php b/code_samples/discounts/src/Command/ManageDiscountsCommand.php index 6243da8ca6..0471f80201 100644 --- a/code_samples/discounts/src/Command/ManageDiscountsCommand.php +++ b/code_samples/discounts/src/Command/ManageDiscountsCommand.php @@ -59,7 +59,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int $discountCodeCreateStruct = new DiscountCodeCreateStruct( 'summer10', - null, // Unlimited usage + 10, // Global usage limit + null, // Unlimited usage per customer $this->permissionResolver->getCurrentUserReference()->getUserId(), $now ); @@ -78,7 +79,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int new IsInRegions(['germany', 'france']), new IsProductInArray(['product-1', 'product-2']), new IsInCurrency('EUR'), - new IsValidDiscountCode($discountCode->getCode(), $discountCode->getUsedLimit()), + new IsValidDiscountCode( + $discountCode->getCode(), + $discountCode->getGlobalLimit(), + $discountCode->getUsedLimit() + ), ]) ->setTranslations([ new DiscountTranslationStruct('eng-GB', 'Discount name', 'This is a discount description', 'Promotion Label', 'Promotion Description'), diff --git a/docs/api/event_reference/discounts_events.md b/docs/api/event_reference/discounts_events.md index f33e24214c..5a7098e23e 100644 --- a/docs/api/event_reference/discounts_events.md +++ b/docs/api/event_reference/discounts_events.md @@ -15,12 +15,12 @@ The events below are dispatched when managing [discounts](discounts_guide.md): | Event | Dispatched by | |---|---| -|[BeforeCreateDiscountEvent](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-Event-BeforeCreateDiscountEvent.html)| [DiscountServiceInterface](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-DiscountServiceInterface.html) -|[CreateDiscountEvent](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-Event-CreateDiscountEvent.html)| [DiscountServiceInterface](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-DiscountServiceInterface.html) -|[BeforeDeleteDiscountEvent](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-Event-BeforeDeleteDiscountEvent.html)| [DiscountServiceInterface](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-DiscountServiceInterface.html) -|[DeleteDiscountEvent](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-Event-DeleteDiscountEvent.html)| [DiscountServiceInterface](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-DiscountServiceInterface.html) +|[BeforeCreateDiscountEvent](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-Event-BeforeCreateDiscountEvent.html)| [DiscountServiceInterface](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-DiscountServiceInterface.html) | +|[CreateDiscountEvent](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-Event-CreateDiscountEvent.html)| [DiscountServiceInterface](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-DiscountServiceInterface.html) | +|[BeforeDeleteDiscountEvent](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-Event-BeforeDeleteDiscountEvent.html)| [DiscountServiceInterface](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-DiscountServiceInterface.html) | +|[DeleteDiscountEvent](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-Event-DeleteDiscountEvent.html)| [DiscountServiceInterface](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-DiscountServiceInterface.html) | |[BeforeUpdateDiscountEvent](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-Event-BeforeUpdateDiscountEvent.html)| [DiscountServiceInterface](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-DiscountServiceInterface.html)| -|[UpdateDiscountEvent](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-Event-UpdateDiscountEvent.html)| [DiscountServiceInterface](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-DiscountServiceInterface.html)| +|[UpdateDiscountEvent](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-Event-UpdateDiscountEvent.html)| [DiscountServiceInterface](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-DiscountServiceInterface.html) | ## Form events diff --git a/docs/content_management/data_migration/importing_data.md b/docs/content_management/data_migration/importing_data.md index 21abb9ab84..4774b36646 100644 --- a/docs/content_management/data_migration/importing_data.md +++ b/docs/content_management/data_migration/importing_data.md @@ -56,6 +56,7 @@ The following data migration step modes are available: | `currency` | ✔ | ✔ | ✔ | | | `customer_group` | ✔ | ✔ | ✔ | | | `discount` | ✔ | ✔ | | | +| `discount_code` | ✔ | | | | | `language` | ✔ | | | | | `location` | | ✔ | | ✔ | | `object_state` | ✔ | | | | @@ -525,6 +526,14 @@ The provided conditions overwrite any already existing ones. For a list of available conditions, see [Discounts API](discounts_api.md#conditions). +### Discount codes [[% include 'snippets/lts-update_badge.md' %]] + +You can create a discount code as in the following example: + +``` yaml +[[= include_file('code_samples/data_migration/examples/discounts/discount_code_create.yaml') =]] +``` + ## Criteria When using `update` or `delete` modes, you can use criteria to identify the objects to operate on. diff --git a/docs/discounts/discounts_api.md b/docs/discounts/discounts_api.md index cb0fc19931..d8ab35759c 100644 --- a/docs/discounts/discounts_api.md +++ b/docs/discounts/discounts_api.md @@ -113,9 +113,9 @@ The example below contains a Command creating a cart discount. The discount: - [depends](#conditions) on - being bought from Germany or France - 2 products - - a `summer10` [discount code](#discount-codes) which can be used unlimited number of times + - a `summer10` [discount code](#discount-codes) which can be used only 10 times, but a single customer can use the code multiple times -``` php hl_lines="60-66 68-92" +``` php hl_lines="60-67 69-97" [[= include_file('code_samples/discounts/src/Command/ManageDiscountsCommand.php') =]] ``` diff --git a/docs/discounts/discounts_guide.md b/docs/discounts/discounts_guide.md index 7c0364bb2f..71a1eef50d 100644 --- a/docs/discounts/discounts_guide.md +++ b/docs/discounts/discounts_guide.md @@ -59,6 +59,7 @@ A shopping cart can have multiple active discounts, but a specific product can o When two or more discounts can be applied to a single product, the system evaluates the following properties to choose the right one: +- discount code existence (discounts with discount codes have priority over the others) - discount activation place (cart discounts rank higher over catalog discounts) - discount priority (higher priority ranks higher) - discount creation date (newer discounts rank higher) @@ -107,7 +108,8 @@ These conditions can include: For **cart discounts**, you can specify an additional text value that needs to be entered in the cart for the discount to apply. -The discount code usage can be limited per customer: +The discount code usage can be limited globally, for example by making the discount valid only for the first 10 customers before it expires. +You can also limit the usage per customer: - single use: every customer can use this code only once - limited use: every customer can use the code a specified number of times diff --git a/docs/release_notes/ibexa_dxp_v4.6.md b/docs/release_notes/ibexa_dxp_v4.6.md index 477a54b753..98e66e2ca3 100644 --- a/docs/release_notes/ibexa_dxp_v4.6.md +++ b/docs/release_notes/ibexa_dxp_v4.6.md @@ -10,6 +10,43 @@ month_change: true
+[[% set version = 'v4.6.22' %]] +[[= release_note_entry_begin("Discounts " + version, 'TODO', ['LTS Update', 'Commerce']) =]] + +#### Global discount codes limits + +- You can now [limit the number of times](discounts_guide.md#discount-codes) a discount code can be used before it expires. The discounts created before this release are set to unlimited global usage + +#### Discount codes prioritization + +- Discounts with discount codes now have priority over the other discounts + +#### Discount codes migrations + +- You can now create discount codes using [data migrations](importing_data.md#discount-codes). + +#### PHP API + +The PHP API has been enhanced with the following new classes: + +- TODO + +[[= release_note_entry_end() =]] +[[= release_note_entry_begin("Ibexa DXP " + version, 'TODO', ['Headless', 'Experience', 'Commerce']) =]] + +#### TODO + +#### PHP API + +The PHP API has been enhanced with the following new classes: + +- TODO + +#### Full changelog + +[[% include 'snippets/release_46.md' %]] +[[= release_note_entry_end() =]] + [[% set version = 'v4.6.21' %]] [[= release_note_entry_begin("Discounts " + version, '2025-06-11', ['LTS Update', 'Commerce']) =]] diff --git a/docs/update_and_migration/from_4.6/update_from_4.6.md b/docs/update_and_migration/from_4.6/update_from_4.6.md index c8d1cfbcab..a4aa8cad2d 100644 --- a/docs/update_and_migration/from_4.6/update_from_4.6.md +++ b/docs/update_and_migration/from_4.6/update_from_4.6.md @@ -372,6 +372,10 @@ Run the following scripts: psql < vendor/ibexa/installer/upgrade/db/postgresql/ibexa-4.6.20-to-4.6.21.sql ``` +## v4.6.22 + +No additional steps needed. + [[% include 'snippets/update/notify_support.md' %]] With the product updated to the latest version, you can now finish the update process or proceed to updating the LTS Updates packages. @@ -391,16 +395,16 @@ To use the [latest features](ibexa_dxp_v4.6.md) added to them, update them separ Then apply manually the changes described below. - ## 4.6.20 + ### Discounts v4.6.20 - ### Policy changes + #### Policy changes The `discount/view` policy is no longer required for the store customers to use a discount and must be removed from all users who are not managing discounts. The policy allows to access all the discount details, including the coupon codes to activate them, which could lead to system abuse. To learn more, see the [discounts policies overview](policies.md#discounts). - ### Database update + #### Database update Run the following scripts: @@ -522,6 +526,28 @@ To use the [latest features](ibexa_dxp_v4.6.md) added to them, update them separ REFERENCES ezuser (contentobject_id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE; ``` + ### Discounts v4.6.22 + + #### Database update + + Run the following scripts: + + === "MySQL" + + ``` sql + ALTER TABLE ibexa_discount ADD override_prioritization tinyint(1) NOT NULL DEFAULT 0; + CREATE INDEX ibexa_discount_prioritization_idx ON ibexa_discount (override_prioritization, type, priority); + ALTER TABLE ibexa_discount_code ADD global_limit INT DEFAULT NULL; + ``` + + === "PostgreSQL" + + ``` sql + ALTER TABLE ibexa_discount ADD override_prioritization tinyint(1) NOT NULL DEFAULT 0; + CREATE INDEX ibexa_discount_prioritization_idx ON ibexa_discount (override_prioritization, type, priority); + ALTER TABLE ibexa_discount_code ADD global_limit INT DEFAULT NULL; + ``` + === "AI actions" Run the following command to get the latest version: