Configuring Return Statuses in 1C-Bitrix
Return statuses in Bitrix are not simply database labels. The way they are configured determines the entire business process: what the customer sees, what actions are available to the manager at each stage, and when notifications and integrations with external systems are triggered automatically. The standard out-of-the-box set is minimal and does not cover real-world scenarios.
Status management: where it lives
Return statuses are stored in the b_sale_order_return_status table and managed through the \CSaleOrderReturnStatus class. Admin interface: Online Store → Return Statuses.
Status fields:
-
ID— string identifier (WAIT, REVIEW, APPROVED, etc.) -
NAME— display name -
DESCRIPTION— description for internal use -
SORT— display order -
COLOR— label colour in the interface (hex) -
NOTIFY— flag: whether to notify the customer when transitioning to this status -
TEMPLATE— email notification template
Creating a custom status set
// /local/install/return_statuses.php — status installation script
$statuses = [
[
'ID' => 'WAIT',
'NAME' => 'Pending Review',
'DESCRIPTION' => 'Request received, not yet processed',
'SORT' => 100,
'COLOR' => '#f0ad4e',
'NOTIFY' => 'N',
],
[
'ID' => 'REVIEW',
'NAME' => 'Under Review',
'DESCRIPTION' => 'Manager is reviewing the request',
'SORT' => 200,
'COLOR' => '#5bc0de',
'NOTIFY' => 'Y',
'TEMPLATE' => 'RETURN_STATUS_REVIEW',
],
[
'ID' => 'NEED_DOCS',
'NAME' => 'Documents Required',
'DESCRIPTION' => 'Additional documents or photos have been requested',
'SORT' => 250,
'COLOR' => '#d9534f',
'NOTIFY' => 'Y',
'TEMPLATE' => 'RETURN_STATUS_NEED_DOCS',
],
[
'ID' => 'APPROVED',
'NAME' => 'Approved',
'DESCRIPTION' => 'Return approved, awaiting item shipment',
'SORT' => 300,
'COLOR' => '#5cb85c',
'NOTIFY' => 'Y',
'TEMPLATE' => 'RETURN_STATUS_APPROVED',
],
[
'ID' => 'RECEIVED',
'NAME' => 'Item Received',
'DESCRIPTION' => 'Warehouse has accepted the returned item',
'SORT' => 400,
'COLOR' => '#337ab7',
'NOTIFY' => 'Y',
'TEMPLATE' => 'RETURN_STATUS_RECEIVED',
],
[
'ID' => 'REFUND',
'NAME' => 'Refunded',
'DESCRIPTION' => 'Payment has been processed',
'SORT' => 500,
'COLOR' => '#3c763d',
'NOTIFY' => 'Y',
'TEMPLATE' => 'RETURN_STATUS_REFUND',
],
[
'ID' => 'EXCHANGE',
'NAME' => 'Exchange',
'DESCRIPTION' => 'Item exchanged instead of a refund',
'SORT' => 450,
'COLOR' => '#8a6d3b',
'NOTIFY' => 'Y',
'TEMPLATE' => 'RETURN_STATUS_EXCHANGE',
],
[
'ID' => 'REJECTED',
'NAME' => 'Rejected',
'DESCRIPTION' => 'Return rejected',
'SORT' => 600,
'COLOR' => '#a94442',
'NOTIFY' => 'Y',
'TEMPLATE' => 'RETURN_STATUS_REJECTED',
],
];
foreach ($statuses as $statusData) {
// Check whether it already exists
$existing = \CSaleOrderReturnStatus::GetByID($statusData['ID']);
if ($existing) {
\CSaleOrderReturnStatus::Update($statusData['ID'], $statusData);
} else {
\CSaleOrderReturnStatus::Add($statusData);
}
}
Status transition matrix
Not all transitions should be permitted. For example, it should not be possible to transition from "Refunded" back to "Pending Review". Implement a matrix of allowed transitions:
namespace Local\Returns;
class StatusTransitionMatrix
{
// [current status] => [allowed next statuses]
private const ALLOWED_TRANSITIONS = [
'WAIT' => ['REVIEW', 'REJECTED'],
'REVIEW' => ['NEED_DOCS', 'APPROVED', 'REJECTED'],
'NEED_DOCS' => ['REVIEW', 'REJECTED'],
'APPROVED' => ['RECEIVED', 'EXCHANGE'],
'RECEIVED' => ['REFUND', 'EXCHANGE'],
'REFUND' => [], // terminal status
'EXCHANGE' => [], // terminal status
'REJECTED' => ['WAIT'], // decision can be reconsidered
];
// Transitions available to administrators only
private const ADMIN_ONLY = [
'REJECTED' => ['WAIT'],
];
public function canTransition(string $from, string $to, bool $isAdmin = false): bool
{
$allowed = self::ALLOWED_TRANSITIONS[$from] ?? [];
if (!in_array($to, $allowed, true)) {
return false;
}
// Check role restrictions
if (isset(self::ADMIN_ONLY[$from]) && in_array($to, self::ADMIN_ONLY[$from], true)) {
return $isAdmin;
}
return true;
}
public function getAvailableTransitions(string $from, bool $isAdmin = false): array
{
$transitions = self::ALLOWED_TRANSITIONS[$from] ?? [];
if (!$isAdmin) {
$adminOnly = self::ADMIN_ONLY[$from] ?? [];
$transitions = array_diff($transitions, $adminOnly);
}
return $transitions;
}
}
Transition validation on status change
// Intercept status change attempts
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
'sale',
'OnBeforeSaleOrderReturnStatusChange',
function (\Bitrix\Main\Event $event) {
$newStatus = $event->getParameter('STATUS_ID');
$return = $event->getParameter('ENTITY');
$oldStatus = $return->getField('STATUS_ID');
$isAdmin = \CUser::IsAdmin();
$matrix = new \Local\Returns\StatusTransitionMatrix();
if (!$matrix->canTransition($oldStatus, $newStatus, $isAdmin)) {
$result = new \Bitrix\Main\EventResult(
\Bitrix\Main\EventResult::ERROR,
"Transition from '{$oldStatus}' to '{$newStatus}' is not permitted"
);
return $result;
}
// Data validation for specific statuses
if ($newStatus === 'APPROVED') {
if (!$return->getField('REFUND_AMOUNT')) {
return new \Bitrix\Main\EventResult(
\Bitrix\Main\EventResult::ERROR,
"Please specify the refund amount before approving"
);
}
}
if ($newStatus === 'REJECTED') {
if (!$return->getField('MANAGER_COMMENT')) {
return new \Bitrix\Main\EventResult(
\Bitrix\Main\EventResult::ERROR,
"A reason must be provided when rejecting"
);
}
}
}
);
Localisation: status names in multiple languages
For multilingual sites, the customer-facing status name is taken from a language file:
// /local/lang/ru/lib/returns/status_labels.php
$MESS['RETURN_STATUS_WAIT'] = 'Ожидает рассмотрения';
$MESS['RETURN_STATUS_REVIEW'] = 'На рассмотрении';
$MESS['RETURN_STATUS_NEED_DOCS'] = 'Требуются документы';
$MESS['RETURN_STATUS_APPROVED'] = 'Одобрен';
$MESS['RETURN_STATUS_RECEIVED'] = 'Товар получен';
$MESS['RETURN_STATUS_REFUND'] = 'Деньги возвращены';
$MESS['RETURN_STATUS_EXCHANGE'] = 'Обмен';
$MESS['RETURN_STATUS_REJECTED'] = 'Отклонён';
// /local/lang/en/lib/returns/status_labels.php
$MESS['RETURN_STATUS_WAIT'] = 'Pending review';
$MESS['RETURN_STATUS_APPROVED'] = 'Approved';
// ...
In the personal account template:
$statusLabel = \Bitrix\Main\Localization\Loc::getMessage(
'RETURN_STATUS_' . $returnStatusId
) ?: $returnStatusId;
Scope of work
- Designing the status set for the specific business process
- Installation script for creating/updating statuses
- Allowed transition matrix with role-based restrictions
- Transition validator via the
OnBeforeSaleOrderReturnStatusChangeevent - Email notification templates for each "public" status
- Localisation of labels for the personal account
Timeline: status configuration and transition matrix — 3–7 business days.







