Разработка кастомной формы возврата товара в 1С-Битрикс

Мы часто встречаем проекты, где стандартный компонент `bitrix:sale.order.return.edit` не справляется с задачами: в нём нет загрузки фотографий дефекта, пошагового интерфейса и возможности указать разные причины для каждой позиции. При 50–100 обращениях по возвратам в день неудобная форма — это прямы
Услуги, которые мы предлагаем
Показано 1 из 1Все 1626 услуг
Разработка кастомной формы возврата товара в 1С-Битрикс
Средний
~1-2 недели

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

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1415
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    995
  • 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 Appointment Booking Widget for a Medical Center
    733
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Разработка на базе 1С Предприятие для компании МИРСАНБЕЛ
    863
  • image_crm_dolbimby_434_0.webp
    Разработка сайта на CRM Битрикс24 для компании DOLBIMBY
    772
  • image_crm_technotorgcomplex_453_0.webp
    Разработка на базе Битрикс24 для компании ТЕХНОТОРГКОМПЛЕКС
    1134

Мы часто встречаем проекты, где стандартный компонент bitrix:sale.order.return.edit не справляется с задачами: в нём нет загрузки фотографий дефекта, пошагового интерфейса и возможности указать разные причины для каждой позиции. При 50–100 обращениях по возвратам в день неудобная форма — это прямые потери времени менеджеров на уточнения по телефону.

Представьте: покупатель получил бракованный товар. Он заходит в личный кабинет, находит заказ, но стандартная форма возврата не позволяет приложить фото дефекта. Приходится звонить менеджеру, уточнять причину, отправлять фото в ответном письме. Это увеличивает время обработки в среднем на 15 минут. При 100 возвратах в день — потеря 25 человеко-часов. Два менеджера тратят полдня на переписку и звонки вместо того, чтобы обрабатывать другие заявки.

Мы разрабатываем кастомные формы возврата под ключ, сокращая время обработки на 30%. Наш опыт с Битрикс — 10+ лет, более 50 проектов по возвратам. Гарантируем отказоустойчивость и соответствие 54-ФЗ. Внедрение такой формы окупается в течение 3–6 месяцев, а при 100 возвратах в день экономия времени менеджеров приносит существенную экономию бюджета.

Почему wizard быстрее стандартной формы?

Кастомный wizard обрабатывает возврат в 3 раза быстрее штатной формы — 4 минуты вместо 15. Покупатель проходит 4 шага, менеджер получает полные данные без необходимости перезванивать.

Характеристика Стандартная форма Кастомная форма
Загрузка фото нет есть (до 5 МБ)
Выбор причины по позиции нет есть
Количество шагов 1 4 (wizard)
Время заполнения ~15 мин ~4 мин
Интеграция с 1С через обмен прямая через REST

Структура wizard-формы

Оптимальный UX для формы возврата — 3–4 шага:

  1. Выбор заказа — покупатель выбирает из своей истории заказов, доступных для возврата.
  2. Выбор товаров и причин — галочками выбирает позиции, для каждой указывает причину и количество.
  3. Дополнительная информация — комментарий, загрузка фото/документов.
  4. Подтверждение — итоговый экран с данными заявки и инструкциями.

Шаг 1: доступные для возврата заказы

Возврат возможен только по оплаченным заказам в определённый период (обычно 14 дней по закону). Загружаем список:

<?php namespace Local\Returns; class ReturnableOrdersProvider { private int $userId; private int $returnWindowDays; public function __construct(int $userId, int $returnWindowDays = 14) { $this->userId = $userId; $this->returnWindowDays = $returnWindowDays; } public function getReturnableOrders(): array { \Bitrix\Main\Loader::includeModule('sale'); $dateFrom = new \Bitrix\Main\Type\Date(); $dateFrom->add('-' . $this->returnWindowDays . ' days'); $result = \Bitrix\Sale\OrderTable::getList([ 'filter' => [ 'USER_ID' => $this->userId, 'PAYED' => 'Y', '>=DATE_PAY' => $dateFrom, '!STATUS_ID' => ['CANCELED', 'RETURNED'], ], 'select' => ['ID', 'ACCOUNT_NUMBER', 'DATE_INSERT', 'PRICE', 'CURRENCY', 'STATUS_ID'], 'order' => ['DATE_INSERT' => 'DESC'], ]); $orders = []; while ($row = $result->fetch()) { // Проверяем: нет ли уже полного возврата по этому заказу if (!$this->hasFullReturn($row['ID'])) { $orders[] = $row; } } return $orders; } private function hasFullReturn(int $orderId): bool { $existing = \Bitrix\Sale\OrderReturnTable::getList([ 'filter' => ['ORDER_ID' => $orderId, 'STATUS_ID' => ['APPROVED', 'RECEIVED', 'REFUND']], 'select' => ['ID'], 'limit' => 1, ])->fetch(); return (bool)$existing; } } 

Шаг 2: позиции заказа с выбором причины

<?php class OrderItemsProvider { public function getReturnableItems(int $orderId, int $userId): array { $order = \Bitrix\Sale\Order::load($orderId); if (!$order || $order->getUserId() !== $userId) { throw new \RuntimeException('Order not found or access denied'); } $items = []; foreach ($order->getBasket() as $item) { // Считаем уже возвращённое количество $returnedQty = $this->getReturnedQuantity($orderId, $item->getId()); $availableQty = $item->getQuantity() - $returnedQty; if ($availableQty <= 0) continue; $items[] = [ 'basket_id' => $item->getId(), 'product_id' => $item->getProductId(), 'name' => $item->getField('NAME'), 'quantity' => $item->getQuantity(), 'available_qty' => $availableQty, 'price' => $item->getFinalPrice(), 'image' => $this->getProductImage($item->getProductId()), 'article' => $item->getField('ARTICLE'), ]; } return $items; } private function getReturnedQuantity(int $orderId, int $basketItemId): float { $result = \Bitrix\Sale\OrderReturnBasketTable::getList([ 'filter' => [ 'ORDER_RETURN.ORDER_ID' => $orderId, 'BASKET_ID' => $basketItemId, 'ORDER_RETURN.STATUS_ID' => ['WAIT', 'REVIEW', 'APPROVED', 'RECEIVED', 'REFUND'], ], 'runtime' => [ new \Bitrix\Main\ORM\Fields\ExpressionField('TOTAL_QTY', 'SUM(%s)', 'QUANTITY'), ], 'select' => ['TOTAL_QTY'], ])->fetch(); return (float)($result['TOTAL_QTY'] ?? 0); } } 

Клиентская часть: step-by-step форма

React-компонент для пошаговой формы (или Vue — по выбору):

import React, { useState } from 'react'; function ReturnWizard({ orderId }) { const [step, setStep] = useState(1); const [selectedItems, setSelectedItems] = useState([]); const [files, setFiles] = useState([]); const returnReasons = [ { id: 'defect', label: 'Производственный брак' }, { id: 'wrong_item', label: 'Прислали не тот товар' }, { id: 'damaged', label: 'Повреждён при доставке' }, { id: 'not_fit', label: 'Не подошёл' }, { id: 'other', label: 'Другая причина' }, ]; const canProceed = selectedItems.some(item => item.selected && item.reason); async function submitReturn() { const formData = new FormData(); formData.append('order_id', orderId); formData.append('sessid', BX.bitrix_sessid()); formData.append('items', JSON.stringify(selectedItems.filter(i => i.selected))); files.forEach((file, i) => formData.append(`files[${i}]`, file)); const res = await fetch('/local/api/return-submit.php', { method: 'POST', body: formData, }); const data = await res.json(); if (data.success) { setStep(4); // Success screen } } // ... рендер шагов } 

Серверный обработчик финальной отправки

<?php // /local/api/return-submit.php require_once($_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php'); header('Content-Type: application/json'); if (!\CUser::IsAuthorized()) { http_response_code(401); exit(json_encode(['error' => 'Unauthorized'])); } if (!\bitrix_sessid_check($_POST['sessid'] ?? '')) { http_response_code(403); exit(json_encode(['error' => 'Invalid session'])); } $orderId = (int)($_POST['order_id'] ?? 0); $items = json_decode($_POST['items'] ?? '[]', true); $userId = (int)\CUser::GetID(); // Валидируем, что заказ принадлежит пользователю $validator = new \Local\Returns\ReturnValidator($userId); if (!$validator->canReturnOrder($orderId)) { exit(json_encode(['success' => false, 'error' => 'Заказ недоступен для возврата'])); } // Загружаем прикреплённые файлы $fileIds = []; $uploader = new \Local\Upload\FileUploader(); foreach ($_FILES as $key => $file) { if (strpos($key, 'files') === 0 && $file['error'] === UPLOAD_ERR_OK) { try { $result = $uploader->handle($file); $fileIds[] = $result['id']; } catch (\Exception $e) { // Логируем, но не прерываем } } } // Создаём заявку на возврат $manager = new \Local\Returns\ReturnManager(); $returnId = $manager->createReturn($orderId, $items, 'MONEY'); // Прикрепляем файлы к заявке if ($fileIds) { \Local\Returns\ReturnAttachments::attach($returnId, $fileIds); } // Отправляем уведомления \Local\Returns\Notifications::sendToCustomer($returnId); \Local\Returns\Notifications::sendToManager($returnId); exit(json_encode([ 'success' => true, 'return_id' => $returnId, 'message' => 'Заявка #' . $returnId . ' создана. Рассмотрим в течение 2 рабочих дней.', ])); 

Вложения к заявке: расширение таблицы

Стандартная система возвратов Битрикс не хранит прикреплённые файлы. Расширяем через Highload-блок:

<?php class ReturnAttachmentTable extends \Bitrix\Main\ORM\Data\DataManager { public static function getTableName(): string { return 'local_return_attachments'; } public static function getMap(): array { return [ new \Bitrix\Main\ORM\Fields\IntegerField('ID', ['primary' => true, 'autocomplete' => true]), new \Bitrix\Main\ORM\Fields\IntegerField('RETURN_ID'), new \Bitrix\Main\ORM\Fields\IntegerField('FILE_ID'), // b_file.ID new \Bitrix\Main\ORM\Fields\DatetimeField('CREATED_AT'), ]; } } 

Как обеспечить безопасность обработки заявок?

Критично защитить AJAX-обработчик от XSS и CSRF-атак. Во-первых, проверяем сессию через bitrix_sessid_check. Во-вторых, валидируем, что заказ принадлежит текущему пользователю. В-третьих, фильтруем загружаемые файлы по типу и размеру — только изображения до 5 МБ, остальные отклоняем.

Как интегрировать форму возврата с 1С?

Интеграция с 1С осуществляется через CommerceML или REST API. Мы настраиваем автоматическое создание документов возврата в 1С при одобрении заявки. Это исключает двойной ввод данных и ускоряет процесс возврата.

Этап Действие Ответственный Срок
Анализ Аудит текущих бизнес-процессов возвратов Аналитик 1-3 дня
Проектирование Согласование логики wizard и экранов Аналитик + клиент 2-5 дней
Разработка Backend API, wizard на React/Vue, интеграции Разработчик 1-3 недели
Тестирование Юнит-тесты, нагрузка, UAT Тестировщик 3-5 дней
Деплой Развёртывание на боевой сервер DevOps 1 день
Обучение Документация, обучение менеджеров Аналитик до 2 часов

Что входит в работу

  • Аудит текущего процесса возвратов и согласование логики
  • Разработка wizard-формы с 4 шагами (React/Vue)
  • Серверная часть: API для создания и статусов возвратов
  • Интеграция с почтовыми уведомлениями (покупатель + менеджер)
  • Страница "Мои возвраты" в личном кабинете
  • Документация по каждому компоненту
  • Обучение сотрудников (до 2 часов)
  • Техническая поддержка 1 месяц после запуска
Типичные ошибки при интеграции - Забывают проверять сессию в AJAX-обработчике — ведёт к XSS. - Не учитывают частичные возвраты: нужно считать уже возвращённое количество. - При загрузке фото не проверяют размер — файлы могут быть больше 5 МБ.

Сроки ориентировочно

Полная форма с wizard и загрузкой файлов — от 2 до 4 недель. Более сложные интеграции (1С, кастомные бизнес-процессы) — до 6 недель. Стоимость рассчитывается индивидуально. Оценим ваш проект — напишите.

Закажите разработку формы возврата уже сегодня — получите бесплатный аудит текущих процессов. Мы гарантируем корректную работу на высоких нагрузках (1000+ возвратов в день) и соответствие требованиям 54-ФЗ для фискализации. Свяжитесь с нами для консультации — покажем демо-форму.

Официальная документация: REST API Битрикс для интеграций.