Developing Custom Discount Logic in 1C-Bitrix
The standard Bitrix discount constructor covers most typical scenarios. But when you need a discount that depends on external CRM, order history for a quarter, competitor inventory, or time of day — standard tools run out. Then custom logic is written.
Two levels of extension
Level 1: Custom basket event handler
The OnBeforeSaleOrderFinalAction event allows intervening at the moment of basket calculation before final discount application. The OnSaleBasketItemAdd event allows intercepting product addition.
Example: 5% discount on entire order if customer already made a purchase this month:
AddEventHandler('sale', 'OnBeforeSaleOrderFinalAction', function(&$order) {
$userId = $order->getUserId();
$currentMonth = date('Y-m');
$prevOrders = \Bitrix\Sale\Order::load(/* filter by userId and month */);
if (!empty($prevOrders)) {
// apply programmatic discount
$basket = $order->getBasket();
foreach ($basket as $item) {
$price = $item->getPrice();
$item->setField('PRICE', $price * 0.95);
$item->setField('DISCOUNT_PRICE', $price * 0.05);
}
}
});
Level 2: Custom discount provider
A cleaner approach — implement the \Bitrix\Sale\Discount\DiscountProviderInterface interface and register your own provider. The provider receives the basket as input and returns a list of applied discounts in standard format. Standard rules from b_sale_discount continue to work in parallel or replace completely — depends on configuration.
Custom conditions in rule constructor
You can extend the list of available conditions in the marketing rules constructor without replacing the entire mechanism. Create an inheriting class of \Bitrix\Sale\Discount\Actions\Base or a condition inheritor of \Bitrix\Sale\Discount\Condition\Base:
namespace MyModule\Discount\Condition;
class PreviousOrdersCount extends \Bitrix\Sale\Discount\Condition\Base
{
public function check(\Bitrix\Sale\Order $order): bool
{
// check logic
}
}
The class is registered via the module's events.php, and a new "Number of previous orders" condition appears in the rules constructor.
Custom discounts from external systems
For integration with external CRM or ERP where individual customer discounts are stored:
- When user logs in, request their discount profile from external system
- Save in session or in user
UF_fields - Basket event handler reads this data and applies the discount
Caching the response from external system is mandatory — each request on every basket change to external API creates unacceptable delays. Cache in Redis/Memcached with TTL 15–60 minutes.
Debugging custom discount logic
Logging applied discounts: b_sale_order_discount stores all rules applied to an order. For debugging custom discounts, add a record to this journal with "custom rule" identifier so that in the admin panel you can see exactly what was applied.
Estimated timeframes
| Task | Timeframe |
|---|---|
| Custom event handler for one rule | 1–2 days |
| New condition in marketing rules constructor | 2–3 days |
| Full custom discount provider | 1–2 weeks |
| External CRM discount integration with caching | 3–5 days |







