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