Как получить данные табличной части документа, который указан в табличной части другого документа?

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

В практике программирования на платформе 1С:Предприятие 8 часто возникает задача получения данных из «вложенных» структур. Типичный пример: у нас есть документ «Платежное поручение», в табличной части которого перечислены ссылки на документы «Ведомость в банк». Нам необходимо выгрузить данные о сотрудниках и суммах, которые находятся уже внутри каждой такой ведомости (в её собственной табличной части). Разберем, как это сделать правильно, избегая распространенных ошибок производительности и синтаксиса.

Разбор типичных ошибок при работе с вложенными данными

Проанализируем ситуацию, с которой сталкиваются начинающие разработчики. Основная сложность заключается в непонимании того, к какому именно объекту мы обращаемся в цикле. Рассмотрим фрагмент кода, который вызывает ошибку:


Для Каждого Строка1 из Платежка.Ведомости Цикл
    // Ошибка: Строка1 — это строка текущей табличной части, 
    // а не сам документ ведомость!
    Для Каждого СтрокаВедомости Из Строка1 Цикл 
        Работник = СтрокаВедомости.Сотрудник;
    КонецЦикла;
КонецЦикла;

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

Еще одна критическая ошибка — использование метода ПолучитьОбъект() внутри циклов. Рассмотрим, почему это плохо:

  1. Избыточность: Метод ПолучитьОбъект() считывает из базы данных абсолютно все реквизиты документа и его табличные части, подготавливая их для записи. Если вам нужно только прочитать данные, достаточно использовать Ссылку.
  2. Производительность: Если в платежке 10 ведомостей, вы 10 раз заставите сервер приложений создавать тяжелый объект в памяти. Это замедляет работу системы.

Правильный способ получения данных через вложенный цикл

Рассмотрим, как исправить код, чтобы он работал корректно. Выясним причину ошибки: нам нужно обратиться к конкретному полю в строке табличной части, которое содержит ссылку на документ.

Предположим, в документе «Платежное поручение» есть табличная часть Ведомости, а в ней колонка Ведомость (тип — Ссылка на документ «Ведомость в банк»). У ведомости, в свою очередь, есть табличная часть Зарплата.

Правильный алгоритм действий будет выглядеть так:


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

Важный нюанс: Обращение через точку вида ТекущаяВедомость.Зарплата заставляет платформу неявно выполнить запрос к базе данных. Это допустимо, если документов немного, но является плохим тоном при обработке больших массивов данных.

Оптимальное решение: Использование запросов

Проанализируем ситуацию с точки зрения производительности. Профессиональный подход в 1С — это использование языка запросов. Вместо того чтобы в цикле «дергать» базу данных для каждой ведомости, мы можем получить все данные одним махом.

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ПлатежкаВедомости.Ведомость КАК СсылкаНаВедомость,
    |	ВедомостьЗарплата.Сотрудник КАК Работник,
    |	ВедомостьЗарплата.КВыплате КАК Сумма,
    |	ВедомостьЗарплата.НомерЛицевогоСчета КАК ЛицевойСчет
    |ИЗ
    |	Документ.ПлатежноеПоручение.Ведомости КАК ПлатежкаВедомости
    |		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ВедомостьНаВыплатуЗарплаты.Зарплата КАК ВедомостьЗарплата
    |		ПО ПлатежкаВедомости.Ведомость = ВедомостьЗарплата.Ссылка
    |ГДЕ
    |	ПлатежкаВедомости.Ссылка = &СсылкаНаПлатежку";

Запрос.УстановитьПараметр("СсылкаНаПлатежку", ПлатежкаСсылка);

РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();

Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
    // Здесь мы сразу получаем готовые данные по сотрудникам всех ведомостей
    Сообщить("Сотрудник: " + ВыборкаДетальныеЗаписи.Работник + " Сумма: " + ВыборкаДетальныеЗаписи.Сумма);
КонецЦикла;

Почему этот способ лучше? Мы делаем всего одно обращение к серверу базы данных. Платформа самостоятельно выполнит соединение таблиц на уровне SQL, что в десятки раз быстрее, чем перебор в циклах на языке 1С.

Рекомендации по отладке

Если вы не уверены, как называется поле или какая структура у объекта в памяти, воспользуйтесь инструментом Отладка. Разберем по шагам:

  1. Установите точку останова (F9) на строке цикла Для Каждого.
  2. Когда выполнение программы прервется, выделите переменную Строка1 (или как вы назвали переменную цикла) и нажмите Shift+F9.
  3. В окне «Вычислить выражение» вы увидите все колонки вашей табличной части. Найдите ту, где тип значения — ДокументСсылка....
  4. Разверните это значение, и вы увидите структуру самого документа, включая его табличные части.

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

← На главную