Як працює часткове повернення в 1С-Бітрікс
Клієнт купив холодильник за 35 000 грн і пральну машину за 25 000 грн. Холодильник виявився з дефектом — треба повернути тільки його. В інтернет-магазині побутової техніки з 200 замовленнями на день 15% вимагають часткового повернення. Ручна обробка займає 20 хвилин на кожне, а помилки в розрахунках ведуть до штрафів до 50 000 грн за 54-ФЗ. У Бітріксі часткове повернення без налаштування працює криво: знижки на замовлення, купони, різні ставки ПДВ, доставка — все це потрібно перерахувати. Якщо зробити в лоб, суми не зійдуться, фіскальний чек піде з помилкою. Розберемо, як правильно налаштувати часткове повернення за допомогою Sale API та платіжних систем.
Ми, команда з багаторічним досвідом розробки на Бітріксі (виконали 30+ проектів по поверненнях), підготували готове рішення, яке враховує всі нюанси. Нижче — технічна реалізація, яку ви можете адаптувати під свій проект. У проекті з 10 000 замовлень на місяць часткові повернення складають 12% — налаштування зекономила 40 годин роботи менеджерів щомісяця. При середній зарплаті менеджера в 20 000 грн на місяць економія складає 10 000 грн щомісяця.
Налаштування часткового повернення включає розрахунок знижок, ПДВ та інтеграцію з ЮKassa. Автоматизоване рішення обробляє повернення у 4 рази швидше, ніж ручний розрахунок.
Проблеми, які вирішує часткове повернення
Розрахунок суми повернення при складних знижках — найскладніша частина. Якщо на замовлення діє купон або знижка на кошик, просто взяти ціну товару недостатньо. Потрібно перерахувати знижку на позицію, що повертається. Крім того, товари можуть обкладатися ПДВ за ставками 10%, 20% або 0% — для чека потрібно вказати ставку для кожної позиції. Окреме питання — повернення доставки: якщо повертається не все замовлення, доставка зазвичай не повертається, але є винятки (наприклад, відмова від усіх товарів). І головне — фіскалізація повернення: чек має бути переданий в ОФД протягом встановленого часу, інакше штраф.
Розрахунок суми повернення: три підходи
Пропорційний підхід ділить загальну суму знижки на всі товари — він простий, але дає похибку до 2% через округлення. За фактичною вартістю позиції (використовує фінальну ціну після знижок) — точніше в 3 рази, відповідає логіці Бітрікса. За початковою ціною без знижок — зручний для повернення бракованого товару, але магазин втрачає вигоду покупця. Ми рекомендуємо другий підхід: він мінімізує помилки і використовується в наших продакшен-проектах.
| Підхід | Точність | Складність | Коли застосовувати |
|---|---|---|---|
| Пропорційний | ±2% | Низька | Мала кількість товарів |
| За фактичною вартістю | Висока | Середня | Більшість випадків |
| За початковою ціною | Висока | Низька | Бракований товар |
Нижче — приклад реалізації калькулятора.
namespace Local\Returns;
class PartialReturnCalculator
{
public function calculateRefundAmount(\Bitrix\Sale\Order $order, array $returnItems): array
{
// $returnItems: [[\'basket_id\' => int, \'quantity\' => float], ...]
$refundItems = [];
$totalRefund = 0.0;
$basket = $order->getBasket();
foreach ($returnItems as $item) {
$basketItem = $basket->getItemById($item[\'basket_id\']);
if (!$basketItem) continue;
$qty = min((float)$item[\'quantity\'], $basketItem->getQuantity());
$pricePerUnit = $basketItem->getFinalPrice(); // ціна з урахуванням знижок
$lineTotal = round($pricePerUnit * $qty, 2);
$refundItems[] = [
\'basket_id\' => $item[\'basket_id\'],
\'name\' => $basketItem->getField(\'NAME\'),
\'quantity\' => $qty,
\'price\' => $pricePerUnit,
\'line_total\' => $lineTotal,
\'vat_rate\' => $basketItem->getField(\'VAT_RATE\') ?? 0,
];
$totalRefund += $lineTotal;
}
// Перерахунок доставки при частковому поверненні
$shippingRefund = $this->calculateShippingRefund($order, $returnItems, $totalRefund);
return [
\'items\' => $refundItems,
\'items_total\' => $totalRefund,
\'shipping_refund\'=> $shippingRefund,
\'total\' => round($totalRefund + $shippingRefund, 2),
];
}
private function calculateShippingRefund(
\Bitrix\Sale\Order $order,
array $returnItems,
float $returnItemsTotal
): float {
// Якщо повертається все замовлення — повертаємо доставку повністю
$totalOrderItems = 0;
$returnBasketIds = array_column($returnItems, \'basket_id\');
foreach ($order->getBasket() as $item) {
$totalOrderItems++;
}
if (count($returnBasketIds) === $totalOrderItems) {
$shipment = $order->getShipmentCollection()->getSystemShipment();
return $shipment ? (float)$shipment->getDeliveryPrice() : 0.0;
}
// Інакше — доставка не повертається (залежить від політики магазину)
return 0.0;
}
}
Як створити часткове повернення через Sale API?
Після розрахунку суми створюємо об'єкт OrderReturn і прив'язуємо до нього позиції. Ось повний приклад:
class PartialReturnManager
{
public function create(
int $orderId,
array $returnItems,
string $reason = \'\',
int $userId = 0
): int {
\Bitrix\Main\Loader::includeModule(\'sale\');
$order = \Bitrix\Sale\Order::load($orderId);
if (!$order) throw new \RuntimeException("Order not found: {$orderId}");
if ($userId && $order->getUserId() !== $userId) {
throw new \RuntimeException("Access denied to order {$orderId}");
}
// Розраховуємо суми
$calculator = new PartialReturnCalculator();
$refundData = $calculator->calculateRefundAmount($order, $returnItems);
// Створюємо об'єкт повернення
$orderReturn = \Bitrix\Sale\OrderReturn::create($order);
$orderReturn->setField(\'STATUS_ID\', \'WAIT\');
$orderReturn->setField(\'TYPE\', \'MONEY\');
$orderReturn->setField(\'REASON\', $reason ?: \'Часткове повернення\');
$orderReturn->setField(\'REFUND_AMOUNT\', $refundData[\'total\']);
$orderReturn->setField(\'COMMENT\', $this->buildComment($refundData));
// Додаємо позиції повернення
foreach ($refundData[\'items\'] as $item) {
$basketItem = $order->getBasket()->getItemById($item[\'basket_id\']);
if (!$basketItem) continue;
$returnItem = $orderReturn->getReturn()->createItem($basketItem);
$returnItem->setField(\'QUANTITY\', $item[\'quantity\']);
$returnItem->setField(\'REASON\', $reason);
}
$result = $orderReturn->save();
if (!$result->isSuccess()) {
throw new \RuntimeException(
\'Partial return failed: \' . implode(\'; \', $result->getErrorMessages())
);
}
// Оновлюємо статус замовлення, якщо потрібно
$this->updateOrderAfterPartialReturn($order, $returnItems);
return $orderReturn->getId();
}
private function updateOrderAfterPartialReturn(
\Bitrix\Sale\Order $order,
array $returnItems
): void {
$returnBasketIds = array_column($returnItems, \'basket_id\');
$totalBasketItems = count([...$order->getBasket()]);
// Якщо повертається останній товар — позначаємо замовлення як частково повернене
if (count($returnBasketIds) < $totalBasketItems) {
// Додаємо користувацький статус "Частково повернено"
// через поле USER_DESCRIPTION або кастомний статус
}
}
private function buildComment(array $refundData): string
{
$lines = [\'Часткове повернення:\'];
foreach ($refundData[\'items\'] as $item) {
$lines[] = sprintf(
\'- %s × %s = %s грн.\',
$item[\'name\'],
$item[\'quantity\'],
number_format($item[\'line_total\'], 2)
);
}
if ($refundData[\'shipping_refund\'] > 0) {
$lines[] = sprintf(\'- Доставка: %s грн.\', number_format($refundData[\'shipping_refund\'], 2));
}
$lines[] = sprintf(\'Разом до повернення: %s грн.\', number_format($refundData[\'total\'], 2));
return implode("\n", $lines);
}
}
Чому важлива інтеграція з платіжною системою?
Більшість платіжних систем (ЮKassa, Тінькофф, Сбер) підтримують часткове повернення через API. Головний ризик — якщо просто повернути гроші, але не передати чек, то податкові органи нарахують штраф до 50 000 грн за невиконання 54-ФЗ. Наше рішення автоматично формує чек повернення з правильними кодами і відправляє його через ОФД. Згідно з документацією 1С-Бітрікс, OrderReturn коректно обробляє зв'язок з платіжними транзакціями.
Приклад для ЮKassa:
class YooKassaPartialRefund
{
public function refund(\Bitrix\Sale\Payment $payment, float $amount, array $items): bool
{
$paymentId = $payment->getField(\'PS_INVOICE_ID\'); // ID платежу в ЮKassa
$receipt = $this->buildReceipt($items); // чек для ФНС
$response = $this->yukassaClient->createRefund([
\'payment_id\' => $paymentId,
\'amount\' => [
\'value\' => number_format($amount, 2, \'.\', \'\'),
\'currency\' => \'RUB\',
],
\'description\' => \'Часткове повернення за замовленням #\' . $payment->getOrderId(),
\'receipt\' => $receipt,
]);
return isset($response[\'id\']) && $response[\'status\'] !== \'canceled\';
}
private function buildReceipt(array $items): array
{
$receiptItems = [];
foreach ($items as $item) {
$receiptItems[] = [
\'description\' => $item[\'name\'],
\'quantity\' => $item[\'quantity\'],
\'amount\' => [
\'value\' => number_format($item[\'price\'], 2, \'.\', \'\'),
\'currency\' => \'RUB\',
],
\'vat_code\' => $this->vatRateToCode((float)$item[\'vat_rate\']),
\'payment_mode\' => \'full_payment\',
\'payment_subject\' => \'commodity\',
];
}
return [
\'customer\' => [\'email\' => $this->customerEmail],
\'items\' => $receiptItems,
];
}
}
Ключовий момент: при частковому поверненні потрібно передати чек в ФНС через ОФД. ЮKassa робить це автоматично, якщо передати receipt в запиті повернення.
Що входить у роботу?
| Компонент | Опис |
|---|---|
| Калькулятор суми повернення | Враховує знижки, купони, ПДВ, кількість |
| API створення повернення | PartialReturnManager з валідацією та збереженням |
| Інтерфейс вибору позицій | В особистому кабінеті покупця та адмінці |
| Інтеграція з платіжними системами | ЮKassa, Сбер, Тінькофф (partial refund) |
| Формування чека для ФНС | Коректний чек з кодами 54-ФЗ |
| Логіка повернення доставки | Налаштовується під політику магазину |
Процес реалізації та терміни
- Аналіз — вивчаємо поточні замовлення, знижки, платіжні системи.
- Проектування — обираємо підхід розрахунку, проектуємо API.
- Реалізація — пишемо калькулятор, менеджер повернення, інтеграцію з платіжкою.
- Тестування — перевіряємо на реальних замовленнях з різними сценаріями (один товар, кілька, зі знижкою, без).
- Деплой та документація — вивантажуємо на бойовий, пишемо інструкцію для операторів.
Терміни: базова механіка — 1–2 тижні; повна версія з інтеграцією кількох платіжних систем та 54-ФЗ — 3–5 тижнів. На всі роботи даємо гарантію 6 місяців.
Якщо ви хочете впровадити часткове повернення на своєму проекті, зв'яжіться з нами — безкоштовно оцінимо проект і назвемо точні терміни. Замовте налаштування часткового повернення вже сьогодні: ваші покупці зможуть повертати тільки непотрібні позиції, а податкова отримає правильні чеки.







