Разработчики, начинающие работать с крупными типовыми конфигурациями компании «1С», такими как 1С:ERP Управление предприятием, Комплексная автоматизация (КА) или Управление торговлей (УТ 11), часто испытывают легкий шок при попытке программно изменить или пересчитать строки табличной части (ТЧ). Вместо простого и понятного кода их встречает каскад вызовов из различных общих модулей, глубина которых может достигать десятков уровней.
В этой статье мы подробно разберем, как устроен этот механизм изнутри, почему разработчики «1С» отказались от классических обработчиков событий в пользу декларативного подхода, и как правильно использовать универсальные функции пересчета при программном создании или изменении документов на сервере и клиенте.
В обычных формах и старых конфигурациях пересчет сумм выполнялся прямолинейно: при изменении номенклатуры вызывалась процедура, которая жестко перезаписывала цену, ставку НДС, затем рассчитывала сумму и сумму НДС. В управляемом интерфейсе ERP/УТ/КА этот подход заменили на паттерн «Структура действий».
Суть паттерна заключается в том, что вместо непосредственного вызова процедур расчета мы формируем структуру (коллекцию «Ключ и Значение»), где ключами выступают строковые идентификаторы необходимых технологических операций (команд), а значениями — параметры для выполнения этих операций. Затем эта структура передается в универсальный интерпретатор.
Рассмотрим, для чего это было сделано. Платформа 1С:Предприятие не поддерживает классическое переопределение и перегрузку методов (полиморфизм в ООП) на уровне встроенного языка. Использование структуры действий позволяет динамически определять состав и последовательность расчетов в зависимости от контекста документа и настроек системы без дублирования громоздких ветвлений Если... ИначеЕсли.
Проанализируем типовой пример интерактивного изменения строки табличной части «Товары». При изменении номенклатуры или количества система должна каскадно рассчитать множество зависимых реквизитов. Вызов выглядит следующим образом:
// Пример интерактивной обработки строки ТЧ на стороне тонкого клиента
Процедура ТоварыНоменклатураПриИзмененииВопросПользователюЗавершение(Результат, ДополнительныеПараметры) Экспорт
ТекущаяСтрока = ДополнительныеПараметры.ТекущаяСтрока;
// Получаем базовую структуру для расчета сумм и НДС
СтруктураПересчетаСуммы = ОбработкаТабличнойЧастиКлиентСервер.ПолучитьСтруктуруПересчетаСуммыНДСВСтрокеТЧ(Объект);
// Формируем декларативный список задач для пересчета
СтруктураДействий = Новый Структура;
СтруктураДействий.Вставить("ПроверитьХарактеристикуПоВладельцу", ТекущаяСтрока.Характеристика);
СтруктураДействий.Вставить("ПроверитьЗаполнитьУпаковкуПоВладельцу", ТекущаяСтрока.Упаковка);
СтруктураДействий.Вставить("ПересчитатьКоличествоЕдиниц");
Если ИспользоватьСоглашенияСКлиентами И ЗначениеЗаполнено(Объект.Соглашение) Тогда
СтруктураДействий.Вставить("ЗаполнитьУсловияПродаж", ОбработкаТабличнойЧастиКлиентСервер.ПолучитьСтруктуруЗаполненияУсловийПродажВСтрокеТЧ(Объект));
Иначе
СтруктураДействий.Вставить("ЗаполнитьЦенуПродажи", ОбработкаТабличнойЧастиКлиентСервер.ПолучитьСтруктуруЗаполненияЦеныВСтрокеТЧ(Объект));
КонецЕсли;
СтруктураДействий.Вставить("ЗаполнитьСтавкуНДС", Объект.НалогообложениеНДС);
СтруктураДействий.Вставить("ПересчитатьСуммуНДС", СтруктураПересчетаСуммы);
СтруктураДействий.Вставить("ПересчитатьСуммуСНДС", СтруктураПересчетаСуммы);
СтруктураДействий.Вставить("ПересчитатьСумму");
// Передаем строку, структуру действий и кэш в универсальный клиентский обработчик
ОбработкаТабличнойЧастиКлиент.ОбработатьСтрокуТЧ(ТекущаяСтрока, СтруктураДействий, КэшированныеЗначения);
КонецПроцедуры
В данном коде процедура ОбработатьСтрокуТЧ выступает в роли диспетчера. Она анализирует переданную СтруктуруДействий и последовательно запускает внутренние алгоритмы. Это избавляет программиста от написания кастомного кода расчетов для каждого реквизита, гарантируя, что все связанные суммы обновятся корректно. Для адаптации типовой логики под специфику учета можно применить расширение для программной работы с упаковками и единицами измерения.
Основная сложность возникает тогда, когда задачу нужно решить программно вне контекста формы — например, в фоновом задании, при импорте данных из Excel или XML-файлов, либо при групповой обработке документов. Использовать клиентский модуль ОбработкаТабличнойЧастиКлиент на сервере невозможно.
Для серверных расчетов в ERP и смежных конфигурациях предусмотрен модуль ОбработкаТабличнойЧастиСервер и его метод ОбработатьТЧ(). Выясним, как правильно составить программный код для массового пересчета всей таблицы без открытия формы.
Разберем по шагам серверный сценарий пересчета сумм и НДС для документа:
// Функция пересчета всей табличной части на стороне сервера
Процедура ПересчитатьТабличнуюЧастьНаСервере(ДокументОбъект) Экспорт
// Шаг 1. Получаем параметры пересчета сумм из метаданных документа
ПараметрыПересчета = ОбработкаТабличнойЧастиКлиентСервер.ПараметрыПересчетаСуммыНДСВТЧ(ДокументОбъект);
// Шаг 2. Конструируем структуру действий для сервера
СтруктураДействий = Новый Структура;
СтруктураДействий.Вставить("ПересчитатьСуммуНДС", ПараметрыПересчета);
СтруктураДействий.Вставить("ПересчитатьСуммуСНДС", ПараметрыПересчета);
СтруктураДействий.Вставить("ПересчитатьСумму");
// Шаг 3. Инициализируем кэш (КРИТИЧЕСКИ ВАЖНО для производительности!)
КэшЗначений = ОбработкаТабличнойЧастиКлиентСервер.ПолучитьСтруктуруКэшируемыеЗначения();
// Шаг 4. Вызываем универсальную обработку всей таблицы
ОбработкаТабличнойЧастиСервер.ОбработатьТЧ(ДокументОбъект.Товары, СтруктураДействий, КэшЗначений);
КонецПроцедуры
Этот способ позволяет одной командой обновить все расчетные показатели во всех строках документа. Метод ОбработатьТЧ самостоятельно обойдет коллекцию строк и применит к ним правила, описанные в структуре действий.
Рассмотрим детальнее параметр КэшированныеЗначения (или КэшЗначений). Игнорирование этого механизма — самая частая ошибка начинающих разработчиков, приводящая к фатальному падению производительности систем автоматизации.
Когда вы выполняете пересчет строки, универсальный механизм вынужден обращаться к базе данных, чтобы прочитать свойства номенклатуры (например, базовую ставку НДС, единицу измерения, текстовое описание). Если в вашей табличной части содержится 500 строк, и вы не передадите структуру кэша, система выполнит 500 отдельных SQL-запросов к СУБД для получения одних и тех же данных.
Передавая инициализированную структуру КэшированныеЗначения в качестве третьего параметра в методы ОбработатьСтрокуТЧ или ОбработатьТЧ, вы заставляете платформу кэшировать прочитанные данные в оперативной памяти. Повторные запросы к СУДБ для одинаковых товаров производиться не будут. Это ускоряет работу алгоритма в десятки раз.
Посмотрим на пример построчного обхода на сервере с правильным использованием кэша, когда нам требуется не просто пересчитать суммы, а последовательно обновить ставки НДС на основе реквизитов шапки документа:
// Построчный обход табличной части с использованием кэширования
Процедура ОбновитьСтавкиИПересчитатьТЧ(ДокументОбъект) Экспорт
// Создаем единый кэш перед началом цикла
КэшЗначений = ОбработкаТабличнойЧастиКлиентСервер.ПолучитьСтруктуруКэшируемыеЗначения();
ПараметрыПересчета = ОбработкаТабличнойЧастиКлиентСервер.ПараметрыПересчетаСуммыНДСВТЧ(ДокументОбъект);
СтруктураДействий = Новый Структура;
СтруктураДействий.Вставить("ЗаполнитьСтавкуНДС", ДокументОбъект.НалогообложениеНДС);
СтруктураДействий.Вставить("ПересчитатьСуммуНДС", ПараметрыПересчета);
СтруктураДействий.Вставить("ПересчитатьСуммуСНДС", ПараметрыПересчета);
СтруктураДействий.Вставить("ПересчитатьСумму");
Для Каждого СтрокаТовары Из ДокументОбъект.Товары Цикл
// Передаем кэш аргументом, он наполняется и используется на каждой итерации
ОбработкаТабличнойЧастиСервер.ОбработатьСтрокуТЧ(СтрокаТовары, СтруктураДействий, КэшЗначений);
КонецЦикла;
КонецПроцедуры
Необходимо понимать, что универсальность всегда требует компромиссов. Даже при корректном использовании кэширования построчный вызов универсального пересчета для документов с экстремально большим объемом данных (более 10 000 строк) может работать медленно.
Если перед вами стоит задача регулярной загрузки сотен тысяч строк из внешних систем, стандартный универсальный механизм пересчета использовать не рекомендуется. В таких сценариях лучшим решением будет:
Однако для 95% повседневных задач автоматизации использования стандартных методов ОбработкаТабличнойЧастиСервер.ОбработатьТЧ со структурой действий более чем достаточно. Это гарантирует корректность расчетов с точки зрения вендора и облегчает последующую поддержку кода.