При разработке обработок выгрузки данных (например, реестров в банк) часто возникает ситуация, когда один документ содержит ссылки на другие документы. Типичный пример: Платежное поручение, в табличной части которого перечислено несколько документов Ведомость на выплату зарплаты. Основная сложность здесь заключается в том, чтобы корректно "провалиться" внутрь каждой ведомости и извлечь данные о сотрудниках, суммах и лицевых счетах.
В этой статье мы подробно разберем, как правильно организовать обход вложенных табличных частей, какие ошибки чаще всего допускают разработчики и как оптимизировать этот процесс для повышения производительности системы.
Рассмотрим ситуацию, представленную в вопросе. Разработчик пытается использовать вложенные циклы для обхода данных. Ошибка возникает в строке Для Каждого СтрокаВедомости Из СтрокаВед1 Цикл. Проблема заключается в использовании неопределенной переменной СтрокаВед1. В контексте кода 1С, если мы находимся внутри цикла по табличной части документа (например, по ведомостям в платежке), текущая строка цикла содержит ссылку на документ ведомости. Чтобы получить доступ к данным этой ведомости, нужно обратиться к ее метаданным через эту ссылку.
Давайте проанализируем структуру типичного документа «Платежное поручение» в зарплатных конфигурациях. Обычно оно содержит табличную часть (например, ВыплатаПоВедомостям или просто Ведомости), где есть реквизит Ведомость. Сама же ведомость имеет свою табличную часть — например, Зарплата или Состав.
Для исправления кода нам необходимо заменить некорректную переменную на правильный путь к данным. Путь будет выглядеть так: ТекущаяСтрокаПлатежки.Ведомость.ИмяТабличнойЧастиВедомости. Рассмотрим исправленный пример кода, который позволит корректно выгрузить данные:
Для Каждого СтрокаСПлатежкой Из МассивДокументов Цикл
// Получаем объект платежки (хотя для чтения данных лучше использовать ссылку)
Платежка = СтрокаСПлатежкой.ПолучитьОбъект();
ДатаПлатежки = Формат(Платежка.Дата, "ДФ=dd.MM.yyyy");
НомерПлатежки = СокрЛП(ПрефиксацияОбъектовКлиентСервер.НомерНаПечать(Платежка.Номер, Истина, Истина));
Текст.ДобавитьСтроку("Д;ООО ""ТАИФ-СТ"";" + ДатаПлатежки + ";" + НомерПлатежки + ";");
Текст.ДобавитьСтроку("Зачисление денежных средств");
// Первый цикл: обходим ведомости, указанные в табличной части Платежного поручения
Для каждого СтрокаВед Из Платежка.Ведомости Цикл
// Обязательно проверяем, заполнена ли ссылка на ведомость
Если НЕ ЗначениеЗаполнено(СтрокаВед.Ведомость) Тогда
Продолжить;
КонецЕсли;
// Второй цикл: обращаемся к ТЧ "Зарплата" конкретной ведомости через точку от ссылки
// В типовых ЗУП 3.1 ТЧ ведомости называется "Зарплата"
Для каждого СтрокаЗарплаты Из СтрокаВед.Ведомость.Зарплата Цикл
Работник = СтрокаЗарплаты.Сотрудник;
Сумма = СтрокаЗарплаты.КВыплате;
// Получаем номер лицевого счета
// Внимание: в некоторых конфигурациях он может храниться не в ТЧ, а в регистре
ЛицевойСчет = СтрокаЗарплаты.НомерЛицевогоСчета;
Если ПустаяСтрока(ЛицевойСчет) Тогда
Сообщить("Перечисление (" + Сумма + " руб.) сотруднику " + Работник + " не может быть произведено: отсутствует лицевой счет!", СтатусСообщения.Важное);
Продолжить;
КонецЕсли;
Текст.ДобавитьСтроку(ЛицевойСчет + ";" + Формат(Сумма, "ЧРД=.;ЧДЦ=2;ЧГ=0") + ";" + Строка(Работник));
КонецЦикла;
КонецЦикла;
КонецЦикла;
Одной из главных трудностей является определение точных имен объектов в конкретной конфигурации (ЗУП, ERP или БП). Чтобы не гадать, рекомендуем использовать следующие методы:
СтрокаВед. Разверните структуру и посмотрите, как называется реквизит, содержащий ссылку на документ (например, Ведомость). Затем в табло введите СтрокаВед.Ведомость и посмотрите список доступных табличных частей этого объекта.Зарплата).Хотя предложенный выше вариант с вложенными циклами является рабочим, он не идеален с точки зрения производительности. Рассмотрим, как сделать ваш код профессиональнее.
1. Избегайте ПолучитьОбъект() для чтения
В исходном коде используется Платежка.ПолучитьОбъект(). Это "тяжелая" операция, которая считывает все данные документа из базы в память. Если вам нужно только прочитать данные для выгрузки, работайте напрямую со ссылкой. Обращение к табличной части через точку от ссылки (ПлатежкаСсылка.Ведомости) работает быстрее.
2. Проблема N+1 запросов
Когда вы используете вложенный цикл типа Для каждого СтрокаЗарплаты Из СтрокаВед.Ведомость.Зарплата, платформа 1С выполняет отдельный запрос к базе данных для каждой ведомости, чтобы получить её табличную часть. Если в платежке 100 ведомостей, будет сделано 100 лишних запросов. На больших объемах данных это приведет к значительным задержкам.
3. Использование Запроса (Рекомендуемый способ)
Самый правильный способ — получить все данные одним запросом. Это позволит мгновенно выгрузить даже реестры на тысячи сотрудников. Проанализируем структуру такого запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПлатежкаВедомости.Ведомость КАК Ведомость,
| ВедомостьЗарплата.Сотрудник КАК Сотрудник,
| ВедомостьЗарплата.КВыплате КАК Сумма,
| ВедомостьЗарплата.НомерЛицевогоСчета КАК ЛицевойСчет
|ИЗ
| Документ.ПлатежноеПоручение.Ведомости КАК ПлатежкаВедомости
| ЛЕВОЕ СОЕДИНЕНИЕ Документ.ВедомостьНаВыплатуЗарплатыВБанк.Зарплата КАК ВедомостьЗарплата
| ПО ПлатежкаВедомости.Ведомость = ВедомостьЗарплата.Ссылка
|ГДЕ
| ПлатежкаВедомости.Ссылка = &СсылкаНаПлатежку";
Запрос.УстановитьПараметр("СсылкаНаПлатежку", СсылкаНаПлатежку);
РезультатЗапроса = Запрос.Выполнить();
С помощью такого подхода вы получаете плоскую таблицу данных, которую остается только обойти одним циклом и записать в текстовый файл.
Проанализируем ситуацию с лицевыми счетами. В современных конфигурациях (например, 1С:ЗУП 3.1) номер счета может отсутствовать непосредственно в табличной части документа. Он часто хранится в регистре сведений ЛицевыеСчетаСотрудниковПоЗарплатнымПроектам. Если в вашем случае ЛицевойСчет оказывается неопределенным, необходимо использовать программный интерфейс конфигурации или прямой запрос к регистру по сотруднику и организации.
Подведем итог: чтобы правильно получить табличную часть вложенного документа, нужно обращаться через реквизит-ссылку текущей строки основной табличной части. Однако для разработки надежных и быстрых решений всегда отдавайте предпочтение запросам, а не вложенным циклам.