При пробитии чеков на контрольно-кассовой технике (ККТ) разработчики и пользователи конфигураций 1С (в частности, 1С:Управление торговлей 10.3) часто сталкиваются с ошибкой форматно-логического контроля: сумма по строкам или итог документа не совпадает с суммой, которую рассчитывает сама онлайн-касса. Проанализируем природу этой ошибки, разберем ограничения протоколов фискализации и выясним, как правильно решить проблему.
Посмотрим на типовую ситуацию. Документ содержит весовой товар, где количество указано с точностью до тысячных долей (например, тонны), а расчет НДС настроен по схеме «сверху»:
26.4 тонны.5279.96 руб.5279.96 / 26.4 = 199.884848... руб.199.88 руб.199.88 * 26.4 = 5276.83 руб.В результате возникает расхождение в 3.13 руб. между итогом документа и суммой, вычисленной кассовым аппаратом. Согласно требованиям форматов фискальных документов (ФФД), реквизиты «цена за единицу» (тег 1079) и «стоимость предмета расчета» (тег 1043) ограничены двумя знаками после запятой (копейки), а «количество» (тег 1023) в большинстве ККТ — тремя знаками. Кроме того, драйверы ККТ запрещают передавать отрицательные позиции (например, сторнирующие строки услуг или корректировки), поэтому просто отправить разницу отдельной строкой невозможно.
Часто программисты пытаются решить эту задачу программным разбиением одной товарной позиции на две строки с ценами, отличающимися на 1 копейку. Рассмотрим предложенный на практике алгоритм перебора:
КолВо = 26.4;
СуммаДок = 5279.96;
Цена = Окр(СуммаДок / КолВо, 2);
СуммаВыч = Окр(Цена * КолВо, 2);
Если (СуммаВыч = СуммаДок) или (КолВо <= 1) Тогда
Сообщить("" + Цена + " * " + КолВо + " = " + СуммаВыч);
Иначе
КолВоМинус = 0;
Дельта = 1;
ФлагВывести = Истина;
Пока КолВоМинус >= 0 Цикл
КолВоМинус = КолВо - Дельта;
СуммаМинус = КолВоМинус * Цена;
НовЦена = (СуммаДок - СуммаМинус) / Дельта;
РазницаЦен = Макс(НовЦена - Цена, -(НовЦена - Цена));
Если РазницаЦен = 0.01 Тогда
Сообщить("" + Цена + " * " + КолВоМинус + " = " + СуммаМинус);
Сообщить("" + НовЦена + " * " + Дельта + " = " + НовЦена * Дельта);
ФлагВывести = Ложь;
Прервать;
Иначе
Дельта = Дельта + 1;
КонецЕсли;
КонецЦикла;
Если ФлагВывести Тогда
Сообщить("!! " + Цена + " * " + КолВо + " = " + СуммаВыч);
КонецЕсли;
КонецЕсли;
Разберем по шагам, почему данный алгоритм приводит к ошибкам:
Дельта = Дельта + 1 отнимает от общего объема целые единицы. Для дробного количества (26.4 т) расчет КолВоМинус = КолВо - Дельта всегда оставляет дробную часть (25.4, 24.4 и т.д.), что делает математически невозможным подбор ровной копейки целыми шагами. Шаг должен быть согласован с минимальной дискретностью единицы (0.001), а не единицей.Цена1 и Цена2 = Цена1 + 0.01) решается аналитически через систему линейных уравнений за одно действие:
Цена1 = ОкрВниз(СуммаДок / КолВо, 2);
Цена2 = Цена1 + 0.01;
КолВо2 = (СуммаДок - (Цена1 * КолВо)) / 0.01;
КолВо1 = КолВо - КолВо2;
КолВо2 почти всегда получает более трех знаков после запятой. Округление количества до допустимых кассой трех знаков снова разрушает баланс, и касса выдает ошибку несоответствия сумм.Рассмотрим проверенные и методически верные способы решения проблемы округления при печати чеков:
Окр(Цена * Количество, 2), и касса принимает чек без единой ошибки.26.4 т передается 26400 кг). При этом важно контролировать, чтобы цена за единицу не опустилась ниже 0.01 руб., иначе касса отклонит операцию с нулевой ценой.