Как универсально передать табличную часть документа в общую процедуру для обработки

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Разберем подробно, как можно изящно решить эту задачу, избегая «спагетти-кода» и упрощая дальнейшую поддержку системы. Рассмотрим несколько подходов: от простых до архитектурно правильных.

Способ 1. Нормализация данных через таблицу значений

Этот метод заключается в том, что каждый документ перед вызовом общей процедуры самостоятельно подготавливает данные и приводит их к единому стандарту. Проанализируем, как это реализовать. В модуле объекта каждого документа мы создаем таблицу значений с жестко заданными именами колонок, которые ожидает общая процедура.

Рассмотрим пример кода в модуле объекта документа:


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

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

Способ 2. Использование структуры-описателя соответствия колонок

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

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

Пример вызова:


ПараметрыСопоставления = Новый Структура;
ПараметрыСопоставления.Вставить("Товар", "Номенклатура");
ПараметрыСопоставления.Вставить("Сумма", "Всего");

МойОбщийМодуль.УниверсальнаяОбработка(ЭтотОбъект.Товары, ПараметрыСопоставления);

А внутри общего модуля работаем следующим образом:


Процедура УниверсальнаяОбработка(ТабличнаяЧасть, Сопоставление) Экспорт
    Для Каждого Строка Из ТабличнаяЧасть Цикл
        ТекущийТовар = Строка[Сопоставление.Товар];
        ТекущаяСумма = Строка[Сопоставление.Сумма];
        
        // Далее выполняем общую логику
    КонецЦикла;
КонецПроцедуры

Этот метод экономит память, так как мы не создаем копии данных в виде ТаблицаЗначений.

Способ 3. Использование запросов для нормализации (Самый эффективный)

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

  1. Передаем табличную часть в запрос.
  2. В тексте запроса назначаем псевдонимы (например, ТЧ.ЦенаПродажи КАК Цена).
  3. Выгружаем результат запроса в таблицу значений.

Пример реализации в общем модуле:


Функция ПодготовитьДанныеЗапросом(ДокументОбъект, ИмяТЧ, ИмяКолонкиЦены) Экспорт
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |   ВТ.Номенклатура КАК Номенклатура,
    |   ВТ.Количество КАК Количество,
    |   ВТ." + ИмяКолонкиЦены + " КАК Цена
    |ПОМЕСТИТЬ ВременнаяТаблица
    |ИЗ
    |   &ТЧ КАК ВТ;
    |ВЫБРАТЬ * ИЗ ВременнаяТаблица";
    
    Запрос.УстановитьПараметр("ТЧ", ДокументОбъект[ИмяТЧ].Выгрузить());
    
    Возврат Запрос.Выполнить().Выгрузить();
КонецФункции

Важно: Метод Выгрузить() у табличной части позволяет передать список конкретных колонок, что еще сильнее оптимизирует работу с памятью: Объект.Товары.Выгрузить(, "Номенклатура, Количество, Цена").

Способ 4. Вынос логики в Модуль Менеджера

Для соблюдения принципов объектно-ориентированного программирования (инкапсуляции) в 1С удобно использовать модули менеджеров. Посмотрим на этот паттерн. Вместо того чтобы общий модуль пытался «угадать», как работать с документом, мы просим сам документ подготовить данные.

В модуле менеджера каждого документа создаем экспортную функцию:


Функция ПолучитьДанныеДляОбщейОбработки(ДокументОбъект) Экспорт
    Результат = Новый ТаблицаЗначений;
    // ... логика заполнения ...
    Возврат Результат;
КонецФункции

В общем модуле вызов будет выглядеть универсально:


ИмяМенеджера = ДокументСсылка.Метаданные().Имя;
Данные = Документы[ИмяМенеджера].ПолучитьДанныеДляОбщейОбработки(ДокументОбъект);

Способ 5. Использование ДополнительныеСвойства

Если вы работаете в конфигурации на базе БСП (Библиотека стандартных подсистем), можно использовать встроенную структуру ДополнительныеСвойства, которая есть у любого объекта документа. Рассмотрим этот вариант. Перед вызовом обработки мы помещаем в эту структуру нужные нам данные (например, уже нормализованную ТЗ). Это позволяет не менять количество параметров в процедурах общего модуля при добавлении новой функциональности.

Резюме:

Для большинства задач наиболее «чистым» решением является Способ 2 (Структура-описатель), так как он не дублирует данные в памяти и сохраняет гибкость. Если же требуется высокая производительность и сложная фильтрация данных (например, исключение строк с нулевой ценой), лучше всего подойдет Способ 3 (Запросы). Выбор метода зависит от объема данных и требований к чистоте архитектуры вашей конфигурации.

← На главную