Yandex Metrika
sanches.free 17 просмотров

События каталога на сохранение цены товара в Bitrix (PriceOnBeforeAdd / Update)

Почему это не классический ORM-хук

Для таблицы цен в модуле catalog многие привыкают искать знакомую пару событий ORM по классам наподобие \Bitrix\Catalog\Model\Price или напрямую по PriceTable. На практике сохранение цены проходит через внутренний слой модели каталога, и «ожидаемые» универсальные обработчики на уровень таблицы либо не вызываются в вашем сценарии, либо до них сложнее достучаться предсказуемо.

Для вмешательства в поля строки перед записью удобнее подписаться на именованные события самого каталога. Типичные точки входа перед добавлением и обновлением записи цены называются PriceOnBeforeAdd и PriceOnBeforeUpdate; есть и сопутствующие варианты на удаление и «после» операции.

Где навесить регистрацию

Разумное место — init.php проекта или автозагружаемый bootstrap модуля, где уже включаются зависимости. Модуль в первом параметре — строка catalog, вторым идёт символьное имя события, третьим — статический метод вашего класса.

use Bitrix\Main\EventManager;

$dispatcher = EventManager::getInstance();
$dispatcher->addEventHandler(
    'catalog',
    'PriceOnBeforeAdd',
    '\Acme\Catalog\RetailPriceScaler::enforceMarkupOnWrite'
);
$dispatcher->addEventHandler(
    'catalog',
    'PriceOnBeforeUpdate',
    '\Acme\Catalog\RetailPriceScaler::enforceMarkupOnWrite'
);

Разбор параметров и поправка полей

Обработчик получает объект события каталожной модели: удобно типизировать как \Bitrix\Catalog\Model\Event. Из параметров интересует массив fields с колонками, которые отправятся в базу.

Чтобы не гадать насчёт целочисленных денежных полей на разных редакциях, синхронно меняйте и сумму цены (PRICE), и её масштабированное значение (PRICE_SCALE), если последнее участвует в вашей сборке данных. Результат для ядра оформляется экземпляром \Bitrix\Catalog\Model\EventResult: метод modifyFields() перечисляет только те ключи массива строки, которые вы переопределили.

namespace Acme\Catalog;

use Bitrix\Catalog\Model\Event;
use Bitrix\Catalog\Model\EventResult;

final class RetailPriceScaler
{
    public static function enforceMarkupOnWrite(Event $pulse): EventResult
    {
        $ledger = new EventResult();
        // Пример ограничения по контексту обмена (раскомментируйте при необходимости):
        // if ($GLOBALS['APPLICATION']->GetCurPage() === '/bitrix/admin/1c_exchange.php') {
        //     return $ledger;
        // }

        $rawRow = $pulse->getParameter('fields');
        if (!is_array($rawRow)) {
            return $ledger;
        }

        $upliftFactor = 2.00; // условная наценка; вынести в настройку модуля / Option

        foreach (['PRICE', 'PRICE_SCALE'] as $columnSlug) {
            if (!array_key_exists($columnSlug, $rawRow)) {
                continue;
            }
            $rawRow[$columnSlug] = round((float) $rawRow[$columnSlug] * $upliftFactor, 4);
        }

        $pulse->setParameter('fields', $rawRow);

        $overwriteSlice = [];
        if (isset($rawRow['PRICE'])) {
            $overwriteSlice['PRICE'] = $rawRow['PRICE'];
        }
        if (isset($rawRow['PRICE_SCALE'])) {
            $overwriteSlice['PRICE_SCALE'] = $rawRow['PRICE_SCALE'];
        }

        if ($overwriteSlice !== []) {
            $ledger->modifyFields($overwriteSlice);
        }

        return $ledger;
    }
}

В реальном обмене с учётными системами коэффициент лучше брать из настроек, а не держать в коде литералом; отдельно продумайте ветку для базовых типов цен и перерасчёта зависимых строк, если включена автоматика наценок в каталоге.

См. также (оригинальные заметки)

Не хотите копаться сами?

Починю за 1-3 дня. Без предоплаты — оплата по результату.

15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии