При оформлении чеков с процентными или ручными скидками в конфигурациях 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С.
Разберем первый способ устранения проблемы: пересчет скидки в документе таким образом, чтобы итоговая сумма позиции без остатка делилась на количество товара. В этом случае цена со скидкой, округленная до копеек, при умножении на количество всегда будет давать точную сумму строки.
Посмотрим на пример пересчета скидки в процедуре назначения скидок:
Процедура НазначитьРучнуюСкидку(СтрокаТЧ, СуммаБезСкидки, СуммаСкидки)
СтрокаТЧ.Сумма = СуммаБезСкидки - ?(СуммаСкидки > СуммаБезСкидки, СуммаБезСкидки, СуммаСкидки);
// Вычисляем цену единицы со скидкой, округленную до копеек
лЦенаСоСкидкой = Окр(СтрокаТЧ.Сумма / СтрокаТЧ.Количество, 2);
лСуммаСоСкидкой = лЦенаСоСкидкой * СтрокаТЧ.Количество;
// Определяем разницу округления
лРазницаВычислений = СтрокаТЧ.Сумма - лСуммаСоСкидкой;
// Корректируем сумму скидки на величину погрешности
СтрокаТЧ.СуммаРучнойСкидки = СтрокаТЧ.СуммаРучнойСкидки + лРазницаВычислений;
СуммаСкидки = СуммаСкидки + лРазницаВычислений;
// Фиксируем итоговую сумму строки, кратную цене
СтрокаТЧ.Сумма = СуммаБезСкидки - ?(СуммаСкидки > СуммаБезСкидки, СуммаБезСкидки, СуммаСкидки);
КонецПроцедуры
При таком подходе данные в учетной системе изначально формируются корректно для передачи в ККТ, что исключает расхождения при печати чека.
Рассмотрим альтернативный и более универсальный вариант, который не затрагивает логику ценообразования и скидок в учетных документах. В данном случае корректировка выполняется непосредственно в момент формирования фискального пакета XML (CheckPackage) перед отправкой в драйвер кассы.
Если товар штучный и количество больше единицы, позицию с неделимой копейкой разбивают на две фискальные строки:
(Количество - 1) по расчетной округленной цене.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.ЗаписатьКонецЭлемента();
СуммаЧека = СуммаЧека + ТекущаяПозиция.Сумма;
КонецЕсли;
При внедрении исправлений необходимо учитывать специфику продаваемой номенклатуры:
0,134 кг) разбиение на штуки невозможно. Для таких товаров рекомендуется использовать пересчет скидки до округленной стоимости или настраивать правила округления чека в пользу покупателя.