Как правильно распределить общую скидку или оплату бонусами по позициям чека в 1С

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Торговля и дистрибуция
← На главную

В розничной и оптовой торговле часто возникает задача: покупателю предоставляется фиксированная скидка на весь чек (например, 50 рублей на корзину из товаров стоимостью 200 и 150 рублей) или списываются бонусные баллы, а учетная система должна корректно распределить эту сумму построчно. Разберем, как реализовать такой расчет, какие типовые инструменты платформы 1С и БСП для этого существуют, а также выясним, как избежать ошибок при округлении копеек при взаимодействии с онлайн-кассами (ККТ) по 54-ФЗ.

Требования фискализации и суть проблемы

Согласно форматам фискальных данных (ФФД 1.05, 1.1, 1.2), в фискальный накопитель и ОФД не передается отдельный реквизит общей скидки на чек. Кассовый аппарат принимает каждую товарную позицию с уже рассчитанной стоимостью с учетом скидки (тег 1043). При этом драйвер ККТ и фискальный накопитель строго контролируют два правила:

  1. Сумма строк чека с учетом скидок обязана копейка в копейку совпадать с итоговой суммой оплат.
  2. Стоимость позиции должна равняться произведению цены на количество (с допустимым отклонением не более 1 копейки).

Если распределять скидку простым умножением на процент и округлять каждую строку по отдельности, возникает погрешность. Например, при сумме скидки 500 рублей на три одинаковых товара по 1000 рублей каждый товар получит скидку 166,67 рубля (итого 500,01 руб.) или 166,66 рубля (итого 499,98 руб.). Возникающая копейка приводит к блокировке пробития чека на ККМ из-за несовпадения итогов.

Способ 1. Использование типовой функции БСП

В типовых конфигурациях 1С (ERP, КА, УТ, Розница, УНФ) на базе «Библиотеки стандартных подсистем» (БСП) есть готовый и протестированный метод, исключающий проблемы с погрешностями округления. Рассмотрим процедуру РаспределитьСуммуПропорциональноКоэффициентам общего модуля ОбщегоНазначенияКлиентСервер или ОбщегоНазначения.

Разберем пример кода:


// Подготовим массив сумм позиций документа
МассивСумм = Новый Массив;
Для Каждого Стр Из ТаблицаТоваров Цикл
    МассивСумм.Добавить(Стр.СуммаБезСкидки);
КонецЦикла;

// Вызываем типовое распределение БСП
МассивСкидок = ОбщегоНазначенияКлиентСервер.РаспределитьСуммуПропорциональноКоэффициентам(
    СуммаСкидкиКРаспределению, 
    МассивСумм, 
    2 // Округление до 2 знаков (копеек)
);

// Записываем результат обратно в строки
Для Индекс = 0 По ТаблицаТоваров.Количество() - 1 Цикл
    Стр = ТаблицаТоваров[Индекс];
    Стр.СуммаСкидки = МассивСкидок[Индекс];
    Стр.Сумма = Стр.СуммаБезСкидки - Стр.СуммаСкидки;
КонецЦикла;

Этот метод БСП внутри себя использует алгоритм наибольших остатков (метод Гамильтона), благодаря которому нераспределенные копейки аккуратно прибавляются к позициям с наибольшей дробной частью, а итоговая сумма всегда сходится с исходной до копейки.

Способ 2. Программное распределение с уменьшающимся остатком

Если конфигурация самописная или не использует БСП, применим алгоритм распределения в цикле с динамическим уменьшением оставшейся базы и скидки. В таком варианте на каждую следующую позицию рассчитывается доля от оставшейся нераспределенной суммы, а на последнюю строку автоматически падает точный остаток.

Посмотрим на пример реализации:


ОстатокСкидки = СуммаСкидкиКРаспределению;
ОстатокБазы   = СуммаЧекаБезСкидки;

ВсегоСтрок = ТаблицаТоваров.Количество();
Для НомерСтроки = 1 По ВсегоСтрок Цикл
    Стр = ТаблицаТоваров[НомерСтроки - 1];
    
    Если НомерСтроки = ВсегоСтрок Тогда
        // На последнюю строку относим оставшийся хвост
        СкидкаПоСтроке = ОстатокСкидки;
    Иначе
        Если ОстатокБазы <> 0 Тогда
            СкидкаПоСтроке = Окр(Стр.СуммаБезСкидки * (ОстатокСкидки / ОстатокБазы), 2);
        Иначе
            СкидкаПоСтроке = 0;
        КонецЕсли;
    КонецЕсли;
    
    Стр.СуммаСкидки = СкидкаПоСтроке;
    Стр.Сумма = Стр.СуммаБезСкидки - СкидкаПоСтроке;
    
    // Уменьшаем остатки для следующей итерации
    ОстатокСкидки = ОстатокСкидки - СкидкаПоСтроке;
    ОстатокБазы   = ОстатокБазы - Стр.СуммаБезСкидки;
КонецЦикла;

Важные нюансы при расчете скидок для фискальных чеков

При реализации собственного распределения скидок важно учитывать специфику законодательства и торговых операций:

  1. Товары с минимальной розничной ценой (МРЦ): Алкоголь, табачная и никотинсодержащая продукция имеют законодательно установленный нижний предел цены. Перед распределением скидки исключим такие позиции из базы расчета, либо ограничим максимальную скидку по строке разницей между текущей ценой и МРЦ.
  2. Весовой товар с дробным количеством: Если в чеке присутствует, например, 0.335 кг товара, то при уменьшении суммы строки цена за единицу Сумма / Количество может получить бесконечную дробную часть. Для чеков ККТ в таких ситуациях применяется алгоритм расщепления строки на две позиции с целочисленными копейками в цене.
  3. Скидка или встречное предоставление (Бонусы): Если бонусная программа юридически оформлена не как прямая скидка, а как оплата сертификатами/авансом, сумму бонусов не нужно распределять по строкам как скидку. В чек передаются исходные цены товаров, а в видах оплат фискального чека указываются две суммы: безналичная оплата (тег 1081) и встречное предоставление (тег 1215).
← На главную