Bitrix API правил работы с корзиной

Представим себе, что перед нами стоит следующая прикладная задача:

Есть правило работы с корзиной, применяющее 10% скидку на определённые товары:

Необходимо на уровне Bitrix API добавить в это правило еще несколько товаров. Данная задача актуальна, когда товаров много и вручную добавлять слишком трудозатратно.

Прежде чем приступить к решению задачи, стоит уточнить, что начиная с версии 16.6.0 модулей Информационные блокиТорговый каталог и Интернет-магазин, доступны объединенные скидки каталога и магазина. Соответственно после конвертации правила работы с корзиной «переплыли» с CCatalogDiscount в CSaleDiscount. В новых же версиях Битрикс необходимо пользоваться классом CSaleDiscount.

Для решения задачи посмотрим, что там под катом у нашего правила работы с корзиной (2 — это ID правила работы с корзиной):

CModule::IncludeModule('sale');
echo '<pre>';
print_r(CSaleDiscount::GetByID(2));
echo '</pre>';

Результат:

Array
(
    [ID] => 2
    [XML_ID] => 
    [LID] => s1
    [SITE_ID] => s1
    [NAME] => скидка 10%
    [PRICE_FROM] => 
    [PRICE_TO] => 
    [CURRENCY] => RUB
    [DISCOUNT_VALUE] => 0.00
    [DISCOUNT_TYPE] => P
    [ACTIVE] => Y
    [SORT] => 100
    [ACTIVE_FROM] => 
    [ACTIVE_TO] => 
    [TIMESTAMP_X] => 13.12.2018 10:45:20
    [MODIFIED_BY] => 1
    [DATE_CREATE] => 12.12.2018 17:23:30
    [CREATED_BY] => 1
    [PRIORITY] => 1
    [LAST_DISCOUNT] => Y
    [VERSION] => 3
    [CONDITIONS] => a:3:{s:8:"CLASS_ID";s:9:"CondGroup";s:4:"DATA";a:2:{s:3:"All";s:3:"AND";s:4:"True";s:4:"True";}s:8:"CHILDREN";a:0:{}}
    [UNPACK] => function($arOrder){return ((1 == 1)); };
    [APPLICATION] => function (&$arOrder){$saleact_0_0=function($row){return ((isset($row['CATALOG']['PARENT_ID']) ? ((isset($row['CATALOG']['ID']) && ($row['CATALOG']['ID'] == 10 || $row['CATALOG']['ID'] == 18)) || ($row['CATALOG']['PARENT_ID'] == 10 || $row['CATALOG']['PARENT_ID'] == 18)) : (isset($row['CATALOG']['ID']) && ($row['CATALOG']['ID'] == 10 || $row['CATALOG']['ID'] == 18))));};\Bitrix\Sale\Discount\Actions::applyToBasket($arOrder, array (
  'VALUE' => -10.0,
  'UNIT' => 'P',
  'LIMIT_VALUE' => 0,
), $saleact_0_0);};
    [ACTIONS] => a:3:{s:8:"CLASS_ID";s:9:"CondGroup";s:4:"DATA";a:1:{s:3:"All";s:3:"AND";}s:8:"CHILDREN";a:1:{i:0;a:3:{s:8:"CLASS_ID";s:14:"ActSaleBsktGrp";s:4:"DATA";a:6:{s:4:"Type";s:8:"Discount";s:5:"Value";i:10;s:4:"Unit";s:4:"Perc";s:3:"Max";i:0;s:3:"All";s:3:"AND";s:4:"True";s:4:"True";}s:8:"CHILDREN";a:1:{i:0;a:2:{s:8:"CLASS_ID";s:13:"CondIBElement";s:4:"DATA";a:2:{s:5:"logic";s:5:"Equal";s:5:"value";a:2:{i:0;s:2:"18";i:1;s:2:"10";}}}}}}}
)

Содержимое элемента массива с ключем «ACTIONS» — это сериализованный массив, содержащий параметры выполняемых действий правила работы с корзиной.

Посмотрим, что внутри:

CModule::IncludeModule('sale');
echo '<pre>';
print_r(unserialize(CSaleDiscount::GetByID(2)['ACTIONS']));
echo '</pre>';

Результат:

Array
(
    [CLASS_ID] => CondGroup
    [DATA] => Array
        (
            [All] => AND
        )

    [CHILDREN] => Array
        (
            [0] => Array
                (
                    [CLASS_ID] => ActSaleBsktGrp
                    [DATA] => Array
                        (
                            [Type] => Discount
                            [Value] => 10
                            [Unit] => Perc
                            [Max] => 0
                            [All] => AND
                            [True] => True
                        )

                    [CHILDREN] => Array
                        (
                            [0] => Array
                                (
                                    [CLASS_ID] => CondIBElement
                                    [DATA] => Array
                                        (
                                            [logic] => Equal
                                            [value] => Array
                                                (
                                                    [0] => 18
                                                    [1] => 10
                                                )

                                        )

                                )

                        )

                )

        )

)

18 и 10 — это ID наших товаров, которые в данный момент времени присутствуют в правиле работы с корзиной.

Собственно код, добавляющий товар с ID = $item_id в правило работы с корзиной:

CModule::IncludeModule('sale');
//получим список товаров в правиле работы с корзиной:
$new_ids = [];
$new_ids = unserialize(CSaleDiscount::GetByID(2)['ACTIONS'])['CHILDREN'][0]['CHILDREN'][0]['DATA']['value'];
$new_ids[] = $item_id;
$new_ids = array_unique($new_ids);
//добавим в правило работы с корзиной:
$upd = [
	'ACTIVE'=>'Y',
	'ACTIONS'=>[
		'CLASS_ID' => 'CondGroup',
		'DATA' => ['All'=>'AND'],
		'CHILDREN' =>[
			[
				'CLASS_ID'=>'ActSaleBsktGrp',
				'DATA'=> [
					'Type'=>'Discount',
					'Value'=>10,
					'Unit'=>'Perc',
					'Max'=>0,
					'All'=>'AND',
					'True'=>'True'
				],
				'CHILDREN'=>[
					[
						'CLASS_ID'=>'CondIBElement',
						'DATA'=>[
							'logic'=>'Equal',
							'value'=>$new_ids
						]
					]
				]
			]
		]
	]
];
CSaleDiscount::Update(2, $upd );

p.s. Обращаю внимание, что создание правил работы с корзиной основанной на выборке товаров по ID может привести к проблемам с производительностью. Эта проблема обычно возникает, когда товаров в правиле много. Для улучшения ситуации лучше использовать идею, описанную в этой статье:

https://dev.1c-bitrix.ru/community/blogs/vws/setup-discounts-problems-and-drop-performance.php

Если кратко: добавить в инфоблок с товарами свойство — идентификатор скидки и уже на основе его значения формировать правило работы с корзиной.