При разработке внешних печатных форм (ВПФ) в современных конфигурациях 1С, использующих Библиотеку Стандартных Подсистем (БСП), начинающие разработчики часто сталкиваются с архитектурным барьером. Типичная ситуация: в одной обработке печатной формы создано два макета (например, старый бланк УПД и новый УПД по Постановлению №981), и требуется выводить один из них в зависимости от условий (например, от даты документа). Однако при попытке программно переключить макет система либо ничего не выводит, либо печатает пустые листы, либо выводит обе формы одновременно.
В этой статье мы подробно разберем, почему возникает эта проблема, проанализируем внутренние механизмы работы БСП и рассмотрим два практических решения: автоматический выбор макета в зависимости от параметров документа и ручной выбор пользователем через меню печати.
Главная ошибка, которую совершают программисты при работе с БСП, заключается в смешивании понятий "Идентификатор команды" и "Имя макета".
Давайте посмотрим, как работает стандартный механизм БСП при вызове внешней печатной формы:
СведенияОВнешнейОбработке(). В таблице команд регистрируются доступные кнопки для меню "Печать". Каждая команда имеет свой уникальный Идентификатор.КоллекцияПечатныхФорм) и передает её в процедуру Печать(). Эта коллекция содержит строки только для тех команд, которые были явно выбраны пользователем.УправлениеПечатью.НужноПечататьМакет(КоллекцияПечатныхФорм, "Идентификатор") проверяет наличие строки с указанным идентификатором команды в этой коллекции. Он не проверяет наличие макета в метаданных вашей обработки!Если в коде функции СведенияОВнешнейОбработке() зарегистрирована только одна команда (например, с идентификатором "ПФ_MXL_УПД"), то проверка УправлениеПечатью.НужноПечататьМакет(КоллекцияПечатныхФорм, "ПФ_MXL_УПД981") всегда будет возвращать значение Ложь, так как команда с таким идентификатором не существует в системе и не могла быть выбрана пользователем.
Рассмотрим два правильных подхода к реализации логики с несколькими макетами.
Этот вариант наиболее удобен для конечного пользователя. В интерфейсе документа отображается всего одна кнопка (например, "Универсальный передаточный документ (внешний)"). Система сама анализирует дату документа и подставляет нужный бланк: старый — для документов прошлых лет, новый — для актуальных периодов.
Рассмотрим реализацию этого метода по шагам.
Шаг 1. Регистрация единственной команды
В функции СведенияОВнешнейОбработке мы регистрируем одну команду общего назначения. Имя идентификатора может быть любым удобным, например, "УниверсальныйПередаточныйДокумент".
Функция СведенияОВнешнейОбработке() Экспорт
ПараметрыРегистрации = Новый Структура;
МассивНазначений = Новый Массив;
МассивНазначений.Добавить("Документ.РеализацияТоваровУслуг");
ПараметрыРегистрации.Вставить("Вид", "ПечатнаяФорма");
ПараметрыРегистрации.Вставить("Назначение", МассивНазначений);
ПараметрыРегистрации.Вставить("Наименование", "УПД (внешняя с автовыбором макета)");
ПараметрыРегистрации.Вставить("БезопасныйРежим", Ложь);
ПараметрыРегистрации.Вставить("Версия", "1.1");
ТаблицаКоманд = ПолучитьТаблицуКоманд();
// Регистрируем ОДНУ универсальную команду
ДобавитьКоманду(ТаблицаКоманд,
"УПД (внешний)",
"УниверсальныйПередаточныйДокумент",
"ВызовСерверногоМетода",
Ложь,
"ПечатьMXL");
ПараметрыРегистрации.Вставить("Команды", ТаблицаКоманд);
Возврат ПараметрыРегистрации;
КонецФункции
Шаг 2. Анализ условий и выбор макета в процедуре "Печать"
В экспортной процедуре Печать() мы перехватываем управление для нашей команды и определяем логику выбора макета на основании реквизитов документа.
Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
ПараметрыПечати = Новый Структура;
// Проверяем, выбрана ли наша общая команда
Если УправлениеПечатью.НужноПечататьМакет(КоллекцияПечатныхФорм, "УниверсальныйПередаточныйДокумент") Тогда
// Формируем табличный документ, логику выбора макета переносим внутрь функции формирования
ТабДокумент = СформироватьПечатнуюФорму(МассивОбъектов, ОбъектыПечати, ПараметрыПечати);
// Выводим полученный табличный документ в коллекцию под зарегистрированным идентификатором команды
УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию(
КоллекцияПечатныхФорм,
"УниверсальныйПередаточныйДокумент",
"Универсальный передаточный документ",
ТабДокумент
);
КонецЕсли;
КонецПроцедуры
Шаг 3. Динамическая загрузка макета в функции формирования документа
Проанализируем ситуацию внутри функции генерации табличного документа. Мы обходим массив объектов печати, определяем их ключевые свойства (в данном случае — дату) и на основании этого загружаем соответствующий двоичный макет из метаданных обработки.
Функция СформироватьПечатнуюФорму(МассивОбъектов, ОбъектыПечати, ПараметрыПечати)
ТабДокумент = Новый ТабличныйДокумент;
ТабДокумент.АвтоМасштаб = Истина;
// Получаем общие данные для документов (для примера возьмем дату первого документа)
ДатаДокумента = Неопределено;
Если МассивОбъектов.Количество() > 0 Тогда
ДатаДокумента = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(МассивОбъектов[0], "Дата");
КонецЕсли;
// Граничная дата введения бланка по Постановлению №981 (01.10.2017)
ГраничнаяДата = Дата("20171001000000");
// Выбираем макет в зависимости от даты документа
Если ДатаДокумента <> Неопределено И ДатаДокумента < ГраничнаяДата Тогда
// Используем старый макет из метаданных внешней обработки
Макет = ПолучитьМакет("ПФ_MXL_УПД");
ИмяКлючаПараметров = "ПАРАМЕТРЫ_ПЕЧАТИ_УПД_СТАРЫЙ";
Иначе
// Используем новый макет по Постановлению 981
Макет = ПолучитьМакет("ПФ_MXL_УПД981");
ИмяКлючаПараметров = "ПАРАМЕТРЫ_ПЕЧАТИ_УПД_981";
КонецЕсли;
ТабДокумент.КлючПараметровПечати = ИмяКлючаПараметров;
// Алгоритм заполнения областей макета данными
// [Здесь располагается стандартный код заполнения параметров макета и вывода областей в ТабДокумент]
// ...
Возврат ТабДокумент;
КонецФункции
При таком подходе пользователь нажимает одну кнопку, а система автоматически генерирует правильный печатный бланк.
Этот подход используется в случаях, когда пользователю необходимо предоставить осознанный выбор бланка для печати в любой момент времени независимо от даты или других параметров документа. Нам потребуется зарегистрировать несколько команд в БСП.
Шаг 1. Модернизация функции "СведенияОВнешнейОбработке"
Добавим в таблицу две разные команды с уникальными идентификаторами, соответствующими каждому макету:
Функция СведенияОВнешнейОбработке() Экспорт
ПараметрыРегистрации = Новый Структура;
МассивНазначений = Новый Массив;
МассивНазначений.Добавить("Документ.РеализацияТоваровУслуг");
ПараметрыРегистрации.VассивНазначений = МассивНазначений;
ПараметрыРегистрации.Вставить("Вид", "ПечатнаяФорма");
ПараметрыРегистрации.Вставить("Назначение", МассивНазначений);
ПараметрыРегистрации.Вставить("Наименование", "УПД (внешняя с ручным выбором)");
ПараметрыРегистрации.Вставить("БезопасныйРежим", Ложь);
ПараметрыРегистрации.Вставить("Версия", "1.1");
ТаблицаКоманд = ПолучитьТаблицуКоманд();
// Регистрируем команду для старого бланка
ДобавитьКоманду(ТаблицаКоманд,
"УПД (до 01.10.2017)",
"ПФ_MXL_УПД_Старый",
"ВызовСерверногоМетода",
Ложь,
"ПечатьMXL");
// Регистрируем команду для нового бланка (Постановление 981)
ДобавитьКоманду(ТаблицаКоманд,
"УПД (пост. 981)",
"ПФ_MXL_УПД_981",
"ВызовСерверногоМетода",
Ложь,
"ПечатьMXL");
ПараметрыРегистрации.Вставить("Команды", ТаблицаКоманд);
Возврат ПараметрыРегистрации;
КонецФункции
Шаг 2. Раздельная обработка команд в процедуре "Печать"
Теперь в процедуре Печать() мы должны проверить каждую команду отдельно. Обратите внимание, что пользователь может выделить в списке несколько документов и нажать печать разных вариантов — БСП вызовет процедуру один раз, но заполнит обе строки в коллекции.
Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
ПараметрыПечати = Новый Структура;
// Проверяем вызов старого бланка
Если УправлениеПечатью.НужноПечататьМакет(КоллекцияПечатныхФорм, "ПФ_MXL_УПД_Старый") Тогда
ТабДокумент = СформироватьПечатнуюФормуПоИмени("ПФ_MXL_УПД", МассивОбъектов, ОбъектыПечати, ПараметрыПечати);
УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию(
КоллекцияПечатныхФорм,
"ПФ_MXL_УПД_Старый",
"УПД (до 01.10.2017)",
ТабДокумент
);
КонецЕсли;
// Проверяем вызов нового бланка
Если УправлениеПечатью.НужноПечататьМакет(КоллекцияПечатныхФорм, "ПФ_MXL_УПД_981") Тогда
ТабДокумент = СформироватьПечатнуюФормуПоИмени("ПФ_MXL_УПД981", МассивОбъектов, ОбъектыПечати, ПараметрыПечати);
УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию(
КоллекцияПечатныхФорм,
"ПФ_MXL_УПД_981",
"УПД (пост. 981)",
ТабДокумент
);
КонецЕсли;
КонецПроцедуры
Функция генерации формы СформироватьПечатнуюФормуПоИмени() в данном случае принимает имя требуемого макета в качестве параметра, получает его методом ПолучитьМакет(ИмяМакет) и заполняет его данными по стандартному алгоритму.
Проанализируем попытку обойти механизмы БСП "напрямую", которую предпринял автор темы в сообщении №15:
НовСтр = КоллекцияПечатныхФорм.добавить();
ЗаполнитьЗначенияСвойств(НовСТр,КоллекцияПечатныхФорм[0]);
НовСтр.ИмяМакета = "ПФ_MXL_УПД981";
НовСтр.ИмяВРЕГ = ВРЕГ("ПФ_MXL_УПД981");
Почему так делать категорически не рекомендуется:
КоллекцияПечатныхФорм вручную заставляет подсистему печати думать, что пользователь заказал вывод сразу двух различных документов. В итоге на экран выводятся две вкладки, одна из которых может оказаться пустой или содержать ошибку.ПараметрыВывода.ДокументыКомплекта для формирования пакета документов (например, Счет-фактура + Торг-12). Искусственное добавление строк в коллекцию ломает сопоставление метаданных, и печать комплекта завершается аварийно.Для корректного вывода нескольких макетов в одной ВПФ на БСП всегда следуйте двум правилам:
СведенияОВнешнейОбработке().ПолучитьМакет() делайте внутри процедуры генерации табличного документа.