Почему сумма чека в 1С расходится с кассовым аппаратом на копейки при применении скидок и как это исправить?

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

При оформлении чеков с процентными или ручными скидками в конфигурациях 1С (например, «1С:Управление торговлей» или «1С:Розница») нередко возникает ситуация, когда сумма документа в программе расходится с суммой, которую печатает онлайн-касса, на 1–2 копейки. В результате кассовый аппарат либо пробивает чек на сумму больше принятой от покупателя наличности, что приводит к недостаче, либо вовсе возвращает ошибку «Сумма всех типов оплаты меньше итога чека» и блокирует операцию. Проанализируем, почему это происходит и как правильно решить проблему.

Причины возникновения коллизии

Выясним причину расхождения. В большинстве конфигураций 1С исторически скидка распределяется от общей суммы позиции или всего чека. Например, если продаются 4 штуки товара по базовой цене 290,00 рублей со скидкой 70,18%, сумма строки в 1С рассчитывается как 1160,00 - 814,09 = 345,91 руб.

Однако в соответствии с требованиями 54-ФЗ и форматами фискальных документов (ФФД) в кассовый чек передаются параметры позиции: цена единицы без скидки (тег 1079), количество (тег 1023) и итоговая сумма с учетом скидки (тег 1043). В драйвере кассы или фискальном накопителе заложен строгий контроль математического равенства:

Конечная сумма позиции = Количество × Цена единицы с учетом скидки

Кассовый аппарат делит переданную сумму 345,91 на количество 4, получает цену единицы со скидкой 86,4775, округляет ее до копеек — 86,48 руб., а затем перемножает обратно на количество: 4 × 86,48 = 345,92 руб. Возникает расхождение в 1 копейку, из-за которого итоговая сумма чека на кассе превышает расчетное значение в 1С.

Вариант 1. Корректировка расчета скидок в табличной части 1С

Разберем первый способ устранения проблемы: пересчет скидки в документе таким образом, чтобы итоговая сумма позиции без остатка делилась на количество товара. В этом случае цена со скидкой, округленная до копеек, при умножении на количество всегда будет давать точную сумму строки.

Посмотрим на пример пересчета скидки в процедуре назначения скидок:


Процедура НазначитьРучнуюСкидку(СтрокаТЧ, СуммаБезСкидки, СуммаСкидки)
    
    СтрокаТЧ.Сумма = СуммаБезСкидки - ?(СуммаСкидки > СуммаБезСкидки, СуммаБезСкидки, СуммаСкидки);
    
    // Вычисляем цену единицы со скидкой, округленную до копеек
    лЦенаСоСкидкой = Окр(СтрокаТЧ.Сумма / СтрокаТЧ.Количество, 2);
    лСуммаСоСкидкой = лЦенаСоСкидкой * СтрокаТЧ.Количество;
    
    // Определяем разницу округления
    лРазницаВычислений = СтрокаТЧ.Сумма - лСуммаСоСкидкой;
    
    // Корректируем сумму скидки на величину погрешности
    СтрокаТЧ.СуммаРучнойСкидки = СтрокаТЧ.СуммаРучнойСкидки + лРазницаВычислений;
    СуммаСкидки = СуммаСкидки + лРазницаВычислений;
    
    // Фиксируем итоговую сумму строки, кратную цене
    СтрокаТЧ.Сумма = СуммаБезСкидки - ?(СуммаСкидки > СуммаБезСкидки, СуммаБезСкидки, СуммаСкидки);
    
КонецПроцедуры

При таком подходе данные в учетной системе изначально формируются корректно для передачи в ККТ, что исключает расхождения при печати чека.

Вариант 2. Программное расщепление строки в пакете фискализации (CheckPackage)

Рассмотрим альтернативный и более универсальный вариант, который не затрагивает логику ценообразования и скидок в учетных документах. В данном случае корректировка выполняется непосредственно в момент формирования фискального пакета XML (CheckPackage) перед отправкой в драйвер кассы.

Если товар штучный и количество больше единицы, позицию с неделимой копейкой разбивают на две фискальные строки:

  1. Первая строка: количество (Количество - 1) по расчетной округленной цене.
  2. Вторая строка: количество 1 с суммой, включающей оставшуюся копейку погрешности.

Разберем пример реализации данного алгоритма при формировании узлов FiscalString:


лЦенаСоСкидкой = Окр(ТекущаяПозиция.Сумма / ТекущаяПозиция.Количество, 2);
лРазница = ТекущаяПозиция.Сумма - (лЦенаСоСкидкой * ТекущаяПозиция.Количество);

Если лРазница <> 0 И ТекущаяПозиция.Количество > 1 Тогда
    
    // Рассчитываем сумму для первой части позиции
    лСуммаОсновная = лЦенаСоСкидкой * (ТекущаяПозиция.Количество - 1);
    
    // Формируем первую строку чека
    ЗаписьXML.ЗаписатьНачалоЭлемента("FiscalString");
    ЗаписьXML.ЗаписатьАтрибут("Name", XMLСтрока(ТекущаяПозиция.Наименование));
    ЗаписьXML.ЗаписатьАтрибут("Quantity", XMLСтрока(ТекущаяПозиция.Количество - 1));
    ЗаписьXML.ЗаписатьАтрибут("Price", XMLСтрока(ТекущаяПозиция.Цена));
    ЗаписьXML.ЗаписатьАтрибут("Amount", XMLСтрока(лСуммаОсновная));
    ЗаписьXML.ЗаписатьАтрибут("Department", XMLСтрока(ТекущаяПозиция.НомерСекции));
    ЗаписьXML.ЗаписатьАтрибут("Tax", ?(ПустаяСтрока(ТекущаяПозиция.СтавкаНДС), "none", XMLСтрока(ТекущаяПозиция.СтавкаНДС)));
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
    // Формируем вторую строку на 1 штуку с остатком суммы
    ЗаписьXML.ЗаписатьНачалоЭлемента("FiscalString");
    ЗаписьXML.ЗаписатьАтрибут("Name", XMLСтрока(ТекущаяПозиция.Наименование));
    ЗаписьXML.ЗаписатьАтрибут("Quantity", XMLСтрока(1));
    ЗаписьXML.ЗаписатьАтрибут("Price", XMLСтрока(ТекущаяПозиция.Цена));
    ЗаписьXML.ЗаписатьАтрибут("Amount", XMLСтрока(ТекущаяПозиция.Сумма - лСуммаОсновная));
    ЗаписьXML.ЗаписатьАтрибут("Department", XMLСтрока(ТекущаяПозиция.НомерСекции));
    ЗаписьXML.ЗаписатьАтрибут("Tax", ?(ПустаяСтрока(ТекущаяПозиция.СтавкаНДС), "none", XMLСтрока(ТекущаяПозиция.СтавкаНДС)));
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
    СуммаЧека = СуммаЧека + ТекущаяПозиция.Сумма;
    
Иначе
    
    // Стандартное добавление неразбиваемой строки
    ЗаписьXML.ЗаписатьНачалоЭлемента("FiscalString");
    ЗаписьXML.ЗаписатьАтрибут("Name", XMLСтрока(ТекущаяПозиция.Наименование));
    ЗаписьXML.ЗаписатьАтрибут("Quantity", XMLСтрока(ТекущаяПозиция.Количество));
    ЗаписьXML.ЗаписатьАтрибут("Price", XMLСтрока(ТекущаяПозиция.Цена));
    ЗаписьXML.ЗаписатьАтрибут("Amount", XMLСтрока(ТекущаяПозиция.Сумма));
    ЗаписьXML.ЗаписатьАтрибут("Department", XMLСтрока(ТекущаяПозиция.НомерСекции));
    ЗаписьXML.ЗаписатьАтрибут("Tax", ?(ПустаяСтрока(ТекущаяПозиция.СтавкаНДС), "none", XMLСтрока(ТекущаяПозиция.СтавкаНДС)));
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
    СуммаЧека = СуммаЧека + ТекущаяПозиция.Сумма;
    
КонецЕсли;

Ограничения и особенности применения решений

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

  1. Маркированная продукция: Если товар подлежит обязательной маркировке («Честный ЗНАК»), расщепление строк в фискальном пакете требует обязательной привязки индивидуального кода маркировки (тег 1162 / 1163) к каждой выведенной строке.
  2. Весовой товар: Для весовых позиций (например, с количеством 0,134 кг) разбиение на штуки невозможно. Для таких товаров рекомендуется использовать пересчет скидки до округленной стоимости или настраивать правила округления чека в пользу покупателя.
  3. Округление чека в типовом функционале: В современных версиях типовых конфигураций 1С рекомендуется использовать штатную автоматическую скидку «Округление суммы документа» с наивысшим приоритетом, что минимизирует вероятность возникновения дробных копеек еще на этапе оформления продажи.
← На главную