diff --git a/code_samples/discounts/src/Command/ManageDiscountsCommand.php b/code_samples/discounts/src/Command/ManageDiscountsCommand.php
new file mode 100644
index 0000000000..6243da8ca6
--- /dev/null
+++ b/code_samples/discounts/src/Command/ManageDiscountsCommand.php
@@ -0,0 +1,96 @@
+userService = $userSerice;
+ $this->discountService = $discountService;
+ $this->discountCodeService = $discountCodeService;
+ $this->permissionResolver = $permissionResolver;
+
+ parent::__construct();
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $this->permissionResolver->setCurrentUserReference(
+ $this->userService->loadUserByLogin('admin')
+ );
+
+ $now = new DateTimeImmutable();
+
+ $discountCodeCreateStruct = new DiscountCodeCreateStruct(
+ 'summer10',
+ null, // Unlimited usage
+ $this->permissionResolver->getCurrentUserReference()->getUserId(),
+ $now
+ );
+ $discountCode = $this->discountCodeService->createDiscountCode($discountCodeCreateStruct);
+
+ $discountCreateStruct = new DiscountCreateStruct();
+ $discountCreateStruct
+ ->setIdentifier('discount_identifier')
+ ->setType(DiscountType::CART)
+ ->setPriority(10)
+ ->setEnabled(true)
+ ->setUser($this->userService->loadUserByLogin('admin'))
+ ->setRule(new FixedAmount(10))
+ ->setStartDate($now)
+ ->setConditions([
+ new IsInRegions(['germany', 'france']),
+ new IsProductInArray(['product-1', 'product-2']),
+ new IsInCurrency('EUR'),
+ new IsValidDiscountCode($discountCode->getCode(), $discountCode->getUsedLimit()),
+ ])
+ ->setTranslations([
+ new DiscountTranslationStruct('eng-GB', 'Discount name', 'This is a discount description', 'Promotion Label', 'Promotion Description'),
+ new DiscountTranslationStruct('ger-DE', 'Discount name (German)', 'Description (German)', 'Promotion Label (German)', 'Promotion Description (German)'),
+ ])
+ ->setEndDate(null) // Permanent discount
+ ->setCreatedAt($now)
+ ->setUpdatedAt($now)
+ ->setContext(new ArrayMap(['custom_context' => 'custom_value']));
+
+ $this->discountService->createDiscount($discountCreateStruct);
+
+ return Command::SUCCESS;
+ }
+}
diff --git a/docs/content_management/data_migration/importing_data.md b/docs/content_management/data_migration/importing_data.md
index 36c9e67dd1..e1da81407a 100644
--- a/docs/content_management/data_migration/importing_data.md
+++ b/docs/content_management/data_migration/importing_data.md
@@ -522,6 +522,8 @@ The provided conditions overwrite any already existing ones.
[[= include_file('code_samples/data_migration/examples/discounts/discount_update.yaml') =]]
```
+For a list of available conditions, see [Discounts API](discounts_api.md#conditions).
+
## Criteria
When using `update` or `delete` modes, you can use criteria to identify the objects to operate on.
diff --git a/docs/discounts/discounts.md b/docs/discounts/discounts.md
index 78d4070de2..322335a76c 100644
--- a/docs/discounts/discounts.md
+++ b/docs/discounts/discounts.md
@@ -16,5 +16,6 @@ You can also extend the feature, for example, by creating custom pricing rules,
[[= cards([
"discounts/discounts_guide",
-"discounts/install_discounts"
-], columns=4) =]]
+"discounts/install_discounts",
+"discounts/discounts_api"
+], columns=3) =]]
diff --git a/docs/discounts/discounts_api.md b/docs/discounts/discounts_api.md
new file mode 100644
index 0000000000..c6ef554b98
--- /dev/null
+++ b/docs/discounts/discounts_api.md
@@ -0,0 +1,129 @@
+---
+description: Discounts LTS Update enables reducing prices on products or product categories based on a detailed logic resolution.
+month_change: true
+editions:
+ - lts-update
+ - commerce
+---
+
+# Discounts API
+
+## Manage discounts and discount codes
+
+By integrating with the [Discount feature](discounts_guide.md) you can automate the process of managing discounts, streamlining the whole process and automating business rules.
+
+For example, you can automatically create a discount when a customer places their 3rd order, encouraging them to make another purchase and increase their chances of becoming a local customer.
+
+You can manage discounts using [data migrations](importing_data.md#discounts), [REST API](/api/rest_api/rest_api_reference/rest_api_reference.html#discounts), or the PHP API by using the [`Ibexa\Contracts\Discounts\DiscountServiceInterface`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-DiscountServiceInterface.html) service.
+
+The core concepts when working with discounts through the APIs are listed below.
+
+### Types
+
+When using the PHP API, the discount type defines where the discount can be applied.
+
+Discounts are applied in two places, listed in the [`DiscountType`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-Value-DiscountType.html) class:
+
+- **Product catalog** - `catalog` discounts are activated when browsing the product catalog and do not require any action from the customer to be activated
+- **Cart** - `cart` discounts can activate when entering the [cart](cart.md), if the right conditions are met. They may also require entering a discount code to be activated
+
+Regardless of activation place, discounts always apply to products and reduce their base price.
+
+To define when a discount activates and how the price is reduced, use rules and conditions.
+They make use of the [Symfony Expression language]([[= symfony_doc=]]//components/expression_language.html).
+Use the expression values provided below when using data migrations or when parsing REST API responses.
+
+### Rules
+
+Discount rules define how the calculate the price reduction.
+The following discount rule types are available:
+
+| Rule type | Identifier | Description | Expression value |
+|---|---|---|---|
+| `Ibexa\Discounts\Value\DiscountRule\FixedAmount` | `fixed_amount` | Deducts the specified amount, for example 10 EUR, from the base price | `discount_amount` |
+| `Ibexa\Discounts\Value\DiscountRule\Percentage` | `percentage` | Deducts the specified percentage, for example -10%, from the base price | `discount_percentage` |
+
+Only a single discount can be applied to a given product, and a discount can only have a single rule.
+
+### Conditions
+
+With conditions you can narrow down the scenarios in which the discount applies. The following conditions are available:
+
+| Condition | Applies to | Identifier | Description | Expression values |
+|---|---|---|---|---|
+| `Ibexa\Discounts\Value\DiscountCondition\IsInCategory` | Cart, Catalog | `is_in_category` | Checks if the product belongs to specified [product categories]([[= user_doc =]]/pim/work_with_product_categories) | `categories` |
+| `Ibexa\Discounts\Value\DiscountCondition\IsInCurrency` | Cart, Catalog |`is_in_currency` | Checks if the product has price in the specified currency | `currency_code` |
+| `Ibexa\Discounts\Value\DiscountCondition\IsInRegions` | Cart, Catalog | `is_in_regions` | Checks if the customer is making the purchase in one of the specified regions | `regions` |
+| `Ibexa\Discounts\Value\DiscountCondition\IsProductInArray` | Cart, Catalog| `is_product_in_array` | Checks if the product belongs to the group of selected products | `product_codes` |
+| `Ibexa\Discounts\Value\DiscountCondition\IsUserInCustomerGroup` | Cart, Catalog| `is_user_in_customer_group` | Check if the customer belongs to specified [customer groups](customer_groups.md) | `customer_groups` |
+| `Ibexa\Discounts\Value\DiscountCondition\IsProductInQuantityInCart` | Cart | `is_product_in_quantity_in_cart` | Checks if the required minimum quantity of a given product is present in the cart | `quantity` |
+| `Ibexa\Discounts\Value\DiscountCondition\MinimumPurchaseAmount` | Cart | `minimum_purchase_amount` | Checks if purchase amount in the cart exceeds the specified minimum | `minimum_purchase_amount` |
+| `Ibexa\DiscountsCodes\Value\DiscountCondition\IsValidDiscountCode` | Cart | `is_valid_discount_code` | Checks if the correct discount code has been provided and how many times it was used by the customer | `discount_code`, `usage_count` |
+
+When multiple conditions are specified, all of them must be met.
+
+### Priority
+
+You can set discount priority as a number between 1 and 10 to indicate which discount should have [higher priority](discounts_guide.md#discounts-priority) when choosing the one to apply.
+
+### Start and end date
+
+Discounts can be permanent, or valid only in a specified time frame.
+
+Every discount has a start date, which defaults to the date when the discount was created.
+The end date can be set to `null` to make the discount permanent.
+
+### Status
+
+You can disable a discount anytime to stop it from being active, even if the conditions enforced by start and end date are met.
+
+Only disabled discounts can be deleted.
+
+### Discount translations
+
+The discount has four properties that can be translated:
+
+| Property | Usage |
+|---|---|
+| Name | Internal information for store managers |
+| Description | Internal information for store managers |
+| Promotion label | Information displayed to customers |
+| Promotion description | Information displayed to customers |
+
+Use the [`DiscountTranslationStruct`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-Value-Struct-DiscountTranslationStruct.html) to provide translations for discounts.
+
+### Discount codes
+
+To activate a cart discount only after a proper discount code is provided, you need to:
+
+1. Create a discount code using the [`DiscountCodeServiceInterface::createDiscountCode()`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-DiscountsCodes-DiscountCodeServiceInterface.html#method_createDiscountCode) method
+1. Attach it to a discount by using the `IsValidDiscountCode` condition
+
+Set the [`usedLimit`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-DiscountsCodes-Value-Struct-DiscountCodeCreateStruct.html#method___construct) property to the number of times a single customer can use this code, or to `null` to make the usage unlimited.
+
+The [`DiscountCodeServiceInterface::registerUsage()`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-DiscountsCodes-DiscountCodeServiceInterface.html#method_registerUsage) method is used to track the number of times a discount code has been used.
+
+### Example API usage
+
+The example below contains a Command creating a cart discount. The discount:
+
+- has the highest possible [priority](#priority) value
+- [rule](#rules) deducts 10 EUR from the base price of the product
+- is [permanent](#start-and-end-date)
+- [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
+
+``` php hl_lines="60-66 68-92"
+[[= include_file('code_samples/discounts/src/Command/ManageDiscountsCommand.php') =]]
+```
+
+Similarly, use the `deleteDiscount`, `deleteTranslation`, `disableDiscount`, `enableDiscount`, and `updateDiscount` methods from the [DiscountServiceInterface](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-DiscountServiceInterface.html) to manage the discounts. You can always attach additional logic to the Discounts API by listening to the [available events](discounts_events.md).
+
+## Search
+
+You can search for Discounts using the [`DiscountServiceInterface::findDiscounts()`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-Discounts-DiscountServiceInterface.html#method_findDiscounts) method.
+To learn more about the available search options, see Discounts' [Search Criteria](discounts_criteria.md) and [Sort Clauses](discounts_sort_clauses.md).
+
+For discount codes, you can query the database for discount code usage using [`DiscountCodeServiceInterface::findCodeUsages()`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-DiscountsCodes-DiscountCodeServiceInterface.html#method_findCodeUsages) and [`DiscountCodeUsageQuery`](/api/php_api/php_api_reference/classes/Ibexa-Contracts-DiscountsCodes-Value-Query-DiscountCodeUsageQuery.html).
diff --git a/docs/discounts/discounts_guide.md b/docs/discounts/discounts_guide.md
index 615d02c737..7c0364bb2f 100644
--- a/docs/discounts/discounts_guide.md
+++ b/docs/discounts/discounts_guide.md
@@ -1,6 +1,6 @@
---
description: Discounts LTS Update enables reducing prices on products or product categories based on a detailed logic resolution.
-month_change: false
+month_change: true
editions:
- lts-update
- commerce
@@ -55,6 +55,8 @@ Discounts are applied in two places:
A shopping cart can have multiple active discounts, but a specific product can only have a single discount applied to it at a time.
+#### Discounts priority
+
When two or more discounts can be applied to a single product, the system evaluates the following properties to choose the right one:
- discount activation place (cart discounts rank higher over catalog discounts)
diff --git a/mkdocs.yml b/mkdocs.yml
index 20be1066a9..6cabd93a4c 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -406,6 +406,7 @@ nav:
- Discounts: discounts/discounts.md
- Discounts guide: discounts/discounts_guide.md
- Install Discounts: discounts/install_discounts.md
+ - Discounts API: discounts/discounts_api.md
- Customer management:
- Customer Portal: customer_management/customer_portal.md
- Customer Portal guide: customer_management/customer_portal_guide.md