Как программно пересчитать суммы и НДС в табличной части документа в 1С:Управление торговлей 11?

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

При программном создании или заполнении документов в 1С разработчики нередко сталкиваются с ситуацией: документ создается на основании другого объекта (например, заказа поставщику), после чего в строках табличной части программно меняется количество. Однако зависимые реквизиты — Сумма, СуммаНДС, СуммаСНДС и суммы взаиморасчетов — автоматически не пересчитываются. Разберем, почему это происходит и какими способами можно выполнить корректный пересчет строк на сервере.

Почему не происходит автоматический пересчет?

Проанализируем архитектурную особенность платформы 1С:Предприятие. Все формулы расчета строк, привязанные к событиям элементов интерфейса (например, процедура ПриИзменении для поля количества), функционируют исключительно в контексте управляемой формы на клиенте. Когда мы работаем с объектом напрямую на сервере через Документы.ПоступлениеТоваровУслуг.СоздатьДокумент(), форма не открывается, события интерфейса не инициируются, а встроенные вычисляемые поля не обновляются сами по себе. Всю математику зависимых колонок необходимо запускать явно из программного кода.

Способ 1. Использование типового механизма ОбработкаТабличнойЧастиСервер (рекомендуемый для УТ 11, КА, ERP)

В типовых конфигурациях на базе библиотеки стандартных подсистем и современных редакций (в частности, в 1С:Управление торговлей 11) существует серверный модуль ОбработкаТабличнойЧастиСервер. В нем реализована универсальная экспортная процедура ОбработатьСтрокуТЧ, которая выполняет пересчеты зависимых колонок на основе переданной структуры действий.

Посмотрим на пример практической реализации при пакетной обработке строк документа:


// Создаем или получаем объект документа
ДокПоступление = Документы.ПриобретениеТоваровУслуг.СоздатьДокумент();
ДокПоступление.Заполнить(ВыбЗаказПоставщику);

// Инициализируем кэш перед циклом для оптимизации вызовов
КэшированныеЗначения = Неопределено;

// Формируем структуру параметров расчета сумм и НДС
СтруктураПересчетаСуммы = Новый Структура;
СтруктураПересчетаСуммы.Вставить("ЦенаВключаетНДС", ДокПоступление.ЦенаВключаетНДС);
СтруктураПересчетаСуммы.Вставить("НДСпоСтавкам4и2", ДокПоступление.Контрагент.НДСпоСтавкам4и2);

// Описываем перечень действий для обработки строки
СтруктураДействий = Новый Структура;
СтруктураДействий.Вставить("ПересчитатьКоличествоЕдиниц");
СтруктураДействий.Вставить("ПересчитатьСуммуНДС", СтруктураПересчетаСуммы);
СтруктураДействий.Вставить("ПересчитатьСуммуСНДС", СтруктураПересчетаСуммы);
СтруктураДействий.Вставить("ПересчитатьСумму");
СтруктураДействий.Вставить("ПересчитатьСуммуСУчетомРучнойСкидки", Новый Структура("Очищать", Ложь));
СтруктураДействий.Вставить("ОчиститьСуммуВзаиморасчетов");
СтруктураДействий.Вставить("ПересчитатьСуммуСверхЗаказа", Новый Структура("РеализацияПоступлениеПоЗаказу, ТребуетсяЗалогЗаТару", ДокПоступление.ПоступлениеПоЗаказам, ДокПоступление.ТребуетсяЗалогЗаТару));

// Обходим строки табличной части и применяем пересчет
Для Каждого СтрокаТЧ Из ДокПоступление.Товары Цикл
    // Предположим, новое количество получено из внешней таблицы
    СтрокаТЧ.КоличествоУпаковок = НовоеКоличество;
    
    // Вызываем типовой пересчет строки
    ОбработкаТабличнойЧастиСервер.ОбработатьСтрокуТЧ(СтрокаТЧ, СтруктураДействий, КэшированныеЗначения);
КонецЦикла;

Разберем важные детали этого алгоритма:

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

Способ 2. Прямой пересчет значений вручную

Если требуется максимальное быстродействие или обработка пишется для конфигураций старых редакций (например, УТ 10.3 или УПП 1.3), вызов тяжелых сервисных механизмов может быть избыточным. В таких случаях расчет прописывают вручную математически либо с помощью общих процедур расчета сумм.

Для старых конфигураций часто использовались вызовы модуля ОбработкаТабличныхЧастей:


ОбработкаТабличныхЧастей.РассчитатьСуммуТабЧасти(СтрокаТабличнойЧасти, ЭтотОбъект);
ОбработкаТабличныхЧастей.РассчитатьСуммуНДСТабЧасти(СтрокаТабличнойЧасти, ЭтотОбъект);

Если же мы рассчитываем значения напрямую в коде без внешних библиотек, необходимо учитывать признак включения НДС в стоимость:


СтрокаТЧ.Сумма = СтрокаТЧ.КоличествоУпаковок * СтрокаТЧ.Цена;

Если ДокПоступление.ЦенаВключаетНДС Тогда
    // Расчет НДС "в том числе"
    СтрокаТЧ.СуммаСНДС = СтрокаТЧ.Сумма;
    СтрокаТЧ.СуммаНДС  = СтрокаТЧ.СуммаСНДС * СтрокаТЧ.СтавкаНДС.Ставка / (100 + СтрокаТЧ.СтавкаНДС.Ставка);
Иначе
    // Расчет НДС "сверху"
    СтрокаТЧ.СуммаНДС  = СтрокаТЧ.Сумма * СтрокаТЧ.СтавкаНДС.Ставка / 100;
    СтрокаТЧ.СуммаСНДС = СтрокаТЧ.Сумма + СтрокаТЧ.СуммаНДС;
КонецЕсли;

Подводные камни и частые ошибки при программном заполнении

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

  1. Поиск строк методом ТЧ.Найти(): Метод Найти() возвращает только первое найденное вхождение. Если в документе одна и та же позиция разбита на несколько строк (по разным складам, характеристикам, сериям или датам поступления), метод вернет лишь первую попавшуюся строку. В таких задачах надежнее сопоставлять строки по исходному номеру строки, уникальному идентификатору строки связи или использовать запрос к табличной части.
  2. Поиск номенклатуры в цикле: Вызов Справочники.Номенклатура.НайтиПоКоду() внутри цикла обработки строк выполняет отдельный запрос к базе данных на каждой итерации. При загрузке сотен или тысяч позиций это приведет к серьезным задержкам. Рекомендуется заранее прочитать идентификаторы или сопоставить загружаемую таблицу с базой через единый запрос.
  3. Итоговые суммы в шапке документа: Процедура ОбработатьСтрокуТЧ модифицирует только строки табличной части. Если в конфигурации предусмотрены реквизиты итогов в шапке (например, СуммаДокумента), их необходимо пересчитать отдельно, например: ДокПоступление.СуммаДокумента = ДокПоступление.Товары.Итог("СуммаСНДС");
  4. Очистка взаиморасчетов: Действие ОчиститьСуммуВзаиморасчетов обнуляет реквизит строки. Если валюта взаиморасчетов по договору отличается от валюты самого документа, перед проведением потребуется заполнить эквивалентную сумму в валюте взаиморасчетов по актуальному курсу.
← На главную