В процессе разработки в среде 1С:Предприятие 8.3, особенно при использовании Библиотеки стандартных подсистем (БСП), программисты часто сталкиваются с необходимостью реализовать универсальную процедуру печати. Типичная ситуация: одна и та же внешняя печатная форма или общая процедура в модуле менеджера должна обрабатывать разные типы документов, применяя к ним различную логику формирования табличного документа. Рассмотрим подробнее, как правильно определить тип документа в процедуре Печать.
Разберем структуру стандартной экспортной процедуры Печать, которая обычно располагается в модуле менеджера объекта или в модуле обработки печати:
Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
// Здесь должен быть код определения типа документа
КонецПроцедуры
Проанализируем параметры этой процедуры. Самым важным для нас является МассивОбъектов. Вопреки названию, в этом параметре чаще всего передаются не сами Объекты (в понимании ДокументОбъект), а Ссылки (ДокументСсылка) на документы, выбранные пользователем для печати. Это критически важный момент, так как работа со ссылками требует иного подхода, чем работа с объектами в памяти.
Самый быстрый и методически верный способ определить, с каким документом мы работаем — это использование функции ТипЗнч и сравнение её с типом ссылки конкретного документа. Рассмотрим по шагам, как это реализовать. Поскольку в МассивОбъектов может быть несколько документов (групповая печать), нам нужно проверить хотя бы первый элемент массива.
Если МассивОбъектов.Количество() > 0 Тогда
СсылкаНаДокумент = МассивОбъектов[0];
Если ТипЗнч(СсылкаНаДокумент) = Тип("ДокументСсылка.ДанныеДляРасчетаЗарплаты") Тогда
// Логика для документа Данные для расчета зарплаты
ИначеЕсли ТипЗнч(СсылкаНаДокумент) = Тип("ДокументСсылка.НачислениеЗарплаты") Тогда
// Логика для документа Начисление зарплаты
КонецЕсли;
КонецЕсли;
Почему это работает лучше всего? Сравнение типов в 1С происходит очень быстро, так как идентификаторы типов кэшируются платформой. Выполнение Тип("ДокументСсылка.Имя") не требует обращения к базе данных или тяжелых операций с метаданными.
Иногда нам нужно получить строковое имя документа, чтобы использовать его в динамических условиях или логах. В этом случае мы можем обратиться к свойству Метаданные() ссылки. Проанализируем пример кода:
ИмяДокумента = МассивОбъектов[0].Метаданные().Имя;
Если ИмяДокумента = "ДанныеДляРасчетаЗарплаты" Тогда
// Обработка конкретного типа
КонецЕсли;
Этот способ удобен тем, что Метаданные().Имя возвращает именно то имя документа, которое задано в конфигураторе. Однако стоит помнить, что частое обращение к метаданным в циклах может быть чуть менее производительным, чем прямое сравнение типов.
Выясним причину, по которой код из первого сообщения темы не работал. Автор пытался использовать конструкцию:
Если Документы.ДанныеДляРасчетаЗарплаты Тогда...
В чем ошибка? Документы.ДанныеДляРасчетаЗарплаты — это обращение к Менеджеру документа. В языке 1С менеджер объекта существует всегда (если такой объект определен в конфигурации). Поэтому проверка этого значения в условии Если всегда будет возвращать Истина, так как объект менеджера не равен Неопределено или Ложь. Эта проверка никак не связана с тем, какой именно документ в данный момент находится в обработке.
Рассмотрим ситуацию, когда печать вызывается из общего журнала документов, и пользователь выделил документы разных типов. В этом случае проверка только первого элемента МассивОбъектов[0] приведет к ошибке при обработке последующих элементов. Правильный алгоритм должен выглядеть так:
Для Каждого Ссылка Из МассивОбъектов Цикл
МетаданныеОбъекта = Ссылка.Метаданные();
Если МетаданныеОбъекта = Метаданные.Документы.ДанныеДляРасчетаЗарплаты Тогда
// Индивидуальная логика формирования данных для Т-11
ИначеЕсли МетаданныеОбъекта = Метаданные.Документы.НачислениеЗарплаты Тогда
// Другая логика
КонецЕсли;
КонецЦикла;
При определении типа документа нам часто нужно получить и его реквизиты (например, «Вид операции» или «Организация»). Помните, что в процедуре печати мы имеем дело со ссылками. Вызов Ссылка.ПолучитьОбъект() крайне нежелателен, так как он считывает все данные документа из базы данных, что замедляет работу системы.
Вместо этого рекомендуется использовать функции общего назначения БСП для получения только необходимых полей:
// Пример получения реквизита без получения всего объекта
ВидДокумента = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаНаДокумент, "ВидОперации");
Если одна и та же форма печати регистрируется для разных документов в процедуре ДобавитьКомандыПечати, мы можем заранее задать уникальный идентификатор для каждой команды. Тогда в процедуре Печать нам не придется угадывать тип документа по ссылке, мы сможем ориентироваться на имя макета или параметр команды.
Посмотрим на пример регистрации команды:
Команда = КомандыПечати.Добавить();
Команда.Идентификатор = "ПриказТ11_Спец"; // Уникальный ID
Команда.Представление = НСтр("ru = 'Приказ (специальный)'");
// ... остальные параметры
В самой процедуре печати мы можем проверить, какая именно печатная форма запрошена:
Если УправлениеПечатью.НужноПечататьМакет(КоллекцияПечатныхФорм, "ПриказТ11_Спец") Тогда
// Мы точно знаем, что нужно печатать, даже если тип документа совпадает
КонецЕсли;
Для надежного определения документа в коде 1С 8.3 придерживайтесь следующих правил:
ТипЗнч(МассивОбъектов[0]) для быстрой идентификации типа.МассивОбъектов[0].Метаданные().Имя, если вам нужно строковое представление имени объекта метаданных.Если Документы.ИмяДокумента Тогда для проверки текущего контекста.ЗначениеРеквизитаОбъекта.Используя эти подходы, вы сможете создавать гибкие и производительные механизмы печати, которые корректно работают с любыми типами документов в вашей информационной базе.