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