Как реализовать интерактивный выбор параметров печати в управляемых формах 1С?

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

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

В управляемых формах ситуация в корне меняется. Формирование печатных форм происходит на сервере (обычно в модуле менеджера объекта), где интерактивные действия и диалоги с пользователем физически невозможны и вызывают ошибку компиляции. В то же время стандартная подсистема печати БСП требует красивого вывода табличного документа в типовое окно предпросмотра, где пользователю доступны отправка по почте, сохранение в PDF и другие сервисные функции. Как же «спросить» пользователя о параметрах печати (например, выбрать тип акта или указать количество копий поддонов), не нарушая стандарты разработки БСП? Разберем это детально.

Архитектурный принцип решения

Чтобы решить эту задачу красиво и без отказа от стандартного окна предпросмотра БСП, мы должны разделить процесс на два этапа:

  1. На клиенте: Перехватить нажатие кнопки печати, задать пользователю все необходимые вопросы в асинхронном режиме (используя описание оповещений) и упаковать полученные ответы в структуру параметров.
  2. На сервере: Передать эту структуру в стандартный метод печати БСП, распаковать параметры в модуле менеджера и сформировать соответствующий табличный документ.

Рассмотрим пошагово реализацию этого механизма для встроенных и внешних печатных форм.

Вариант 1. Реализация для встроенных печатных форм

Представим ситуацию из обсуждения: при печати документа нам необходимо спросить у пользователя, какой вид акта формировать — «Акт передачи» или «Акт возврата». Выясним, как это запрограммировать.

Шаг 1. Изменение обработчика в модуле менеджера

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


Процедура ДобавитьКомандыПечати(КомандыПечати) Экспорт
    
    КомандаПечати = КомандыПечати.Добавить();
    КомандаПечати.Идентификатор = "АктПриемаПередачи";
    КомандаПечати.Представление = НСтр("ru='Акт приема-передачи ОС';uk='Акт прийому-передачі ОЗ'");
    
    // Вместо стандартного УправлениеПечатьюБПКлиент.ВыполнитьКомандуПечати
    // указываем собственный клиентский общий модуль
    КомандаПечати.Обработчик = "МойКлиентскийМодульПечатиКлиент.ВыполнитьПечатьСВыборомРежима";
    КомандаПечати.СписокФорм = "ФормаСписка,ФормаВыбора,ФормаДокумента";
    
КонецПроцедуры

Шаг 2. Создание клиентского обработчика и диалога с пользователем

Теперь создадим неглобальный общий модуль МойКлиентскийМодульПечатиКлиент (свойства: «Клиент (управляемое приложение)»). В нем мы опишем процедуру, которая будет вызываться при нажатии кнопки на форме. Так как в управляемом клиенте все диалоги асинхронные, мы применим объект ОписаниеОповещения.


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

Шаг 3. Передача выбранного параметра в подсистему БСП

В этом же модуле пишем экспортную процедуру ожидания ПослеВыбораВидаАкта, которая сработает, когда пользователь выберет один из вариантов.


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

Шаг 4. Обработка параметров на сервере (в модуле менеджера)

Теперь возвращаемся в модуль менеджера документа РеализацияТоваровУслуг. Стандартный метод Печать принимает вторым параметром нашу структуру ПараметрыПечати. Нам остается лишь прочитать значение и направить алгоритм по нужному пути.


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

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


Решение практического кейса с вводом количества (Сообщение 17)

Рассмотрим более сложный пример, упомянутый в сообщении №17. Складским рабочим требуется распечатать этикетки для поддонов. При этом количество поддонов всегда разное, и программа должна запросить у пользователя число (например, «10»), чтобы сгенерировать 10 страниц с надписями «1/10», «2/10» ... «10/10».

Для решения этой задачи мы заменим выбор из списка на ввод числа.

Реализация на клиенте:


Процедура ВыполнитьПечатьЭтикетокПоддонов(ПараметрКоманды, ПараметрыВыполненияКоманды) Экспорт
    
    ДополнительныеПараметры = Новый Структура;
    ДополнительныеПараметры.Вставить("ПараметрКоманды", ПараметрКоманды);
    ДополнительныеПараметры.Вставить("Источник", ПараметрыВыполненияКоманды.Источник);
    
    Оповещение = Новый ОписаниеОповещения("ПослеВводаКоличестваПоддонов", ЭтотОбъект, ДополнительныеПараметры);
    
    // Запрашиваем у пользователя число с ограничением ввода
    Подсказка = "Укажите количество поддонов для печати:";
    ПоказатьВводЧисла(Оповещение, 1, Подсказка, 10, 2); // Начальное значение 1, разрядность 10, дробная часть 0
    
КонецПроцедуры

Процедура ПослеВводаКоличестваПоддонов(РезультатВвода, ДополнительныеПараметры) Экспорт
    
    Если РезультатВвода = Неопределено Или РезультатВвода <= 0 Тогда
        Возврат; // Пользователь отказался от ввода или ввел некорректное число
    КонецЕсли;
    
    ПараметрыПечати = Новый Структура;
    ПараметрыПечати.Вставить("КоличествоПоддонов", РезультатВвода);
    
    УправлениеПечатьюКлиент.ВыполнитьКомандуПечати(
        "Документ.РасходныйОрдерНаТовары",
        "ЭтикеткиПоддонов",
        ДополнительныеПараметры.ПараметрКоманды,
        ДополнительныеПараметры.Источник,
        ПараметрыПечати
    );
    
КонецПроцедуры

Реализация на сервере:

В модуле менеджера документа формируем макет в цикле, опираясь на переданный параметр:


Функция СформироватьЭтикеткиПоддонов(МассивОбъектов, ОбъектыПечати, ПараметрыПечати)
    
    ТабДок = Новый ТабличныйДокумент;
    ТабДок.КлючПараметровПечати = "ПараметрыПечати_ЭтикеткиПоддонов";
    
    КоличествоПоддонов = 1; // Значение по умолчанию
    Если ПараметрыПечати <> Неопределено И ПараметрыПечати.Свойство("КоличествоПоддонов") Тогда
        КоличествоПоддонов = ПараметрыПечати.КоличествоПоддонов;
    КонецЕсли;
    
    Макет = ПолучитьМакет("МакетЭтикетки");
    ОбластьЭтикетки = Макет.ПолучитьОбласть("Этикетка");
    
    ПервыйДокумент = Истина;
    
    Для Каждого СсылкаНаДокумент Из МассивОбъектов Цикл
        
        Для НомерПоддона = 1 По КоличествоПоддонов Цикл
            
            Если Не ПервыйДокумент Тогда
                ТабДок.ВывестиГоризонтальныйРазделительСтраниц();
            КонецЕсли;
            ПервыйДокумент = Ложь;
            
            // Заполняем параметры области макета
            ОбластьЭтикетки.Параметры.Заполнить(СсылкаНаДокумент);
            ОбластьЭтикетки.Параметры.НомерПоддонаТекст = Строка(НомерПоддона) + " / " + Строка(КоличествоПоддонов);
            
            ТабДок.Вывести(ОбластьЭтикетки);
            
        КонецЦикла;
        
    КонецЦикла;
    
    Возврат ТабДок;
    
КонецФункции

Вариант 2. Реализация во внешних печатных формах (ВПФ)

Если вы разрабатываете внешнюю печатную форму (ВПФ) и хотите спросить что-то у пользователя, вы не можете изменить общие модули или модуль менеджера типовой конфигурации. Но БСП предоставляет элегантное решение и для этого случая.

Проанализируем структуру внешней обработки и настроим ее для интерактивного взаимодействия.

Шаг 1. Настройка функции регистрации ВПФ

В модуле объекта внешней обработки в функции СведенияОВнешнейОбработке для нашей команды необходимо установить особый тип использования: Вызов клиентского метода.


Функция СведенияОВнешнейОбработке() Экспорт
    
    ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработкеКликСистем();
    
    // Задаем параметры регистрации (Вид, Назначение и т.д.)
    // ...
    
    НоваяКоманда = ПараметрыРегистрации.Команды.Добавить();
    НоваяКоманда.Представление = "Акт с выбором параметров (внешний)";
    НоваяКоманда.Идентификатор = "ВнешнийАктСПараметрами";
    
    // КРИТИЧЕСКИ ВАЖНО: Указываем клиентский запуск
    НоваяКоманда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовКлиентскогоМетода();
    
    Возврат ПараметрыРегистрации;
    
КонецФункции

Шаг 2. Создание формы внешней обработки

Поскольку команда имеет тип «Вызов клиентского метода», БСП при ее нажатии будет искать форму внешней обработки (которая должна быть назначена основной формой) и пытаться вызвать в ее модуле экспортную процедуру Печать.

Создадим основную форму внешней обработки и в ее модуле напишем код взаимодействия:


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

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

Шаг 3. Чтение параметров на сервере в ВПФ

В модуле объекта нашей ВПФ в процедуре Печать мы принимаем переданные параметры и формируем макет:


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

Заключение

Мы подробно разобрали, как обойти ограничение клиент-серверного разделения в 1С управляемых формах и сохранить при этом все преимущества интеграции с БСП. Главное правило — никогда не пытаться вызывать модальные или асинхронные окна на стороне сервера в процедурах печати модуля менеджера. Выносите всю интерактивную работу с пользователем на уровень клиентского обработчика команд, собирайте нужные данные и передавайте их на сервер через стандартный параметр ПараметрыПечати. Этот подход сделает вашу конфигурацию надежной, быстрой и полностью соответствующей стандартам разработки фирмы «1С».

← На главную