При автоматизации розничной торговли и внедрении бонусных программ лояльности разработчики регулярно сталкиваются с задачей распределения общей суммы бонусов или фиксированной скидки по позициям чека. Современные фискальные регистраторы и онлайн-кассы требуют, чтобы в фискальный накопитель передавалась стоимость каждой конкретной единицы товара с уже учтенными скидками, а сумма всех строк чека копейка в копейку сходилась с итоговой суммой оплаты. Разберем по шагам, как построить надежный алгоритм распределения скидки в среде 1С:Предприятие 7.7 с использованием таблицы значений.
На первый взгляд задача кажется простой: достаточно посчитать долю каждой позиции в общей стоимости чека и умножить общую скидку на эту долю. Однако на практике возникают три ключевых препятствия:
Рассмотрим типовую ситуацию: у нас есть таблица значений ТЗ со списком позиций и колонкой максимальной возможной скидки МаксСкидка. Для вычисления доли в каждой строке требуется знать сумму текущей и всех последующих строк. Попытка на каждом шаге цикла заново обращаться к строкам таблицы приводит к лишней вычислительной сложности.
Проанализируем эффективный подход: перед входом в цикл мы берем общую сумму по колонке через метод Итог(), а внутри цикла поддерживаем накопительную переменную уже пройденных строк. Разность общей суммы и накопленного итога дает точную сумму оставшейся части чека.
Посмотрим на пример базового цикла обхода:
ОбщаяСумма = ТЗ.Итог("МаксСкидка");
СуммаПройденныхСтрок = 0;
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл
ОстатокКРаспределению = ОбщаяСумма - СуммаПройденныхСтрок;
// Выполняем расчет для текущей строки чека
СуммаПройденныхСтрок = СуммаПройденныхСтрок + ТЗ.МаксСкидка;
КонецЦикла;
Теперь разберем полноценный алгоритм распределения скидки СуммаСкидкиВсего по строкам чека. Чтобы не потерять копейки и гарантировать, что сумма распределенных скидок сойдется с исходной величиной цент в цент, будем уменьшать распределяемую скидку на каждом шаге цикла, а для последней доступной строки просто отдадим весь нераспределенный остаток.
Рассмотрим готовый фрагмент кода для 1С 7.7:
// Входные переменные:
// ТЗ - ТаблицаЗначений, содержащая колонки: Товар, Сумма, МаксСкидка, СкидкаНаСтроку
// СуммаСкидкиВсего - общая сумма бонусов/скидки, которую нужно распределить
ОстатокСкидки = Мин(СуммаСкидкиВсего, ТЗ.Итог("МаксСкидка"));
ОстатокБазы = ТЗ.Итог("МаксСкидка");
Если ОстатокБазы > 0 Тогда
ВсегоСтрок = ТЗ.КоличествоСтрок();
Для НомерСтр = 1 По ВсегоСтрок Цикл
ТЗ.ПолучитьСтрокуПоНомеру(НомерСтр);
ТекущийЛимит = ТЗ.МаксСкидка;
Если ТекущийЛимит <= 0 Тогда
ТЗ.СкидкаНаСтроку = 0;
Продолжить;
КонецЕсли;
Если НомерСтр = ВсегоСтрок Тогда
// На последней строке отдаем весь оставшийся «хвост» скидки
ТекущаяСкидка = ОстатокСкидки;
Иначе
// Рассчитываем пропорциональную долю от текущего остатка
Доля = ТекущийЛимит / ОстатокБазы;
ТекущаяСкидка = Окр(ОстатокСкидки * Доля, 2, 1);
// Проверяем, чтобы скидка не превысила установленный максимум строки
ТекущаяСкидка = Мин(ТекущаяСкидка, ТекущийЛимит);
ТекущаяСкидка = Мин(ТекущаяСкидка, ОстатокСкидки);
КонецЕсли;
ТЗ.СкидкаНаСтроку = ТекущаяСкидка;
// Уменьшаем остатки для следующих итераций
ОстатокСкидки = ОстатокСкидки - ТекущаяСкидка;
ОстатокБазы = ОстатокБазы - ТекущийЛимит;
Если ОстатокСкидки <= 0 Тогда
Прервать;
КонецЕсли;
КонецЦикла;
КонецЕсли;
При реализации расчетов в 1С 7.7 важно учитывать техническое поведение объекта ТаблицаЗначений:
НоваяКолонка("Скидка", "Число", 15, 2), платформа будет автоматически округлять любое записываемое значение до двух знаков после запятой. Промежуточные коэффициенты (доли, проценты) нельзя сохранять в колонки с точностью 2 знака — для них используйте локальные переменные модуля или создавайте служебные колонки с повышенной точностью (например, точность 5 или 6 знаков).МаксСкидка для каждой позиции должна заранее рассчитываться как СуммаСтроки - (Количество * 0.01).Если бизнес-процессы компании и кассовое программное обеспечение позволяют закрывать чек несколькими типами оплат, распределения скидки по строкам можно полностью избежать. В этом случае списание бонусов оформляется не как ценовая скидка, а как встречное предоставление или безналичная оплата отдельной секцией (например, оплата бонусами или подарочным сертификатом). Чек пробивается по полным розничным ценам, а в фискальный регистратор передаются две строки оплаты: «Наличные/Карта» и «Бонусы». Это снимает проблему распределения копеек и гарантирует полное соответствие фискальным нормам.