Как вывести фиксированное количество строк табличной части в макет печатной формы 1С и исправить ошибку итератора?

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

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

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

В чем суть технической проблемы?

Рассмотрим сценарий: у нас есть документ (например, «Путевой лист») с табличной частью ЗаданиеВодителю. В макет печатной формы выводится лицевая сторона документа, на которой заранее нарисованы ровно 5 строк для отображения маршрута. В этих строках определены параметры СтрокаЗВ1, СтрокаЗВ2, СтрокаЗВ3, СтрокаЗВ4 и СтрокаЗВ5.

Нам необходимо последовательно прочитать первые 5 строк табличной части документа и заполнить эти параметры. Если строк в документе больше 5, мы должны проигнорировать оставшиеся (или вывести предупреждение), а если меньше — корректно очистить неиспользованные параметры, чтобы в готовой печатной форме пользователь не увидел сырые технические имена параметров вида <СтрокаЗВ4>.

Анализ ошибки «Итератор для значения не определен»

Для решения задачи разработчики часто пишут следующий запрос, выбирая табличную часть как вложенную таблицу:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ПутевойЛист.Ссылка КАК Ссылка,
    |	ПутевойЛист.ЗаданиеВодителю.(
    |		НомерСтроки,
    |		Место
    |	) КАК ЗаданиеВодителю
    |ИЗ
    |	Документ.ПутевойЛист КАК ПутевойЛист
    |ГДЕ
    |	ПутевойЛист.Ссылка = &Ссылка";

После получения выборки программист пытается обойти вложенную таблицу привычным циклом:


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

На этом этапе программа аварийно завершает работу, выдавая ошибку: «Итератор для значения не определен». Выясним причину этой ошибки.

Дело в том, что при выборке вложенной таблицы через запрос поле Выборка.ЗаданиеВодителю имеет специальный тип данных платформы — ВыборкаИзРезультатаЗапроса. Этот объект не является ни массивом, ни таблицей значений, ни какой-либо другой стандартной коллекцией, поддерживающей непосредственное перечисление элементов. Обходить объект типа ВыборкаИзРезультатаЗапроса через цикл Для каждого синтаксически запрещено. Для работы с ним необходимо использовать только последовательное считывание записей с помощью специального метода.

Решение проблемы обхода вложенной выборки

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


Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    
    // Получаем вложенную выборку для табличной части
    ВыборкаТЧ = Выборка.ЗаданиеВодителю.Выбрать();
    
    // Теперь обходим строки в цикле Пока...Следующий()
    Пока ВыборкаТЧ.Следующий() Цикл
        Сообщить(ВыборкаТЧ.Место);
    КонецЦикла;
    
КонецЦикла;

Этот подход является наиболее производительным с точки зрения работы СУБД, так как данные шапки документа и его табличной части извлекаются за один один запрос к базе данных без повторного обращения к Ссылке.

Решение задачи: Вывод фиксированных строк в макет

Теперь, когда мы научились правильно и без ошибок обходить вложенную выборку табличной части, реализуем алгоритм заполнения фиксированных параметров макета (СтрокаЗВ1 ... СтрокаЗВ5).

Для того чтобы код был лаконичным и нам не приходилось вручную писать сложные конструкции условий для каждого параметра, мы воспользуемся возможностью динамического обращения к свойствам структуры Параметры области макета. В платформе 1С к параметрам области можно обращаться как по имени точки (Область.Параметры.Имя), так и по индексу квадратных скобок (Область.Параметры["Имя"]).

Проанализируем пошагово правильный и безопасный алгоритм:

  1. Очистка параметров. Перед выводом данных обязательно очищаем все 5 параметров на макете пустой строкой. Это защитит форму от вывода пустых угловых скобок параметров, если в документе окажется меньше 5 строк.
  2. Ограничение по количеству. В цикле обхода ТЧ заводим счетчик. Если счетчик превышает максимальное число доступных строк на макете (в нашем случае 5), прерываем цикл.
  3. Динамическое заполнение. С помощью конструкции ОбластьЛицевая.Параметры["СтрокаЗВ" + Сч] = ВыборкаТЧ.Место; заполняем параметры макета.

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


// Шаг 1. Предварительная очистка всех фиксированных параметров на макете
Для Индекс = 1 По 5 Цикл
    ОбластьЛицевая.Параметры["СтрокаЗВ" + Индекс] = "";
КонецЦикла;

// Шаг 2. Инициализация вложенной выборки и счетчика строк
ВыборкаЗадания = Выборка.ЗаданиеВодителю.Выбрать();
Сч = 1;

// Шаг 3. Заполнение параметров реальными данными
Пока ВыборкаЗадания.Следующий() Цикл
    
    // Прерываем выполнение, если строк в документе больше, чем мест в макете
    Если Сч > 5 Тогда
        Прервать;
    КонецЕсли;
    
    // Динамически обращаемся к параметру макета по индексу строки
    ОбластьЛицевая.Параметры["СтрокаЗВ" + Сч] = ВыборкаЗадания.Место;
    
    // Увеличиваем счетчик для следующей итерации
    Сч = Сч + 1;
    
КонецЦикла;

Альтернативное решение: Работа с Таблицей Значений в пакетном запросе

Иногда программисту удобнее и привычнее работать с коллекцией ТаблицаЗначений и обходить ее стандартным циклом Для каждого. Это также позволяет легко использовать методы таблиц значений, такие как сортировка, поиск или свертка. Чтобы реализовать такой подход, мы можем использовать пакетный запрос.

В первом запросе пакета мы выберем данные шапки документа, а во втором запросе — данные табличной части. Посмотрим на этот пример подробнее:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ПутевойЛист.Номер КАК Номер,
    |	ПутевойЛист.Дата КАК Дата,
    |	ПутевойЛист.Ссылка КАК Ссылка
    |ИЗ
    |	Документ.ПутевойЛист КАК ПутевойЛист
    |ГДЕ
    |	ПутевойЛист.Ссылка = &Ссылка
    |;
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	ЗаданиеВодителю.Место КАК Место,
    |	ЗаданиеВодителю.НомерСтроки КАК НомерСтроки
    |ИЗ
    |	Документ.ПутевойЛист.ЗаданиеВодителю КАК ЗаданиеВодителю
    |ГДЕ
    |	ЗаданиеВодителю.Ссылка = &Ссылка";

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

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

// Извлекаем данные шапки в выборку
ВыборкаШапки = РезультатыПакета[0].Выбрать();

// Извлекаем данные табличной части в Таблицу Значений
ТаблицаТЧ = РезультатыПакета[1].Выгрузить();

Теперь, имея полноценную таблицу значений ТаблицаТЧ, мы можем абсолютно безопасно обходить ее через цикл Для каждого, не боясь ошибок итератора:


// Очистка параметров макета
Для Индекс = 1 По 5 Цикл
    ОбластьЛицевая.Параметры["СтрокаЗВ" + Индекс] = "";
КонецЦикла;

Сч = 1;
Для каждого СтрокаТЧ Из ТаблицаТЧ Цикл
    Если Сч > 5 Тогда
        Прервать;
    КонецЕсли;
    
    ОбластьЛицевая.Параметры["СтрокаЗВ" + Сч] = СтрокаТЧ.Место;
    Сч = Сч + 1;
КонецЦикла;

Интеграция решения в реальную процедуру печати

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

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


Процедура ПечатьГрузовогоПутевогоЛиста(Ссылка, ТабДок) Экспорт

    Макет = Документы.ПутевойЛист.ПолучитьМакет("Грузовой");
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	ПутевойЛист.Номер КАК Номер,
        |	ПутевойЛист.Дата КАК Дата,
        |	ПутевойЛист.Водитель1.Наименование КАК ВодительНаименование,
        |	ПутевойЛист.ТС.Марка КАК ТСМарка,
        |	ПутевойЛист.ТС.ГосНомер КАК ТСГосНомер,
        |	ПутевойЛист.ЗаданиеВодителю.(
        |		НомерСтроки,
        |		Место
        |	) КАК ЗаданиеВодителю
        |ИЗ
        |	Документ.PutevoyList КАК ПутевойЛист
        |ГДЕ
        |	ПутевойЛист.Ссылка В (&Ссылка)";
        
    Запрос.Параметры.Вставить("Ссылка", Ссылка);
    Выборка = Запрос.Выполнить.Выбрать();
    
    ОбластьЛицевая = Макет.ПолучитьОбласть("Лицевая");
    ОбластьОборотная = Макет.ПолучитьОбласть("Оборотная");
    
    ТабДок.Очистить();
    ВставлятьРазделительСтраниц = Ложь;
    
    Пока Выборка.Следующий() Цикл
    
        Если ВставлятьРазделительСтраниц Тогда
            ТабДок.ВывестиГоризонтальныйРазделительСтраниц;
        КонецЕсли;
        
        // Заполнение реквизитов шапки на макете
        ОбластьЛицевая.Параметры.Номер = Выборка.Номер;
        ОбластьЛицевая.Параметры.ДатаВыписки = Формат(Выборка.Дата, "ДЛФ=ДД");
        ОбластьЛицевая.Параметры.МодельТС = Выборка.ТСМарка;
        ОбластьЛицевая.Параметры.ТранспортноеСредствоГосНомер = Выборка.ТСГосНомер;
        ОбластьЛицевая.Параметры.Водитель1 = Выборка.ВодительНаименование;
        
        // === НАЧАЛО БЛОКА ЗАПОЛНЕНИЯ ФИКСИРОВАННЫХ СТРОК ТЧ ===
        
        // 1. Сначала обязательно очищаем все 5 параметров макета пустой строкой
        Для Индекс = 1 По 5 Цикл
            ОбластьЛицевая.Параметры["СтрокаЗВ" + Индекс] = "";
        КонецЦикла;
        
        // 2. Получаем вложенную выборку строк табличной части
        ВыборкаЗадания = Выборка.ЗаданиеВодителю.Выбрать();
        
        // 3. Выводим строки в макет (не более 5)
        Сч = 1;
        Пока ВыборкаЗадания.Следующий() Цикл
            Если Сч > 5 Тогда
                // Если строк больше 5, прерываем заполнение, чтобы избежать ошибок выхода за границы
                Прервать; 
            КонецЕсли;
            
            // Назначаем значение параметра динамически по счетчику
            ОбластьЛицевая.Параметры["СтрокаЗВ" + Сч] = ВыборкаЗадания.Место;
            Сч = Сч + 1;
        КонецЦикла;
        
        // === КОНЕЦ БЛОКА ЗАПОЛНЕНИЯ ФИКСИРОВАННЫХ СТРОК ТЧ ===
        
        // Выводим заполненные области в итоговый табличный документ
        ТабДок.Вывести(ОбластьЛицевая);
        ТабДок.Вывести(ОбластьОборотная);
        
        ВставлятьРазделительСтраниц = Истина;
        
    КонецЦикла;

КонецПроцедуры

Методологический совет: Что делать, если строк больше 5?

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

Для правильного решения этой бизнес-задачи на практике используют три подхода:

  1. Программный запрет на заполнение в форме. На уровне формы документа пишется проверка «ПриЗаписи» или «ПередЗаписью». Если пользователь пытается добавить в табличную часть более 5 строк, система выдает предупреждение: «В данном типе путевого листа разрешено вводить не более 5 строк заданий!» и отменяет запись документа. Это самый простой и надежный способ избежать расхождений между экранной и печатной формой.
  2. Печать дополнительного листа (Приложения). Если количество строк превышает емкость бланка (Строк > 5), то в параметры макета выводятся первые 4 строки. В 5-ю строку программно записывается текст: «См. продолжение в Приложении к путевому листу». После основного макета программно генерируется и выводится обычная динамическая таблица со всеми 8 строками в виде отдельной страницы-приложения.
  3. Разбиение на несколько листов. Если строк много, программа может автоматически сгенерировать несколько одинаковых бланков путевого листа (Страница 1 из 2, Страница 2 из 2), распределив строки ТЧ равномерно по бланкам (на первом — строки с 1 по 5, на втором — с 6 по 10).

Выбор конкретного варианта всегда зависит от требований бизнес-процесса вашей организации и используемых стандартов документооборота.

← На главную