Разработка кастомной логики смены статусов заказа 1С-Битрикс
Стандартный механизм статусов Битрикс позволяет менеджеру вручную переводить заказ в любой статус с нужными правами доступа. Но реальный процесс обработки заказа сложнее: перевод в «доставляется» должен быть возможен только из «собирается», переход в «отменён» — только если заказ не оплачен, возврат из «завершён» — только для администратора. Всё это — кастомная логика переходов, которая не реализуется через стандартные настройки.
Валидация переходов: событийная модель
Битрикс предоставляет событие OnSaleOrderBeforeStatusChange — обработчик может заблокировать переход и вернуть ошибку:
// /local/php_interface/init.php
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
'sale',
'OnSaleOrderBeforeStatusChange',
['\App\Order\StatusValidator', 'validate']
);
// /local/lib/Order/StatusValidator.php
namespace App\Order;
use Bitrix\Main\Event;
use Bitrix\Main\EventResult;
use Bitrix\Sale\Order;
class StatusValidator
{
// Матрица допустимых переходов
private static array $allowedTransitions = [
'N' => ['P', 'A'], // Новый → Принят или Отменён
'P' => ['W', 'ASSEMBLY', 'A'], // Принят → Ожидает оплаты, Сборка, Отменён
'W' => ['P', 'ASSEMBLY', 'A'], // Ожидает оплаты → Принят, Сборка, Отменён
'ASSEMBLY' => ['D', 'A'], // Сборка → Доставляется, Отменён
'D' => ['F'], // Доставляется → Завершён
'F' => [], // Завершён — финальный
'A' => [], // Отменён — финальный
];
public static function validate(Event $event): EventResult
{
/** @var Order $order */
$order = $event->getParameter('ENTITY');
$newStatus = $event->getParameter('VALUE');
$currentStatus = $order->getField('STATUS_ID');
$allowed = self::$allowedTransitions[$currentStatus] ?? [];
if (!in_array($newStatus, $allowed, true)) {
return new EventResult(
EventResult::ERROR,
[
'message' => sprintf(
'Переход из статуса "%s" в "%s" запрещён',
$currentStatus,
$newStatus
),
],
'sale'
);
}
// Дополнительная бизнес-проверка: нельзя отменить оплаченный заказ
if ($newStatus === 'A' && $order->isPaid()) {
return new EventResult(
EventResult::ERROR,
['message' => 'Нельзя отменить оплаченный заказ. Оформите возврат.'],
'sale'
);
}
return new EventResult(EventResult::SUCCESS);
}
}
Дополнительная валидация по ролям
Администратор может делать то, что недоступно менеджеру:
public static function validate(Event $event): EventResult
{
global $USER;
$order = $event->getParameter('ENTITY');
$newStatus = $event->getParameter('VALUE');
$currentStatus = $order->getField('STATUS_ID');
// Возврат из финального статуса — только администратор
if ($currentStatus === 'F' && !$USER->IsAdmin()) {
return new EventResult(
EventResult::ERROR,
['message' => 'Изменение завершённого заказа доступно только администратору'],
'sale'
);
}
// ... остальные проверки
}
Реакция на переход: постобработка
После успешного перехода — событие OnSaleOrderStatusChange:
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
'sale',
'OnSaleOrderStatusChange',
function(Event $event) {
$order = $event->getParameter('ENTITY');
$newStatus = $event->getParameter('VALUE');
$oldStatus = $event->getParameter('OLD_VALUE');
switch ($newStatus) {
case 'ASSEMBLY':
// Создать задачу на складе
WarehouseIntegration::createPickingTask($order);
break;
case 'D':
// Передать в службу доставки
DeliveryService::registerShipment($order);
// Отправить покупателю трек-номер
Notifications::sendTrackingNumber($order);
break;
case 'F':
// Начислить бонусные баллы
LoyaltyProgram::creditPoints($order);
// Запросить отзыв через 3 дня
ReviewRequest::schedule($order->getUserId(), 3);
break;
case 'A':
// Вернуть резерв на складе
if ($oldStatus !== 'N') {
StockManager::releaseReservation($order);
}
// Инициировать возврат оплаты если нужно
if ($order->isPaid()) {
RefundManager::initiate($order);
}
break;
}
}
);
Программная смена статуса
При изменении статуса из кода — не через setField напрямую, а через корректный метод:
$order = \Bitrix\Sale\Order::load($orderId);
// Правильный способ — с триггером всех событий
$result = $order->setField('STATUS_ID', 'D');
if ($result->isSuccess()) {
$saveResult = $order->save();
if (!$saveResult->isSuccess()) {
// Обработка ошибок сохранения
$errors = $saveResult->getErrorMessages();
}
} else {
// Событие OnSaleOrderBeforeStatusChange вернуло ошибку
$errors = $result->getErrorMessages();
}
Журнал переходов
Все смены статусов логируются в b_sale_order_change автоматически. Для кастомного аудита — собственная таблица с историей переходов и причинами:
// При каждой смене статуса
\App\Order\StatusLog::record([
'order_id' => $order->getId(),
'from_status' => $oldStatus,
'to_status' => $newStatus,
'user_id' => $GLOBALS['USER']->GetID(),
'comment' => $comment,
'timestamp' => new \Bitrix\Main\Type\DateTime(),
]);
Интерфейс смены статуса с комментарием
Стандартный интерфейс не предоставляет поле комментария при смене статуса. Это решается кастомным AJAX-обработчиком на странице детали заказа в админке:
// /local/ajax/change_order_status.php
$orderId = (int)$_POST['order_id'];
$newStatus = htmlspecialchars($_POST['status']);
$comment = htmlspecialchars($_POST['comment']);
$order = \Bitrix\Sale\Order::load($orderId);
// Сохраняем комментарий в сессии/глобале для обработчика события
$_SESSION['order_status_comment'][$orderId] = $comment;
$order->setField('STATUS_ID', $newStatus);
$result = $order->save();
Сроки выполнения
Матрица переходов с валидацией + обработчики реакций на 3–5 статусов — 1–2 рабочих дня. Полноценная система с журналом, интерфейсом комментариев, интеграцией со складом и службой доставки — 3–5 рабочих дней.







