Розробка кастомної логіки зміни статусів замовлення 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 робочих днів.







