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

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

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

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

В качестве отправной точки возьмем код, который формирует печатную форму на основе одного, жестко заданного макета Word.


&НаКлиенте
Процедура СформироватьПечатнуюФорму(ОбъектыНазначения)
    СсылкаНаОбъект = ОбъектыНазначения[0];
    
    // ... получение параметров ...
    СтруктураПараметров = ПолучитьСтруктуруПараметров(СсылкаНаОбъект, п_Организация);

    НазваниеФайла = ПолучитьИмяВременногоФайла("doc");
    ДанныеФормы = Объект;
    
    // Здесь макет жестко задан в коде
    МакетОбъект = ПолучитьМакетНаСервере("Макет1"); 
    
    МакетОбъект.Записать(НазваниеФайла);
    mWordObject = Новый COMObject("Word.Application");
    
    Попытка
        WordDoc = mWordObject.Documents.Add(НазваниеФайла);
        ЗаполнитьПараметрыТрудовогоДоговора(WordDoc, СтруктураПараметров);
        mWordObject.Application.Visible = Истина;
        mWordObject.Activate;
    Исключение
        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = ОписаниеОшибки;
        Сообщение.Сообщить;
        mWordObject.Application.Quit; // Важно закрывать приложение в случае ошибки
    КонецПопытки;
КонецПроцедуры

Наша цель — модифицировать этот код так, чтобы вместо "Макет1" можно было динамически подставлять имя выбранного пользователем макета.

Решение 1: Простое и быстрое — несколько кнопок печати

Это самый прямолинейный подход. Если макетов немного (2-3) и не предполагается их частое добавление, можно создать отдельную команду для каждого макета.

Пошаговая реализация:

  1. В дереве объектов ВПФ на вкладке "Команды" создадим несколько команд, например, "ПечатьПоШаблону1", "ПечатьПоШаблону2", "ПечатьПоШаблону3". Зададим им понятные для пользователя синонимы.
  2. Перенесем эти команды в "Командный интерфейс формы", чтобы они отобразились в виде кнопок.
  3. Вместо одной общей процедуры печати создадим обработчики для каждой кнопки. Каждый обработчик будет вызывать основную процедуру формирования, передавая в нее имя нужного макета.

Для этого сначала доработаем нашу основную процедуру, чтобы она принимала параметр с именем макета.


&НаКлиенте
Процедура СформироватьПечатнуюФорму(ОбъектыНазначения, ИмяМакета) // Добавили параметр
    СсылкаНаОбъект = ОбъектыНазначения[0];
    
    // ... получение параметров ...
    СтруктураПараметров = ПолучитьСтруктуруПараметров(СсылкаНаОбъект, п_Организация);

    НазваниеФайла = ПолучитьИмяВременногоФайла("doc");
    
    // Теперь имя макета получаем из параметра
    МакетОбъект = ПолучитьМакетНаСервере(ИмяМакета); 
    
    // ... остальной код без изменений ...
КонецПроцедуры

Теперь создадим обработчики для каждой кнопки:


&НаКлиенте
Процедура ПечатьПоШаблону1(Команда)
    МассивДанных = Новый Массив;
    МассивДанных.Добавить(Объект.СсылкаНаОбъект);
    СформироватьПечатнуюФорму(МассивДанных, "Макет1");
КонецПроцедуры

&НаКлиенте
Процедура ПечатьПоШаблону2(Команда)
    МассивДанных = Новый Массив;
    МассивДанных.Добавить(Объект.СсылкаНаОбъект);
    СформироватьПечатнуюФорму(МассивДанных, "Макет2");
КонецПроцедуры

&НаКлиенте
Процедура ПечатьПоШаблону3(Команда)
    МассивДанных = Новый Массив;
    МассивДанных.Добавить(Объект.СсылкаНаОбъект);
    СформироватьПечатнуюФорму(МассивДанных, "Макет3");
КонецПроцедуры

Плюсы: очень просто и быстро в реализации.

Минусы: загромождает интерфейс кнопками, плохо масштабируется. Если появится четвертый макет, придется снова лезть в конфигуратор и добавлять новую команду и кнопку.

Решение 2: Удобное и элегантное — выбор из выпадающего списка

Этот способ гораздо лучше с точки зрения пользовательского опыта. У нас остается одна кнопка "Печать", при нажатии на которую появляется контекстное меню или диалоговое окно со списком доступных макетов.

Для реализации этого воспользуемся клиентскими методами ПоказатьВыборИзСписка или Вопрос.

Пошаговая реализация:

  1. Оставляем одну команду "Печать".
  2. В ее клиентском обработчике формируем СписокЗначений с названиями макетов.
  3. Вызываем метод ПоказатьВыборИзСписка, который покажет пользователю модальное окно.
  4. После выбора пользователя, если он был сделан, вызываем нашу доработанную процедуру СформироватьПечатнуюФорму.

Посмотрим на пример кода:


&НаКлиенте
Процедура Печать(Команда)
    // Создаем список для выбора
    СписокМакетов = Новый СписокЗначений;
    СписокМакетов.Добавить("Макет1", "Направление на медосмотр (основной)");
    СписокМакетов.Добавить("Макет2", "Направление на медосмотр (сокращенный)");
    СписокМакетов.Добавить("Макет3", "Направление для водителей (с доп. пунктами)");

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

&НаКлиенте
Процедура ПослеВыбораМакета(ВыбранныйЭлемент, ДополнительныеПараметры) Экспорт
    // Процедура будет вызвана после того, как пользователь сделает выбор.
    // Если пользователь нажал "Отмена", ВыбранныйЭлемент будет Неопределено.
    Если ВыбранныйЭлемент = Неопределено Тогда
        Возврат; // Ничего не делаем
    КонецЕсли;

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

Плюсы: чистый интерфейс (всего одна кнопка), интуитивно понятно для пользователя, легко добавить новые макеты (просто дописать одну строку в СписокЗначений).

Минусы: немного сложнее в реализации из-за асинхронного вызова.

Решение 3: Профессиональный и масштабируемый — отдельная форма выбора

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

Пошаговая реализация:

  1. В ВПФ создаем новую форму. Пусть это будет "ФормаВыбораМакета".
  2. На этой форме размещаем реквизит, например, ИмяВыбранногоМакета (тип Строка).
  3. На форме размещаем элемент управления, который будет заполнять этот реквизит. Идеально подойдет Поле выбора с видом Список выбора или Переключатель.
  4. Добавляем на форму кнопку "Сформировать" (или "ОК"), которая будет закрывать форму и возвращать выбранное значение.
  5. В основной форме, в обработчике команды "Печать", мы будем открывать эту форму модально.

Рассмотрим код. Сначала код формы выбора ("ФормаВыбораМакета").

Модуль формы выбора:


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

&НаКлиенте
Процедура Сформировать(Команда)
    // Закрываем форму и возвращаем выбранное значение
    Закрыть(ИмяВыбранногоМакета);
КонецПроцедуры

Теперь код в модуле основной формы ВПФ:


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

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

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

Минусы: требует больше всего времени на разработку по сравнению с другими способами.

Важное замечание по работе с COM-объектом Word

При работе с COMObject("Word.Application") критически важно обеспечить корректное завершение работы приложения Word, особенно в случае ошибок. Если этого не сделать, в диспетчере задач могут оставаться "зависшие" процессы winword.exe, которые потребляют ресурсы и могут блокировать файлы.

Всегда оборачивайте код работы с COM-объектом в конструкцию Попытка...Исключение. В блоке Исключение обязательно вызывайте метод mWordObject.Application.Quit(), чтобы принудительно закрыть приложение Word и освободить ресурсы.

← На главную