Проблема распределения скидок и возникающая при этом «ошибка копейки» — это классическая задача, с которой сталкивается практически каждый разработчик при автоматизации торговых систем. Ситуация усложняется, когда итоговая сумма после скидки не делится на количество товара без остатка, или когда мы имеем дело с дробным количеством (например, весовой товар). В этой статье мы подробно разберем, как корректно распределить скидку, чтобы итоговая сумма чека совпала до копейки, и какие типовые механизмы 1С помогут нам избежать «изобретения велосипеда».
Рассмотрим конкретный сценарий. Предположим, мы продаем 200 единиц товара по цене 0,31 руб. Общая сумма составляет 62 руб. Покупателю предоставляется скидка в размере 31 руб. (ровно 50%). Нам необходимо определить новую цену за единицу товара.
Если мы просто разделим остаток суммы на количество (31 / 200), мы получим цену 0,155 руб. Однако стандартная точность хранения денежных сумм в 1С (и требования фискальных накопителей) ограничена двумя знаками после запятой. Округлить до 0,15 или 0,16 мы не можем: в первом случае сумма будет 30 руб., во втором — 32 руб. Ни один из вариантов не дает требуемые 31 руб.
Единственный верный путь — это разделение одной товарной позиции на несколько строк с разными ценами (но различающимися всего на одну копейку), чтобы в сумме они давали точный результат. В нашем примере это выглядит так:
Итого: 22,5 + 8,5 = 31 рубль. Задача решена, баланс соблюден.
Для того чтобы не писать сложные циклы распределения остатка вручную, в составе Библиотеки стандартных подсистем (БСП) предусмотрена специализированная функция. Проанализируем её использование.
Рассмотрим функцию ОбщегоНазначения.РаспределитьСуммуПропорциональноКоэффициентам. В зависимости от версии вашей конфигурации (УТ, ERP, КА), она может находиться в модуле ОбщегоНазначения или ОбщегоНазначенияКлиентСервер.
Эта функция принимает на вход общую сумму, которую нужно распределить, и массив коэффициентов (в нашем случае — количество товара). На выходе она возвращает массив распределенных сумм, где сумма элементов в точности равна исходной, а погрешность округления распределена между элементами.
Важное замечание: в старых версиях УТ 11.1 данная функция могла работать не совсем корректно с дробными числами или специфическими наборами данных. Однако в актуальных версиях БСП (3.1 и выше) алгоритм значительно доработан. Он сначала рассчитывает целую часть для каждого элемента, а затем «лишние» копейки распределяет по одной между элементами, имеющими наибольшую дробную часть при делении.
В современных реалиях, особенно с учетом требований 54-ФЗ и форматов фискальных данных (ФФД), просто посчитать суммы недостаточно. В чеке должно соблюдаться равенство: Цена * Количество = Сумма. Если цена имеет более 2-х знаков после запятой, ККТ (контрольно-кассовая техника) выдаст ошибку.
Рассмотрим по шагам, как программно реализовать разбиение одной строки документа на две, если сумма скидки не распределяется нацело:
Пример программной реализации логики распределения (схематично):
// СуммаКРаспределению - итоговая сумма, которую мы хотим получить
// ОбщееКоличество - исходное количество товара в строке
// Коэффициенты - массив, где каждый элемент равен 1 (если распределяем поштучно)
МассивКоэффициентов = Новый Массив;
Для Счетчик = 1 По ОбщееКоличество Цикл
МассивКоэффициентов.Добавить(1);
КонецЦикла;
РезультатРаспределения = ОбщегоНазначения.РаспределитьСуммуПропорциональноКоэффициентам(СуммаКРаспределению, МассивКоэффициентов);
Однако такой подход неудобен при большом количестве (например, 10 000 шт.). В этом случае проанализируем более эффективный метод — группировку результата распределения по одинаковым суммам.
Проанализируем ситуацию с весовым товаром, например, количество 0,003 кг. Здесь погрешность округления становится критической, так как точность количества в 1С обычно составляет 3 знака, а суммы — 2 знака.
Если мы не можем распределить скидку так, чтобы цена за единицу веса была кратна копейке, рекомендуется использовать механизм «Скидка на чек» (округление итоговой суммы чека). В этом случае разница в копейках не распределяется по товарам, а списывается на отдельную статью расходов организации. Это позволяет избежать проблем с расчетом себестоимости микро-порций товара.
Выясним причину, почему нельзя просто списать всю скидку на одну позицию, обнулив её цену. При использовании бонусных программ часто возникает желание оплатить товар бонусами на 100%. С точки зрения законодательства и бухгалтерского учета, продажа по цене 0 руб. может быть расценена как безвозмездная передача (дарение), что влечет за собой налоговые риски.
Поэтому в алгоритме распределения всегда нужно предусматривать проверку «минимальной цены». Обычно это 1 копейка или 1 рубль. Если после распределения скидки цена товара становится меньше этого порога, алгоритм должен «перекинуть» излишек скидки на другие товары в чеке, где запас цены еще позволяет это сделать.
Для решения задачи распределения скидок в 1С:
ОбщегоНазначения.РаспределитьСуммуПропорциональноКоэффициентам для получения точных сумм.Такой системный подход позволит вам создавать надежные механизмы расчета, которые не вызовут нареканий ни у бухгалтерии, ни у налоговых органов, ни у покупателей.