В практике разработки на платформе 1С:Предприятие часто возникает задача, когда один документ должен записывать данные в регистр, основываясь на информации сразу из нескольких табличных частей (ТЧ). Например, первая ТЧ содержит список объектов (основные средства, номенклатура), а вторая — аналитику (статьи расходов, подразделения, коэффициенты). Сложность заключается в том, что количество строк в этих таблицах может не совпадать, а явная связь между ними может отсутствовать.
В данной статье мы разберем основные подходы к решению этой задачи, проанализируем типичные ошибки и выясним, как правильно организовать алгоритм проведения документа в модуле объекта.
Рассмотрим ситуацию: у нас есть документ, в котором первая табличная часть ЗемельныеУчастки содержит перечень объектов, а вторая ОтражениеРасходов — способы распределения затрат по этим объектам. Нам необходимо записать эти данные в регистр сведений СпособыОтраженияРасходов.
Ключевые измерения регистра:
ОсновноеСредство (из первой ТЧ);Организация (из шапки документа);СтатьяРасходов (из второй ТЧ).Если в первой ТЧ у нас 5 строк, а во второй 3, возникает вопрос: сколько записей должно появиться в регистре? Ответ зависит от бизнес-логики, но чаще всего подразумевается «декартово произведение» (каждый объект из ТЧ1 должен получить все аналитики из ТЧ2).
Самый очевидный, но требующий осторожности метод — использование вложенного цикла в процедуре ОбработкаПроведения. Мы перебираем строки первой таблицы и для каждой строки запускаем цикл по второй таблице.
Разберем типичный пример реализации, в котором новички часто допускают ошибку:
Процедура ОбработкаПроведения(Отказ, Режим)
Движения.СпособыОтраженияРасходов.Записывать = Истина;
Для Каждого ТекСтрокаОбъект Из ЗемельныеУчастки Цикл
// Создаем новую запись для каждого объекта
Для Каждого ТекСтрокаАналитика Из ОтражениеРасходов Цикл
Движение = Движения.СпособыОтраженияРасходов.Добавить();
Движение.Период = Дата;
Движение.Организация = Организация;
Движение.ОсновноеСредство = ТекСтрокаОбъект.ЗемельныйУчасток;
// Заполняем ресурсы и измерения из второй ТЧ
Движение.СтатьяРасходов = ТекСтрокаАналитика.СтатьяРасходов;
Движение.АналитикаРасходов = ТекСтрокаАналитика.АналитикаРасходов;
КонецЦикла;
КонецЦикла;
КонецПроцедуры
Важное замечание: В сообщении №13 исходной темы автор допустил критическую ошибку — он вызывал Добавить() во внешнем цикле, а во внутреннем просто перезаписывал поля одной и той же записи. В итоге в регистр попадала только последняя строка из второй табличной части. Всегда вызывайте метод Добавить() внутри самого глубокого цикла, если вам нужны все комбинации строк.
Использование циклов оправдано только на очень малых объемах данных. Если в документе могут быть сотни строк, правильнее использовать запрос. Это позволит избежать избыточных обращений к памяти и ускорит запись движений.
Рассмотрим алгоритм формирования движений через временные таблицы:
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ
| ТЧ_Объекты.ЗемельныйУчасток КАК ОсновноеСредство
|ПОМЕСТИТЬ ВТ_Объекты
|ИЗ
| &ТЧ_Объекты КАК ТЧ_Объекты;
|
|ВЫБРАТЬ
| ТЧ_Расходы.СтатьяРасходов КАК СтатьяРасходов,
| ТЧ_Расходы.АналитикаРасходов КАК АналитикаРасходов
|ПОМЕСТИТЬ ВТ_Расходы
|ИЗ
| &ТЧ_Расходы КАК ТЧ_Расходы;
|
|ВЫБРАТЬ
| &Период КАК Период,
| &Организация КАК Организация,
| ВТ_Объекты.ОсновноеСредство,
| ВТ_Расходы.СтатьяРасходов,
| ВТ_Расходы.АналитикаРасходов
|ИЗ
| ВТ_Объекты КАК ВТ_Объекты
| СОЕДИНЕНИЕ ВТ_Расходы КАК ВТ_Расходы
| ПО (ИСТИНА)";
Запрос.УстановитьПараметр("ТЧ_Объекты", ЗемельныеУчастки.Выгрузить());
Запрос.УстановитьПараметр("ТЧ_Расходы", ОтражениеРасходов.Выгрузить());
Запрос.УстановитьПараметр("Период", Дата);
Запрос.УстановитьПараметр("Организация", Организация);
РезультатЗапроса = Запрос.Выполнить();
Движения.СпособыОтраженияРасходов.Загрузить(РезультатЗапроса.Выгрузить());
Этот метод не только быстрее, но и чище с точки зрения кода. Мы используем метод Загрузить(), который эффективно заполняет коллекцию движений.
При работе с регистрами сведений крайне важно помнить о ключах уникальности. Ключ записи формируется из всех ее измерений и периода (для периодических регистров). Если вы пытаетесь записать две строки, у которых одинаковый набор измерений (например, одно и то же ОсновноеСредство и СтатьяРасходов), система выдаст ошибку при записи набора.
Если ваша бизнес-логика предполагает, что в ТЧ2 могут быть разные аналитики для одной и той же статьи, то АналитикаРасходов также должна быть измерением регистра, а не ресурсом. В противном случае записи «схлопнутся» или вызовут ошибку «Запись с такими ключевыми полями существует».
Иногда строки в ТЧ1 и ТЧ2 не должны перемножаться «все со всеми». В сложных конфигурациях, таких как 1С:ERP или 1С:КА, используется механизм Ключей связи. Это числовой реквизит, присутствующий в обеих таблицах.
В этом случае при проведении мы соединяем таблицы не по условию ПО ИСТИНА, а по равенству ключей:
ПО ВТ_Объекты.КлючСвязи = ВТ_Расходы.КлючСвязи
Это позволяет жестко закрепить: за какие объекты отвечают конкретные строки расходов. Если в вашем документе такая связь отсутствует, но она необходима — стоит задуматься о добавлении такого реквизита.
Проанализируем ситуацию, описанную в финальном сообщении темы. Часто оказывается, что попытка запихнуть данные из разных ТЧ в один регистр — это ошибка проектирования. Если данные в табличных частях независимы (как в примере автора: в одной ТЧ кадастровые данные участков, в другой — счета учета), то правильнее будет:
Если же записи все-таки должны быть в одном регистре, используйте запрос с временными таблицами — это наиболее надежный и производительный способ работы с движениями в 1С.