Перейти к содержимому

Статусы заказов - смена из кода, события и сопоставление с 1С

Меняем статус заказа из кода, ловим смену обработчиком и разбираемся, почему статус не приезжает из 1С.

Решение

Смотрим список статусов и их внешние коды:

$statuses = CSaleStatus::GetList(['SORT' => 'ASC'], [], false, false,
['ID', 'NAME', 'XML_ID', 'SORT']);
while ($status = $statuses->Fetch()) {
printf("%-4s %-24s xml_id=%s\n", $status['ID'], $status['NAME'],
$status['XML_ID'] ?: 'ПУСТО');
}

Идентификатор статуса - строка, а не число: N для нового, F для выполненного и свои коды для остальных. Внешний код нужен только обмену, витрине он не виден, а сортировка задаёт порядок в выпадающем списке менеджера.

Меняем статус объектом заказа:

$order = Bitrix\Sale\Order::load($orderId);
$result = $order->setField('STATUS_ID', 'P'); // запускает события и пересчёты
if (!$result->isSuccess()) {
throw new RuntimeException(implode('; ', $result->getErrorMessages()));
}
$order->save();

Прямая запись поля в базу статус тоже поменяет, но не запустит ни почтовых событий, ни обработчиков. Письмо покупателю не уйдёт, и учётная система о смене не узнает.

Ловим смену статуса обработчиком:

/local/php_interface/init.php
Bitrix\Main\EventManager::getInstance()->addEventHandler(
'sale', 'OnSaleStatusOrderChange',
static function (Bitrix\Main\Event $event) {
$order = $event->getParameter('ENTITY');
$value = $event->getParameter('VALUE'); // новый статус
// здесь свои действия: уведомление, запись в журнал, вызов внешнего API
}
);

Событие приходит уже после проверки прав и валидации, но до сохранения. Долгие операции в нём держать нельзя: они удлиняют сохранение заказа для покупателя.

Проверяем, что вернулось из обмена:

$order = Bitrix\Sale\Order::load($orderId);
printf("статус %s, версия 1С %s, побывал в обмене: %s\n",
$order->getField('STATUS_ID'),
$order->getField('VERSION_1C') ?: '-',
$order->getField('UPDATED_1C'));

Заполненная версия при неизменившемся статусе означает, что обмен заказ видел, а статус не применил: сопоставление по внешнему коду не сошлось.

Сопоставление стоит проверять до запуска обмена в продакшене, а не после. Статусы заводят один раз, внешние коды им проставляют сразу, и дальше справочник не трогают. Переименование статуса безопасно, смена идентификатора - нет: на него ссылается и код сайта, и узел обмена.

Считаем заказы по каждому статусу:

$rows = Bitrix\Sale\Internals\OrderTable::getList([
'select' => ['STATUS_ID', new Bitrix\Main\Entity\ExpressionField('CNT', 'COUNT(1)')],
'group' => ['STATUS_ID'],
]);
foreach ($rows as $row) {
printf("%-4s %d\n", $row['STATUS_ID'], $row['CNT']);
}

Такая сводка сразу показывает застрявшие статусы: сотни заказов в промежуточном состоянии означают, что переход дальше никто не делает или он сломан. Смотреть её стоит регулярно, а не только при разборе жалобы.

Отдельная ловушка - права групп на статус. Статус, недоступный группе менеджера, в его списке не появится, и заказ будет выглядеть застрявшим. Проверять это стоит не под администратором, а под учётной записью того, кто работает с заказами.

Типичные проблемы

Статус сменился, а письмо покупателю не ушло.

Статус записан прямым запросом к базе. Почтовое событие отправляет штатный обработчик смены статуса, и мимо API заказа он не вызывается.

Статус из 1С не применяется.

У статуса на сайте пустой внешний код. Обмен сопоставляет статусы по XML_ID, а названия не использует вовсе.

Свой статус пропал после обновления платформы.

Статус заводился правкой таблицы, а не через интерфейс. Обновление приводит справочник статусов к своему виду и чужие строки не сохраняет.

Заказ выполнен, а отгрузка числится несобранной.

Статус заказа и состояние отгрузки - разные поля. Отгрузка меняется своими методами коллекции, и статус заказа её не двигает.

Частые вопросы

Чем статус заказа отличается от статуса отгрузки?

Статус заказа описывает сделку целиком, статус отгрузки - конкретную посылку. У заказа с двумя отгрузками свой статус один, а состояний отгрузки два, и меняются они независимо.

Как добавить свой статус?

Через интерфейс в разделе статусов заказа: там задаётся идентификатор, название, сортировка и права групп. Идентификатор выбирают латиницей и больше не меняют - на него ссылаются код и обмен.

Можно ли запретить переход между некоторыми статусами?

Штатно нет, порядок статусов не задаёт маршрут. Ограничение делают обработчиком на смене статуса: он сравнивает прежнее значение с новым и возвращает ошибку.

Почему статус меняется, а событие не приходит?

Событие OnSaleStatusOrderChange срабатывает только при смене через объект заказа. Массовые операции, написанные запросами к базе, его не вызывают.

Смежное

Первоисточник