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