Как в 1С заменить нулевую цену на 1 рубль и скорректировать общую сумму документа?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При загрузке заказов клиентов или накладных из внешних файлов (например, Excel) нередко возникают ситуации, когда в табличную часть попадают позиции с нулевой ценой (подарки, сувениры, рекламная продукция или бонусы). Однако проведение документа с нулевой стоимостью строки часто блокируется типовыми проверками конфигурации, либо вызывает ошибки при фискализации чека на ККТ (драйверы касс по 54-ФЗ не позволяют регистрировать нулевые позиции). В связи с этим возникает задача: установить для таких позиций минимальную цену в 1 рубль, а общую сумму документа уменьшить на соответствующую величину за счет других позиций, чтобы итоговый взаиморасчет с клиентом остался неизменным.

Разберем основные риски, математические нюансы и программные алгоритмы решения этой задачи в 1С.

Важные нюансы при программной корректировке табличной части

Прежде чем переходить к алгоритмам, проанализируем ключевые сложности, возникающие при ручном изменении цен и сумм:

  1. Проблема кратности количества товара. Если мы уменьшаем значение реквизита Цена на 1 рубль в строке, где Количество = 5, то сумма строки уменьшится на 5 рублей (Количество * Цена), а не на 1 рубль. Корректировать цены напрямую можно только в строках с единичным количеством, либо необходимо оперировать реквизитом Сумма.
  2. Погрешность округления. При любых делениях, процентных пересчетах и коэффициентах возникают копейки, которые в сумме строк табличной части могут дать расхождение с итогом документа. Эту разницу необходимо принудительно относить на одну из строк.
  3. Связанные реквизиты. При изменении цены или суммы вручную необходимо вызывать процедуры пересчета сопутствующих полей: СуммаНДС, СуммаСНДС, СуммаРучнойСкидки, Всего.

Вариант 1. Последовательное уменьшение цены в два прохода (простой алгоритм)

Рассмотрим прямолинейный подход: сначала подсчитаем количество строк с нулевой ценой и установим им цену 1, а затем последовательно обойдем остальные строки документа и снимем накопленную разницу с первых подходящих позиций.

Посмотрим на пример реализации алгоритма:


// Шаг 1. Находим нулевые строки и задаем им цену 1
СчетчикКоррекции = 0;
Для Каждого Стр Из Заказ.Товары Цикл
    Если Стр.Цена = 0 Тогда
        СчетчикКоррекции = СчетчикКоррекции + 1;
        Стр.Цена = 1;
        Стр.Сумма = Стр.Цена * Стр.Количество;
    КонецЕсли;
КонецЦикла;

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

Ограничение метода: этот алгоритм корректно работает в случаях, когда количество в строках равно 1. Если количество больше единицы, правильнее оперировать суммами строк и скидками.

Вариант 2. Пропорциональное распределение суммы с компенсацией округления (рекомендуемый)

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

Рассмотрим код заполнения и пересчета табличной части:


СуммаКоррекции = 0;

// Обход выборки или строк внешнего файла
Для Каждого Стр Из ДанныеЗагрузки Цикл
    НовСтрока = Заказ.Товары.Добавить();
    НовСтрока.Номенклатура = Стр.Номенклатура;
    НовСтрока.Количество = Стр.Количество;
    НовСтрока.КоличествоУпаковок = Стр.Количество;
    НовСтрока.СтавкаНДС = Перечисления.СтавкиНДС.БезНДС;
    
    Если Стр.Цена = 0 Тогда
        // Устанавливаем минимальную стоимость
        НовСтрока.Цена = 1;
        НовСтрока.Сумма = НовСтрока.Цена * НовСтрока.Количество;
        СуммаКоррекции = СуммаКоррекции + НовСтрока.Сумма;
    Иначе
        НовСтрока.Цена = Стр.Цена;
        НовСтрока.СуммаРучнойСкидки = Стр.СуммаСкидки;
        НовСтрока.Сумма = Стр.Сумма;
    КонецЕсли;
КонецЦикла;

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

КонецЕсли;

Альтернативные и штатные механизмы 1С

Проанализируем другие способы отражения бесплатных товаров в типовых конфигурациях 1С:

  1. Использование ручной скидки 100%. Если конфигурация и драйвер торгового оборудования допускают скидку на строку, позиция заводится со своей реальной базовой ценой (например, 100 руб.), но в колонку СуммаРучнойСкидки или ПроцентРучнойСкидки заносится 100%. В результате сумма позиции равна 0, но базовая цена сохранена.
  2. Подсистема автоматических скидок («Выдать подарок»). В конфигурациях «1С:Управление торговлей», «1С:Комплексная автоматизация» и «1С:ERP» заложен штатный механизм подарков. При выполнении условий бонусный товар добавляется в табличную часть, а его стоимость автоматически «размазывается» в виде скидки на остальные платные позиции документа.
  3. Настройка прав пользователей. В ряде конфигураций запрет на проведение документов с нулевыми ценами регулируется функциональной опцией или дополнительными правами пользователя («Разрешить нулевые цены в оптовой торговле»).
  4. Списание на расходы. Если передаваемый товар является сувенирной или рекламной продукцией (календари, ручки, промо-материалы), корректнее не включать его в документ продажи клиенту, а списывать отдельным документом Внутреннее потребление товаров (списание на расходы/рекламу). Это исключает налоговые риски, связанные с безвозмездной передачей по НДС.
← На главную