Как зарегистрировать и выводить несколько макетов в одной внешней печатной форме (ВПФ) на БСП

Программист 1С v8.3 (Управляемые формы) 1C:Бухгалтерия Налоговый учет IT и автоматизация бизнеса
← На главную

При разработке внешних печатных форм (ВПФ) в современных конфигурациях 1С, использующих Библиотеку Стандартных Подсистем (БСП), начинающие разработчики часто сталкиваются с архитектурным барьером. Типичная ситуация: в одной обработке печатной формы создано два макета (например, старый бланк УПД и новый УПД по Постановлению №981), и требуется выводить один из них в зависимости от условий (например, от даты документа). Однако при попытке программно переключить макет система либо ничего не выводит, либо печатает пустые листы, либо выводит обе формы одновременно.

В этой статье мы подробно разберем, почему возникает эта проблема, проанализируем внутренние механизмы работы БСП и рассмотрим два практических решения: автоматический выбор макета в зависимости от параметров документа и ручной выбор пользователем через меню печати.

Выясняем причину проблемы: разница между идентификатором команды и именем макета

Главная ошибка, которую совершают программисты при работе с БСП, заключается в смешивании понятий "Идентификатор команды" и "Имя макета".

Давайте посмотрим, как работает стандартный механизм БСП при вызове внешней печатной формы:

  1. При регистрации обработки система вызывает функцию СведенияОВнешнейОбработке(). В таблице команд регистрируются доступные кнопки для меню "Печать". Каждая команда имеет свой уникальный Идентификатор.
  2. Когда пользователь нажимает кнопку печати, платформа формирует коллекцию печатных форм (переменная КоллекцияПечатныхФорм) и передает её в процедуру Печать(). Эта коллекция содержит строки только для тех команд, которые были явно выбраны пользователем.
  3. Метод БСП УправлениеПечатью.НужноПечататьМакет(КоллекцияПечатныхФорм, "Идентификатор") проверяет наличие строки с указанным идентификатором команды в этой коллекции. Он не проверяет наличие макета в метаданных вашей обработки!

Если в коде функции СведенияОВнешнейОбработке() зарегистрирована только одна команда (например, с идентификатором "ПФ_MXL_УПД"), то проверка УправлениеПечатью.НужноПечататьМакет(КоллекцияПечатныхФорм, "ПФ_MXL_УПД981") всегда будет возвращать значение Ложь, так как команда с таким идентификатором не существует в системе и не могла быть выбрана пользователем.

Рассмотрим два правильных подхода к реализации логики с несколькими макетами.

Решение 1. Автоматический выбор макета (одна кнопка в меню "Печать")

Этот вариант наиболее удобен для конечного пользователя. В интерфейсе документа отображается всего одна кнопка (например, "Универсальный передаточный документ (внешний)"). Система сама анализирует дату документа и подставляет нужный бланк: старый — для документов прошлых лет, новый — для актуальных периодов.

Рассмотрим реализацию этого метода по шагам.

Шаг 1. Регистрация единственной команды

В функции СведенияОВнешнейОбработке мы регистрируем одну команду общего назначения. Имя идентификатора может быть любым удобным, например, "УниверсальныйПередаточныйДокумент".


Функция СведенияОВнешнейОбработке() Экспорт
    ПараметрыРегистрации = Новый Структура;
    МассивНазначений = Новый Массив;
    МассивНазначений.Добавить("Документ.РеализацияТоваровУслуг");
    
    ПараметрыРегистрации.Вставить("Вид", "ПечатнаяФорма");
    ПараметрыРегистрации.Вставить("Назначение", МассивНазначений);
    ПараметрыРегистрации.Вставить("Наименование", "УПД (внешняя с автовыбором макета)");
    ПараметрыРегистрации.Вставить("БезопасныйРежим", Ложь);
    ПараметрыРегистрации.Вставить("Версия", "1.1");
    
    ТаблицаКоманд = ПолучитьТаблицуКоманд();
    // Регистрируем ОДНУ универсальную команду
    ДобавитьКоманду(ТаблицаКоманд, 
                    "УПД (внешний)", 
                    "УниверсальныйПередаточныйДокумент", 
                    "ВызовСерверногоМетода", 
                    Ложь, 
                    "ПечатьMXL");
                    
    ПараметрыРегистрации.Вставить("Команды", ТаблицаКоманд);
    Возврат ПараметрыРегистрации;
КонецФункции

Шаг 2. Анализ условий и выбор макета в процедуре "Печать"

В экспортной процедуре Печать() мы перехватываем управление для нашей команды и определяем логику выбора макета на основании реквизитов документа.


Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
    
    ПараметрыПечати = Новый Структура;
    
    // Проверяем, выбрана ли наша общая команда
    Если УправлениеПечатью.НужноПечататьМакет(КоллекцияПечатныхФорм, "УниверсальныйПередаточныйДокумент") Тогда
        
        // Формируем табличный документ, логику выбора макета переносим внутрь функции формирования
        ТабДокумент = СформироватьПечатнуюФорму(МассивОбъектов, ОбъектыПечати, ПараметрыПечати);
        
        // Выводим полученный табличный документ в коллекцию под зарегистрированным идентификатором команды
        УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию(
            КоллекцияПечатныхФорм, 
            "УниверсальныйПередаточныйДокумент", 
            "Универсальный передаточный документ", 
            ТабДокумент
        );
        
    КонецЕсли;
    
КонецПроцедуры

Шаг 3. Динамическая загрузка макета в функции формирования документа

Проанализируем ситуацию внутри функции генерации табличного документа. Мы обходим массив объектов печати, определяем их ключевые свойства (в данном случае — дату) и на основании этого загружаем соответствующий двоичный макет из метаданных обработки.


Функция СформироватьПечатнуюФорму(МассивОбъектов, ОбъектыПечати, ПараметрыПечати)
    
    ТабДокумент = Новый ТабличныйДокумент;
    ТабДокумент.АвтоМасштаб = Истина;
    
    // Получаем общие данные для документов (для примера возьмем дату первого документа)
    ДатаДокумента = Неопределено;
    Если МассивОбъектов.Количество() > 0 Тогда
        ДатаДокумента = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(МассивОбъектов[0], "Дата");
    КонецЕсли;
    
    // Граничная дата введения бланка по Постановлению №981 (01.10.2017)
    ГраничнаяДата = Дата("20171001000000");
    
    // Выбираем макет в зависимости от даты документа
    Если ДатаДокумента <> Неопределено И ДатаДокумента < ГраничнаяДата Тогда
        // Используем старый макет из метаданных внешней обработки
        Макет = ПолучитьМакет("ПФ_MXL_УПД");
        ИмяКлючаПараметров = "ПАРАМЕТРЫ_ПЕЧАТИ_УПД_СТАРЫЙ";
    Иначе
        // Используем новый макет по Постановлению 981
        Макет = ПолучитьМакет("ПФ_MXL_УПД981");
        ИмяКлючаПараметров = "ПАРАМЕТРЫ_ПЕЧАТИ_УПД_981";
    КонецЕсли;
    
    ТабДокумент.КлючПараметровПечати = ИмяКлючаПараметров;
    
    // Алгоритм заполнения областей макета данными
    // [Здесь располагается стандартный код заполнения параметров макета и вывода областей в ТабДокумент]
    // ...
    
    Возврат ТабДокумент;
    
КонецФункции

При таком подходе пользователь нажимает одну кнопку, а система автоматически генерирует правильный печатный бланк.

Решение 2. Ручной выбор макета пользователем (несколько кнопок в меню "Печать")

Этот подход используется в случаях, когда пользователю необходимо предоставить осознанный выбор бланка для печати в любой момент времени независимо от даты или других параметров документа. Нам потребуется зарегистрировать несколько команд в БСП.

Шаг 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");

Почему так делать категорически не рекомендуется:

Резюме

Для корректного вывода нескольких макетов в одной ВПФ на БСП всегда следуйте двум правилам:

  1. Проверяйте только те идентификаторы, которые вы зарегистрировали в функции СведенияОВнешнейОбработке().
  2. Если логика выбора макета автоматическая — регистрируйте одну команду общего названия, а ветвление по макетам ПолучитьМакет() делайте внутри процедуры генерации табличного документа.
← На главную