Ручная простановка статусов заказа — источник ошибок и потерянного времени. 1С-Битрикс позволяет менять статус автоматически по факту оплаты, отгрузки или обмена с 1С — через события модуля sale и обработчики в init.php. Разберём, где это настраивается и как написать корректный обработчик без рекурсии.
Где живёт автоматизация статусов
В 1С-Битрикс нет отдельного «конструктора бизнес-процессов заказа» из коробки для магазина на модуле sale — логику автосмены статусов реализуют через события ядра. Модуль sale при каждом сохранении заказа и при изменении его статуса рассылает события, на которые вы вешаете свои обработчики.
Есть три уровня, где статус может меняться автоматически:
- Обработчики событий в
/local/php_interface/init.php— самый гибкий и частый способ; - Обмен с 1С (модуль
sale+1c_catalog) — 1С возвращает статус заказа при выгрузке, и он проставляется автоматически; - Триггеры CRM — если магазин работает в связке с Битрикс24/CRM-режимом, где статусы заказа синхронизированы со стадиями сделки.
Магазин → Настройки → Статусы заказов. Встроены только N (Принят) и F (Выполнен) — остальные вы создаёте сами.Ключевые события модуля sale
Для автосмены статусов используют несколько событий. Важно понимать, какое из них когда срабатывает, иначе обработчик будет ловить лишние вызовы.
| Событие | Когда срабатывает | Что даёт |
|---|---|---|
OnSaleOrderSaved | После каждого сохранения заказа (Order::save()) | Параметры ENTITY (объект заказа) и IS_NEW |
OnSaleOrderBeforeSaved | До записи заказа в БД | Удобно менять поля без повторного save() |
OnSaleStatusOrderChange | При изменении поля STATUS_ID | Параметры ENTITY, VALUE (новый статус) |
OnSaleOrderPaid | Когда заказ помечен оплаченным | Точка для автостатуса «Оплачен» |
Обратите внимание: OnSaleStatusOrder — это устаревшее событие старого API CSaleOrder. В новых проектах на D7 используйте OnSaleStatusOrderChange, оно приходит от объекта \Bitrix\Sale\Order и не завязано на прямые запросы к таблице.
Регистрация обработчика
Обработчики регистрируют в /local/php_interface/init.php через EventManager. Разносить логику лучше в отдельный класс, а не писать её прямо в init.php.
use Bitrix\Main\EventManager;
$em = EventManager::getInstance();
$em->addEventHandler(
'sale',
'OnSaleOrderSaved',
['MyCompany\\Sale\\OrderStatus', 'onOrderSaved']
);Первый аргумент — имя модуля (sale), второй — событие, третий — callback. Класс с методом положите в /local/php_interface/lib/ и подключите автозагрузкой. Такой подход держит init.php чистым и упрощает поддержку.
/local/. Правки в /bitrix/modules/ слетят при первом же обновлении платформы.Автостатус при оплате
Типовой сценарий: как только менеджер (или платёжная система) отмечает заказ оплаченным, он должен автоматически перейти в статус «Оплачен, собирается» — например, P. Логику удобно повесить на OnSaleOrderSaved и проверять флаг оплаты у объекта заказа.
namespace MyCompany\Sale;
use Bitrix\Main\Event;
use Bitrix\Sale\Order;
class OrderStatus
{
private static $processing = false;
public static function onOrderSaved(Event $event): void
{
if (self::$processing) {
return; // защита от рекурсии
}
/** @var Order $order */
$order = $event->getParameter('ENTITY');
if ($order->isPaid() && $order->getField('STATUS_ID') === 'N') {
self::$processing = true;
$order->setField('STATUS_ID', 'P');
$order->save();
self::$processing = false;
}
}
}Здесь $order->isPaid() возвращает true, когда сумма оплат покрывает заказ. Метод setField('STATUS_ID', 'P') меняет статус, а повторный save() его фиксирует. Статический флаг $processing обязателен — без него вложенный save() снова вызовет OnSaleOrderSaved и уйдёт в бесконечную рекурсию.
Автостатус при отгрузке
Отгрузка (Shipment) в Битрикс — отдельная сущность внутри заказа, со своим признаком «отгружено» (DEDUCTED). Поэтому реакцию на отгрузку логичнее строить на факте отгрузки, а не просто на сохранении заказа.
$order->isShipped()— все отгрузки заказа собраны;- событие
OnSaleDeliveryOrder/ проверка коллекции$order->getShipmentCollection()— для частичных отгрузок; - переход заказа в финальный статус
F(Выполнен), когда заказ и оплачен, и отгружен.
Внутри того же обработчика добавьте условие: если $order->isPaid() и $order->isShipped(), ставьте STATUS_ID = 'F'. Так заказ сам «закроется», когда обе операции завершены, без ручного клика менеджера.
Триггеры и уведомления при смене статуса
Смена статуса — удобная точка, чтобы запускать сопутствующие действия. Для этого вешайте обработчик на OnSaleStatusOrderChange: событие приходит уже с новым значением статуса.
public static function onStatusChange(Event $event): void
{
$order = $event->getParameter('ENTITY');
$status = $event->getParameter('VALUE');
if ($status === 'P') {
// отправить письмо, задачу на склад, событие в CRM
\CEvent::Send('ORDER_PAID_CUSTOM', SITE_ID, [
'ORDER_ID' => $order->getId(),
]);
}
}Стандартные почтовые события заказа (SALE_STATUS_CHANGED_*) Битрикс рассылает сам при смене статуса — их шаблоны лежат в Настройки → Настройки продукта → Почтовые события. Свой обработчик нужен, когда типового письма недостаточно: уведомить склад, дёрнуть внешний API, поставить задачу. Так автосмена статусов становится ядром небольшого «пайплайна» обработки заказа.
Подводные камни
Автоматизация статусов ломается на одних и тех же местах. Держите их в голове при внедрении:
- Рекурсия. Любой
save()внутриOnSaleOrderSavedвызывает событие повторно. Спасает статический флаг или перенос логики вOnSaleOrderBeforeSaved, где менять поля можно без повторного сохранения. - Обмен с 1С. Выгрузка из 1С тоже сохраняет заказ и может перезаписать статус. Согласуйте, кто «главный» по статусу — сайт или 1С.
- Права и запреты переходов. Если между статусами настроены ограничения переходов, программная смена должна их учитывать, иначе заказ «застрянет».
- Отложенные операции. Часть логики (письма, обмен) лучше выносить в агенты или очередь, чтобы не тормозить сохранение заказа для покупателя.
OnSaleOrderSaved способна уронить оформление заказа для всех клиентов — проверяйте на стейджинге и логируйте изменения статусов.Итог
Автосмена статусов в 1С-Битрикс держится на событиях модуля sale: OnSaleOrderSaved и OnSaleOrderBeforeSaved — чтобы реагировать на оплату и отгрузку, OnSaleStatusOrderChange — чтобы запускать уведомления и триггеры. Обработчики живут в /local/php_interface/, статус меняется через setField('STATUS_ID', ...), а рекурсию гасит статический флаг. Правильно выстроенная цепочка снимает с менеджеров рутину и убирает человеческий фактор из воронки заказа.
Если нужно связать автосмену статусов с оплатой, отгрузкой, обменом с 1С и внешними системами без риска уронить оформление заказа — мы проектируем и сопровождаем такую логику под ваш процесс. Поможем спроектировать статусную модель, написать безопасные обработчики и покрыть их тестами.
Частые вопросы
Чем OnSaleStatusOrderChange отличается от OnSaleStatusOrder?
OnSaleStatusOrder — устаревшее событие старого API CSaleOrder. В проектах на D7 используйте OnSaleStatusOrderChange, которое приходит от объекта \Bitrix\Sale\Order и передаёт новое значение статуса.
Как избежать бесконечной рекурсии в обработчике?
Не вызывайте save() внутри OnSaleOrderSaved без защиты. Используйте статический флаг-переключатель или меняйте поля в OnSaleOrderBeforeSaved, где повторное сохранение не требуется.
Где регистрировать обработчики событий?
В файле /local/php_interface/init.php через EventManager::getInstance()->addEventHandler('sale', ...). Само тело логики выносите в отдельный класс в /local/php_interface/lib/.
Как узнать, что заказ оплачен, в коде обработчика?
У объекта заказа есть метод $order->isPaid(), возвращающий true, когда сумма оплат покрывает заказ. Для отгрузки аналогично работает $order->isShipped().
Можно ли автоматически ставить статус при обмене с 1С?
Да, 1С возвращает статус заказа при обмене через модуль sale и 1c_catalog. Важно заранее договориться, кто главный по статусу — сайт или 1С, чтобы значения не перезаписывали друг друга.
Нужно ли писать код для писем при смене статуса?
Не всегда. Битрикс сам рассылает почтовые события SALE_STATUS_CHANGED_* по шаблонам из настроек. Свой обработчик нужен для нетиповых действий: уведомить склад, вызвать внешний API, поставить задачу.
Как ставить финальный статус при частичной отгрузке?
Не переводите заказ в статус «Выполнен» по первой отгрузке. Проверяйте всю коллекцию отгрузок через getShipmentCollection() и закрывайте заказ, только когда отгружен весь состав.
Опасно ли ошибиться в обработчике OnSaleOrderSaved?
Да, фатальная ошибка в нём может сломать оформление заказа для всех покупателей. Тестируйте изменения на стейджинге, добавляйте try-catch и логируйте смену статусов.
Поможем с настройкой и поддержкой 1С-Битрикс: Управление сайтом
Поможем с настройкой, доработкой и поддержкой 1С-Битрикс: Управление сайтом.