Розробка системи RMA (Return Merchandise Authorization) на 1С-Бітрікс

Наша компанія займається розробкою, підтримкою та обслуговуванням рішень на Бітрікс та Бітрікс24 будь-якої складності. Від простих односторінкових сайтів до складних інтернет-магазинів, CRM систем з інтеграцією 1С та телефонії. Досвід розробників підтверджено сертифікатами від вендора.
Пропоновані послуги
Показано 1 з 1 послугУсі 1626 послуг
Розробка системи RMA (Return Merchandise Authorization) на 1С-Бітрікс
Середня
~1-2 тижні
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Розробка на базі Бітрікс, Бітрікс24, 1С для компанії Development of an Online
    585
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Розробка на базі 1С Підприємство для компанії МИРСАНБЕЛ
    751
  • image_crm_dolbimby_434_0.webp
    Розробка сайту на CRM Бітрікс24 для компанії DOLBIMBY
    657
  • image_crm_technotorgcomplex_453_0.webp
    Розробка на базі Бітрікс24 для компанії ТЕХНОТОРГКОМПЛЕКС
    989

Розробка системи RMA (Return Merchandise Authorization) на 1С-Бітрікс

Повернення без системи — хаос. Клієнт телефонує або пише на загальну пошту, менеджер вручну шукає замовлення, домовляється про повернення, надсилає інструкції. Відстежити статус повернення неможливо, аналітики немає. RMA-система автоматизує цей процес: клієнт створює заявку на повернення через особистий кабінет, менеджер обробляє її в адміністративній частині, система відстежує статуси. Бітрікс не має вбудованого RMA — його будують поверх модуля sale.

Юридичний контекст

Повернення товару регулюється відповідним законодавством про захист прав споживачів. Два основних підстави:

  • Неналежна якість — дефект, брак. Строк пред'явлення — у межах гарантійного терміну.
  • Належна якість — «передумав». Лише для непродовольчих товарів, 14 днів, товар не повинен бути у вжитку.

У системі RMA обидві підстави мають бути представлені окремими типами заявок з різними полями та процесами.

Модель даних

Таблиця RMA-заявок:

class RmaRequestTable extends \Bitrix\Main\ORM\Data\DataManager
{
    public static function getTableName(): string { return 'b_local_rma_request'; }

    public static function getMap(): array
    {
        return [
            new IntegerField('ID', ['primary' => true, 'autocomplete' => true]),
            new StringField('NUMBER'),              // RMA-YYYYMMDD-XXXX
            new IntegerField('ORDER_ID'),           // b_sale_order
            new IntegerField('USER_ID'),
            new EnumField('TYPE', ['values' => ['DEFECT', 'EXCHANGE', 'REFUND']]),
            new EnumField('STATUS', ['values' => ['NEW', 'UNDER_REVIEW', 'APPROVED', 'REJECTED', 'RETURNED', 'REFUNDED', 'CLOSED']]),
            new TextField('DESCRIPTION'),           // Опис причини від клієнта
            new StringField('DEFECT_PHOTO_IDS'),    // JSON-масив b_file ID
            new StringField('REJECT_REASON'),       // Причина відмови (якщо REJECTED)
            new FloatField('REFUND_AMOUNT'),        // Сума до повернення
            new IntegerField('RESPONSIBLE_ID'),     // Відповідальний менеджер
            new DatetimeField('CREATED_AT'),
            new DatetimeField('UPDATED_AT'),
            new DatetimeField('DEADLINE_AT'),       // Дедлайн обробки (14 днів)
        ];
    }
}

Позиції повернення:

class RmaItemTable extends \Bitrix\Main\ORM\Data\DataManager
{
    public static function getTableName(): string { return 'b_local_rma_item'; }

    public static function getMap(): array
    {
        return [
            new IntegerField('ID', ['primary' => true, 'autocomplete' => true]),
            new IntegerField('RMA_ID'),
            new IntegerField('ORDER_BASKET_ID'),    // b_sale_basket
            new IntegerField('PRODUCT_ID'),
            new StringField('PRODUCT_NAME'),
            new FloatField('QUANTITY'),
            new FloatField('PRICE'),
            new StringField('REASON_CODE'),         // DEFECTIVE, NOT_AS_DESCRIBED, CHANGED_MIND, WRONG_ITEM
            new StringField('CONDITION'),           // NEW, USED, DAMAGED
        ];
    }
}

Історія статусів:

class RmaHistoryTable extends \Bitrix\Main\ORM\Data\DataManager
{
    public static function getTableName(): string { return 'b_local_rma_history'; }
    // RMA_ID, OLD_STATUS, NEW_STATUS, USER_ID, COMMENT, CREATED_AT
}

Генерація номера RMA

class RmaNumberGenerator
{
    public static function generate(): string
    {
        $date   = date('Ymd');
        $lastId = RmaRequestTable::getList([
            'order'  => ['ID' => 'DESC'],
            'limit'  => 1,
            'select' => ['ID'],
        ])->fetch()['ID'] ?? 0;

        return 'RMA-' . $date . '-' . str_pad($lastId + 1, 4, '0', STR_PAD_LEFT);
    }
}

Створення заявки з особистого кабінету

Сторінка /personal/rma/create/?order_id=12345:

class RmaCreateComponent extends \CBitrixComponent
{
    public function executeComponent(): void
    {
        $orderId = (int)$this->arParams['ORDER_ID'];

        if (!$orderId || !$this->isOrderOwner($orderId)) {
            LocalRedirect('/personal/orders/');
            return;
        }

        // Завантажити товари замовлення
        $order  = \Bitrix\Sale\Order::load($orderId);
        $basket = $order->getBasket();

        $items = [];
        foreach ($basket as $item) {
            // Перевірити: чи не минув строк для повернення
            $daysSinceOrder = (new \DateTime())->diff(new \DateTime($order->getField('DATE_INSERT')))->days;
            if ($daysSinceOrder > 365) continue; // Пропустити товари старші року

            $items[] = [
                'BASKET_ID'    => $item->getId(),
                'PRODUCT_ID'   => $item->getProductId(),
                'NAME'         => $item->getField('NAME'),
                'QUANTITY'     => $item->getQuantity(),
                'PRICE'        => $item->getPrice(),
                'CAN_REFUND'   => $daysSinceOrder <= 14, // 14 днів для повернення без дефекту
            ];
        }

        if ($this->request->isPost() && check_bitrix_sessid()) {
            $this->createRmaRequest($orderId, $items);
        }

        $this->arResult['ORDER'] = $order;
        $this->arResult['ITEMS'] = $items;
        $this->includeComponentTemplate();
    }

    private function createRmaRequest(int $orderId, array $items): void
    {
        $selectedItems = [];
        foreach ($this->request->getPost('items') as $basketId => $itemData) {
            if (empty($itemData['selected'])) continue;
            $selectedItems[] = [
                'ORDER_BASKET_ID' => $basketId,
                'QUANTITY'        => (float)$itemData['quantity'],
                'REASON_CODE'     => $itemData['reason'],
                'CONDITION'       => $itemData['condition'],
            ];
        }

        if (empty($selectedItems)) {
            $this->arResult['ERROR'] = 'Оберіть хоча б один товар для повернення';
            return;
        }

        // Завантажити фотографії дефектів
        $photoIds = [];
        foreach ($_FILES['defect_photos']['tmp_name'] as $i => $tmpName) {
            if (is_uploaded_file($tmpName)) {
                $fileId = \CFile::SaveFile([
                    'name'     => $_FILES['defect_photos']['name'][$i],
                    'size'     => $_FILES['defect_photos']['size'][$i],
                    'tmp_name' => $tmpName,
                    'type'     => $_FILES['defect_photos']['type'][$i],
                ], 'rma');
                if ($fileId) $photoIds[] = $fileId;
            }
        }

        $addResult = RmaRequestTable::add([
            'NUMBER'         => RmaNumberGenerator::generate(),
            'ORDER_ID'       => $orderId,
            'USER_ID'        => $GLOBALS['USER']->GetID(),
            'TYPE'           => $this->request->getPost('type'),
            'STATUS'         => 'NEW',
            'DESCRIPTION'    => htmlspecialchars($this->request->getPost('description')),
            'DEFECT_PHOTO_IDS' => json_encode($photoIds),
            'CREATED_AT'     => new \Bitrix\Main\Type\DateTime(),
            'DEADLINE_AT'    => \Bitrix\Main\Type\DateTime::createFromPhp(new \DateTime('+14 days')),
        ]);

        $rmaId = $addResult->getId();
        foreach ($selectedItems as $item) {
            RmaItemTable::add(array_merge($item, ['RMA_ID' => $rmaId]));
        }

        // Сповістити менеджера
        $this->notifyManager($rmaId);

        $this->arResult['SUCCESS'] = true;
        $this->arResult['RMA_NUMBER'] = 'RMA-...' ;
    }
}

Адміністративна обробка

Сторінка /local/admin/rma_list.php — список усіх RMA з фільтрами за статусом, дедлайном, відповідальним. Дії менеджера:

// Схвалити повернення
public function approve(int $rmaId, float $refundAmount, int $managerId): void
{
    RmaRequestTable::update($rmaId, [
        'STATUS'         => 'APPROVED',
        'REFUND_AMOUNT'  => $refundAmount,
        'RESPONSIBLE_ID' => $managerId,
        'UPDATED_AT'     => new \Bitrix\Main\Type\DateTime(),
    ]);

    RmaHistoryTable::add([
        'RMA_ID'     => $rmaId,
        'OLD_STATUS' => 'UNDER_REVIEW',
        'NEW_STATUS' => 'APPROVED',
        'USER_ID'    => $managerId,
        'COMMENT'    => 'Заявку схвалено',
    ]);

    // Ініціювати повернення коштів через платіжну систему
    $this->initiateRefund($rmaId, $refundAmount);

    // Сповістити клієнта
    $this->notifyClient($rmaId, 'APPROVED');
}

Інтеграція з поверненням платежу

Для повернення грошей через платіжну систему — використовуємо API того самого шлюзу, через який прийнято оплату. Більшість шлюзів мають метод рефанду:

// Приклад для ЮKassa
$client = new \YooKassa\Client();
$client->setAuth($shopId, $secretKey);

$originalPaymentId = $this->getOriginalPaymentId($orderId); // із b_sale_pay_system_action
$refund = $client->createRefund([
    'payment_id' => $originalPaymentId,
    'amount'     => ['value' => $refundAmount, 'currency' => 'RUB'],
    'description' => 'Повернення за RMA #' . $rmaNumber,
]);

Після успішного рефанду — статус RMA → REFUNDED, поповнити залишок складу (якщо товар повернуто на склад).

Терміни розробки

Варіант Склад Термін
Базова RMA Форма заявки, список у ОК, статуси 8–12 днів
З обробкою в адмінці Інтерфейс менеджера, історія, сповіщення 12–18 днів
Повна система + Повернення платежів, інтеграція зі складом 18–28 днів