Как распределить сумму скидки или бонусов по позициям чека в 1С 7.7

Программист 1С v7.7 Управленческий учет Торговля и дистрибуция
← На главную

При автоматизации розничной торговли и внедрении бонусных программ лояльности разработчики регулярно сталкиваются с задачей распределения общей суммы бонусов или фиксированной скидки по позициям чека. Современные фискальные регистраторы и онлайн-кассы требуют, чтобы в фискальный накопитель передавалась стоимость каждой конкретной единицы товара с уже учтенными скидками, а сумма всех строк чека копейка в копейку сходилась с итоговой суммой оплаты. Разберем по шагам, как построить надежный алгоритм распределения скидки в среде 1С:Предприятие 7.7 с использованием таблицы значений.

В чем заключается сложность алгоритма распределения

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

  1. Наличие индивидуальных ограничений по строкам. Не на все товары скидка может начисляться одинаково. Для определенных позиций может быть задан предел списания бонусов (например, не более 20% или 50% от цены), либо товары могут являться подакцизными (с жестко установленной минимальной розничной ценой — МРЦ), либо на товар уже действует акционная цена с нулевым лимитом списания.
  2. Проблема округления и копеечного разрыва. Простое деление и округление скидки по каждой строке независимо неизбежно приводит к разнице в несколько копеек между суммой скидок по строкам и исходной суммой скидки чека. Кассовое программное обеспечение и драйверы ККТ в этом случае выдают ошибку несовпадения итогов чека.
  3. Вычисление скользящих остатков в цикле. Чтобы избежать накопления погрешности, скидку необходимо рассчитывать динамически, вычисляя на каждом шаге отношение остатка скидки к оставшейся сумме доступных товаров.

Реализация расчета скользящей суммы в табличной части

Рассмотрим типовую ситуацию: у нас есть таблица значений ТЗ со списком позиций и колонкой максимальной возможной скидки МаксСкидка. Для вычисления доли в каждой строке требуется знать сумму текущей и всех последующих строк. Попытка на каждом шаге цикла заново обращаться к строкам таблицы приводит к лишней вычислительной сложности.

Проанализируем эффективный подход: перед входом в цикл мы берем общую сумму по колонке через метод Итог(), а внутри цикла поддерживаем накопительную переменную уже пройденных строк. Разность общей суммы и накопленного итога дает точную сумму оставшейся части чека.

Посмотрим на пример базового цикла обхода:


ОбщаяСумма = ТЗ.Итог("МаксСкидка");
СуммаПройденныхСтрок = 0;

ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл
    ОстатокКРаспределению = ОбщаяСумма - СуммаПройденныхСтрок;
    
    // Выполняем расчет для текущей строки чека
    
    СуммаПройденныхСтрок = СуммаПройденныхСтрок + ТЗ.МаксСкидка;
КонецЦикла;

Алгоритм пропорционального распределения без потери копеек

Теперь разберем полноценный алгоритм распределения скидки СуммаСкидкиВсего по строкам чека. Чтобы не потерять копейки и гарантировать, что сумма распределенных скидок сойдется с исходной величиной цент в цент, будем уменьшать распределяемую скидку на каждом шаге цикла, а для последней доступной строки просто отдадим весь нераспределенный остаток.

Рассмотрим готовый фрагмент кода для 1С 7.7:


// Входные переменные:
// ТЗ - ТаблицаЗначений, содержащая колонки: Товар, Сумма, МаксСкидка, СкидкаНаСтроку
// СуммаСкидкиВсего - общая сумма бонусов/скидки, которую нужно распределить

ОстатокСкидки = Мин(СуммаСкидкиВсего, ТЗ.Итог("МаксСкидка"));
ОстатокБазы = ТЗ.Итог("МаксСкидка");

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

Особенности работы с типами данных в 1С:Предприятие 7.7

При реализации расчетов в 1С 7.7 важно учитывать техническое поведение объекта ТаблицаЗначений:

  1. Точность колонок таблицы. Если колонка добавлена методом НоваяКолонка("Скидка", "Число", 15, 2), платформа будет автоматически округлять любое записываемое значение до двух знаков после запятой. Промежуточные коэффициенты (доли, проценты) нельзя сохранять в колонки с точностью 2 знака — для них используйте локальные переменные модуля или создавайте служебные колонки с повышенной точностью (например, точность 5 или 6 знаков).
  2. Требование минимальной цены. Согласно законодательству и правилам формирования фискальных документов, итоговая цена товара после вычета скидки не может быть нулевой или отрицательной (минимальная допустимая стоимость единицы товара составляет 1 копейку). Поэтому величина МаксСкидка для каждой позиции должна заранее рассчитываться как СуммаСтроки - (Количество * 0.01).

Альтернативное решение: регистрация бонусов отдельным видом оплаты

Если бизнес-процессы компании и кассовое программное обеспечение позволяют закрывать чек несколькими типами оплат, распределения скидки по строкам можно полностью избежать. В этом случае списание бонусов оформляется не как ценовая скидка, а как встречное предоставление или безналичная оплата отдельной секцией (например, оплата бонусами или подарочным сертификатом). Чек пробивается по полным розничным ценам, а в фискальный регистратор передаются две строки оплаты: «Наличные/Карта» и «Бонусы». Это снимает проблему распределения копеек и гарантирует полное соответствие фискальным нормам.

← На главную