Часто возникает задача: для одного и того же документа или справочника необходимо иметь несколько вариантов печатных форм. Например, разные шаблоны договоров, коммерческих предложений или, как в рассматриваемом случае, направлений на медосмотр. Данные для печати одни и те же, но их визуальное представление должно отличаться. Создавать под каждый вариант отдельную внешнюю печатную форму (ВПФ) — неэффективно и неудобно в сопровождении.
Рассмотрим, как реализовать механизм выбора нужного макета в рамках одной ВПФ, чтобы пользователь мог легко переключаться между шаблонами, а код оставался централизованным и управляемым. Проанализируем несколько подходов: от самого простого до наиболее профессионального и гибкого.
В качестве отправной точки возьмем код, который формирует печатную форму на основе одного, жестко заданного макета Word.
&НаКлиенте
Процедура СформироватьПечатнуюФорму(ОбъектыНазначения)
СсылкаНаОбъект = ОбъектыНазначения[0];
// ... получение параметров ...
СтруктураПараметров = ПолучитьСтруктуруПараметров(СсылкаНаОбъект, п_Организация);
НазваниеФайла = ПолучитьИмяВременногоФайла("doc");
ДанныеФормы = Объект;
// Здесь макет жестко задан в коде
МакетОбъект = ПолучитьМакетНаСервере("Макет1");
МакетОбъект.Записать(НазваниеФайла);
mWordObject = Новый COMObject("Word.Application");
Попытка
WordDoc = mWordObject.Documents.Add(НазваниеФайла);
ЗаполнитьПараметрыТрудовогоДоговора(WordDoc, СтруктураПараметров);
mWordObject.Application.Visible = Истина;
mWordObject.Activate;
Исключение
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ОписаниеОшибки;
Сообщение.Сообщить;
mWordObject.Application.Quit; // Важно закрывать приложение в случае ошибки
КонецПопытки;
КонецПроцедуры
Наша цель — модифицировать этот код так, чтобы вместо "Макет1" можно было динамически подставлять имя выбранного пользователем макета.
Это самый прямолинейный подход. Если макетов немного (2-3) и не предполагается их частое добавление, можно создать отдельную команду для каждого макета.
Пошаговая реализация:
Для этого сначала доработаем нашу основную процедуру, чтобы она принимала параметр с именем макета.
&НаКлиенте
Процедура СформироватьПечатнуюФорму(ОбъектыНазначения, ИмяМакета) // Добавили параметр
СсылкаНаОбъект = ОбъектыНазначения[0];
// ... получение параметров ...
СтруктураПараметров = ПолучитьСтруктуруПараметров(СсылкаНаОбъект, п_Организация);
НазваниеФайла = ПолучитьИмяВременногоФайла("doc");
// Теперь имя макета получаем из параметра
МакетОбъект = ПолучитьМакетНаСервере(ИмяМакета);
// ... остальной код без изменений ...
КонецПроцедуры
Теперь создадим обработчики для каждой кнопки:
&НаКлиенте
Процедура ПечатьПоШаблону1(Команда)
МассивДанных = Новый Массив;
МассивДанных.Добавить(Объект.СсылкаНаОбъект);
СформироватьПечатнуюФорму(МассивДанных, "Макет1");
КонецПроцедуры
&НаКлиенте
Процедура ПечатьПоШаблону2(Команда)
МассивДанных = Новый Массив;
МассивДанных.Добавить(Объект.СсылкаНаОбъект);
СформироватьПечатнуюФорму(МассивДанных, "Макет2");
КонецПроцедуры
&НаКлиенте
Процедура ПечатьПоШаблону3(Команда)
МассивДанных = Новый Массив;
МассивДанных.Добавить(Объект.СсылкаНаОбъект);
СформироватьПечатнуюФорму(МассивДанных, "Макет3");
КонецПроцедуры
Плюсы: очень просто и быстро в реализации.
Минусы: загромождает интерфейс кнопками, плохо масштабируется. Если появится четвертый макет, придется снова лезть в конфигуратор и добавлять новую команду и кнопку.
Этот способ гораздо лучше с точки зрения пользовательского опыта. У нас остается одна кнопка "Печать", при нажатии на которую появляется контекстное меню или диалоговое окно со списком доступных макетов.
Для реализации этого воспользуемся клиентскими методами ПоказатьВыборИзСписка или Вопрос.
Пошаговая реализация:
СписокЗначений с названиями макетов.ПоказатьВыборИзСписка, который покажет пользователю модальное окно.СформироватьПечатнуюФорму.Посмотрим на пример кода:
&НаКлиенте
Процедура Печать(Команда)
// Создаем список для выбора
СписокМакетов = Новый СписокЗначений;
СписокМакетов.Добавить("Макет1", "Направление на медосмотр (основной)");
СписокМакетов.Добавить("Макет2", "Направление на медосмотр (сокращенный)");
СписокМакетов.Добавить("Макет3", "Направление для водителей (с доп. пунктами)");
// Создаем описание оповещения, которое сработает после выбора пользователя
Оповещение = Новый ОписаниеОповещения("ПослеВыбораМакета", ЭтотОбъект);
// Показываем пользователю окно выбора
ПоказатьВыборИзСписка(Оповещение, СписокМакетов, , , "Выберите вариант печатной формы:");
КонецПроцедуры
&НаКлиенте
Процедура ПослеВыбораМакета(ВыбранныйЭлемент, ДополнительныеПараметры) Экспорт
// Процедура будет вызвана после того, как пользователь сделает выбор.
// Если пользователь нажал "Отмена", ВыбранныйЭлемент будет Неопределено.
Если ВыбранныйЭлемент = Неопределено Тогда
Возврат; // Ничего не делаем
КонецЕсли;
// ВыбранныйЭлемент - это элемент списка значений. Нас интересует его значение.
ИмяМакета = ВыбранныйЭлемент.Значение;
МассивДанных = Новый Массив;
МассивДанных.Добавить(Объект.СсылкаНаОбъект);
// Вызываем основную процедуру печати с выбранным макетом
СформироватьПечатнуюФорму(МассивДанных, ИмяМакета);
КонецПроцедуры
Плюсы: чистый интерфейс (всего одна кнопка), интуитивно понятно для пользователя, легко добавить новые макеты (просто дописать одну строку в СписокЗначений).
Минусы: немного сложнее в реализации из-за асинхронного вызова.
Этот подход является наиболее правильным, особенно если, помимо выбора макета, нужно задать и другие параметры печати (например, количество копий, ответственное лицо и т.д.). Идея заключается в создании небольшой вспомогательной формы внутри ВПФ.
Пошаговая реализация:
ИмяВыбранногоМакета (тип Строка).Поле выбора с видом Список выбора или Переключатель.Рассмотрим код. Сначала код формы выбора ("ФормаВыбораМакета").
Модуль формы выбора:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Заполняем список выбора для нашего поля
Элементы.ИмяВыбранногоМакета.СписокВыбора.Очистить();
Элементы.ИмяВыбранногоМакета.СписокВыбора.Добавить("Макет1", "Направление (основной)");
Элементы.ИмяВыбранногоМакета.СписокВыбора.Добавить("Макет2", "Направление (сокращенный)");
// Устанавливаем значение по умолчанию
ИмяВыбранногоМакета = "Макет1";
КонецПроцедуры
&НаКлиенте
Процедура Сформировать(Команда)
// Закрываем форму и возвращаем выбранное значение
Закрыть(ИмяВыбранногоМакета);
КонецПроцедуры
Теперь код в модуле основной формы ВПФ:
&НаКлиенте
Процедура Печать(Команда)
// Открываем нашу форму выбора модально
РезультатВыбора = ОткрытьФормуМодально(ЭтаФорма.Имя + "/Форма/ФормаВыбораМакета");
// Если пользователь что-то выбрал и нажал "Сформировать"
Если НЕ РезультатВыбора = Неопределено Тогда
ИмяМакета = РезультатВыбора;
МассивДанных = Новый Массив;
МассивДанных.Добавить(Объект.СсылкаНаОбъект);
СформироватьПечатнуюФорму(МассивДанных, ИмяМакета);
КонецЕсли;
КонецПроцедуры
Этот подход особенно хорош при регистрации ВПФ в системе через механизм "Дополнительные отчеты и обработки". Можно указать, что назначение обработки — "ОткрытиеФормы", и тогда платформа сама будет управлять вызовом. Это соответствует лучшим практикам разработки на базе Библиотеки стандартных подсистем (БСП).
Плюсы: максимальная гибкость и масштабируемость, разделение логики и интерфейса, возможность добавления любых настроек печати, профессиональный внешний вид.
Минусы: требует больше всего времени на разработку по сравнению с другими способами.
При работе с COMObject("Word.Application") критически важно обеспечить корректное завершение работы приложения Word, особенно в случае ошибок. Если этого не сделать, в диспетчере задач могут оставаться "зависшие" процессы winword.exe, которые потребляют ресурсы и могут блокировать файлы.
Всегда оборачивайте код работы с COM-объектом в конструкцию Попытка...Исключение. В блоке Исключение обязательно вызывайте метод mWordObject.Application.Quit(), чтобы принудительно закрыть приложение Word и освободить ресурсы.