Часто возникает задача: пользователь нажимает кнопку «Печать», но перед тем как сформировать документ, нам нужно спросить у него дополнительные данные. Например, выбрать период, за который нужно распечатать пакет документов, указать подписанта или выбрать вариант оформления.
Если мы работаем в современных конфигурациях (например, «1С:Бухгалтерия 3.0», ERP, ЗУП), то логика печати обычно находится в модуле менеджера объекта. И здесь мы сталкиваемся с фундаментальным ограничением платформы: модуль менеджера выполняется на сервере, а любые диалоговые окна (формы) могут быть открыты только на клиенте.
Разберем подробно, как обойти это ограничение, используя механизм Библиотеки Стандартных Подсистем (БСП) и расширения конфигурации. Мы рассмотрим реальный пример: печать путевых листов на неделю вперед, где пользователю нужно выбрать дату начала периода.
Давайте посмотрим на типичную ошибку начинающего разработчика. Мы находимся в модуле менеджера документа, в процедуре печати, и пытаемся сделать так:
// ОШИБОЧНЫЙ КОД в модуле менеджера
Форма = Документы.ПутевойЛист.ПолучитьФорму("ФормаВыбораПериода");
Результат = Форма.ОткрытьМодально(); // Или просто Открыть()
При выполнении этого кода система выдаст ошибку: «Интерактивные операции недоступны».
Причины две:
ОткрытьМодально) в современном интерфейсе «Такси» не рекомендуется или вовсе запрещено (режим использования модальности).В типовых конфигурациях печать организована через подсистему «Печать». Схема работы выглядит так:
Печать, формируется табличный документ.Чтобы вклиниться в этот процесс и задать вопрос пользователю, нам нужно подменить стандартный клиентский обработчик на шаге 2. Мы сделаем это через расширение конфигурации.
Сначала нам нужно перехватить управление в момент регистрации команд печати. В модуле менеджера документа (в расширении) используем аннотацию &После для процедуры ДобавитьКомандыПечати.
Нам нужно найти команду печати нашего путевого листа и указать, что её обработкой займется наш специальный клиентский модуль.
&После("ДобавитьКомандыПечати")
Процедура Расш_ДобавитьКомандыПечати(КомандыПечати)
// Ищем нашу команду по идентификатору
// Обычно идентификатор совпадает с именем макета или задается явно
Для Каждого КомандаПечати Из КомандыПечати Цикл
Если КомандаПечати.Идентификатор = "ПФ_MXL_ПутевойЛист" Тогда
// ПОДМЕНА ОБРАБОТЧИКА
// Указываем путь к экспортной функции в НАШЕМ общем клиентском модуле
КомандаПечати.Обработчик = "Расш_КлиентскийМодульПечати.ОткрытьДиалогИНапечатать";
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Обратите внимание: мы указываем обработчик как строку. Это имя функции, которая будет вызвана на клиенте при нажатии кнопки.
Теперь создадим в расширении Общий модуль. Назовем его, например, Расш_КлиентскийМодульПечати. Обязательно установим галочки:
В этом модуле мы реализуем логику открытия окна. Так как модальность запрещена, мы будем использовать асинхронную модель через ОписаниеОповещения.
Логика следующая:
Рассмотрим код клиентского модуля:
// Функция-точка входа. Именно ее мы прописали в Обработчик.
// Важно: БСП ожидает, что это будет Функция, даже если она ничего не возвращает.
Функция ОткрытьДиалогИНапечатать(КомандаПечати) Экспорт
// Подготовим параметры для передачи "сквозь" диалог.
// Нам нужно сохранить КомандаПечати, чтобы потом продолжить печать.
ПараметрыОповещения = Новый Структура;
ПараметрыОповещения.Вставить("КомандаПечати", КомандаПечати);
// Создаем описание оповещения.
// "ОбработкаВыбораПериода" - это имя процедуры в ЭТОМ же модуле, которая сработает после закрытия окна.
Оповещение = Новый ОписаниеОповещения("ОбработкаВыбораПериода", ЭтотОбъект, ПараметрыОповещения);
// Можно использовать стандартную общую форму "ВводПериода" или любую свою
// Пример для стандартной формы выбора периода (если она есть в конфигурации)
// Или можно открыть простую форму диалога
ОткрытьФорму("ОбщаяФорма.ВыборПериода", , , , , , Оповещение, РежимОткрытияОкнаФормы.БлокироватьОкноВладельца);
КонецФункции
// Процедура, которая сработает после выбора пользователя
Процедура ОбработкаВыбораПериода(Результат, ДополнительныеПараметры) Экспорт
// Результат - это то, что вернула форма выбора (обычно Структура с датами или Неопределено при отмене)
Если Результат = Неопределено Тогда
// Пользователь нажал "Отмена" или закрыл крестиком
Возврат;
КонецЕсли;
// Восстанавливаем нашу команду печати
КомандаПечати = ДополнительныеПараметры.КомандаПечати;
// САМОЕ ГЛАВНОЕ:
// Помещаем выбранный период в ДополнительныеПараметры команды.
// Эти параметры "уедут" на сервер в модуль менеджера.
// Проверяем, есть ли уже такая структура (на всякий случай)
Если КомандаПечати.ДополнительныеПараметры = Неопределено Тогда
КомандаПечати.ДополнительныеПараметры = Новый Структура;
КонецЕсли;
// Предположим, Результат - это структура с полями НачДата и КонДата
КомандаПечати.ДополнительныеПараметры.Вставить("ПериодНачало", Результат.НачалоПериода);
КомандаПечати.ДополнительныеПараметры.Вставить("ПериодКонец", Результат.КонецПериода);
// Возвращаем управление в стандартный механизм печати БСП
// В БП 3.0 модуль обычно называется УправлениеПечатьюБЗККлиент или УправлениеПечатьюКлиент
УправлениеПечатьюКлиент.ВыполнитьКомандуПечати(КомандаПечати);
КонецПроцедуры
Теперь данные с клиента летят на сервер. Нам нужно вернуться в модуль менеджера документа (в расширении) и доработать процедуру Печать.
Сигнатура процедуры печати в БСП выглядит примерно так:
Процедура Печать(МассивОбъектов, ПараметрыПечати, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
Именно в аргументе ПараметрыПечати (это Структура) будут лежать наши добавленные свойства ДополнительныеПараметры.
Рассмотрим, как извлечь эти данные и сформировать пакет путевых листов:
&После("Печать")
Процедура Расш_Печать(МассивОбъектов, ПараметрыПечати, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода)
// Проверяем, наш ли это макет
Если ПараметрыПечати.Идентификатор <> "ПФ_MXL_ПутевойЛист" Тогда
Возврат;
КонецЕсли;
// Пытаемся извлечь наши даты
ДатаНачала = Неопределено;
Если ПараметрыПечати.ДополнительныеПараметры.Свойство("ПериодНачало", ДатаНачала) Тогда
// Если параметр передан, значит печать была вызвана через наш диалог.
// Здесь мы можем реализовать логику печати 7 путевых листов.
// Найдем печатную форму, которую уже, возможно, создал стандартный механизм,
// или создадим свою новую коллекцию
ПечатнаяФорма = УправлениеПечатью.СведенияОПечатнойФорме(КоллекцияПечатныхФорм, "ПФ_MXL_ПутевойЛист");
Если ПечатнаяФорма <> Неопределено Then
// Очищаем стандартный табличный документ, так как мы будем его перезаполнять
ПечатнаяФорма.ТабличныйДокумент.Очистить();
// Получаем макет
Макет = УправлениеПечатью.МакетПечатнойФормы("Документ.ПутевойЛист.ПФ_MXL_ПутевойЛист");
// Цикл на неделю вперед
ТекущаяДатаПечати = ДатаНачала;
Для Счетчик = 1 По 7 Цикл
// Здесь ваша логика заполнения областей макета.
// Данные берем из ссылок в МассивОбъектов
// А дату подставляем ТекущаяДатаПечати
Область = Макет.ПолучитьОбласть("Заголовок");
Область.Параметры.ДатаДокумента = ТекущаяДатаПечати;
// ... заполнение остальных параметров ...
ПечатнаяФорма.ТабличныйДокумент.Вывести(Область);
// Разделяем листы, чтобы каждый день был на новой странице
ПечатнаяФорма.ТабличныйДокумент.ВывестиГоризонтальныйРазделительСтраниц();
// Переходим к следующему дню
ТекущаяДатаПечати = ТекущаяДатаПечати + 86400; // +1 день в секундах
КонецЦикла;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
1. Безопасный режим расширения.
Если вы используете расширение, убедитесь, что для него отключен «Безопасный режим». В некоторых версиях платформы передача сложных параметров между клиентом и сервером или работа с определенными методами печати может блокироваться в безопасном режиме.
2. Универсальность.
Предложенный метод УправлениеПечатьюКлиент.ВыполнитьКомандуПечати является частью БСП. В зависимости от версии библиотеки (которая вшита в вашу конфигурацию), название клиентского модуля может отличаться. Например:
УправлениеПечатьюКлиентУправлениеПечатьюБЗККлиент (для зарплатных решений)Проверьте, какой обработчик стоял у команды по умолчанию (можно посмотреть в отладчике в процедуре ДобавитьКомандыПечати перед тем как его менять), и вызывайте именно его в конце вашей клиентской процедуры.
3. Генерация нескольких документов.
В примере выше мы выводим 7 путевых листов в один табличный документ, разделяя их разрывами страниц. Это удобно для печати. Если же вам нужно, чтобы это были технически разные печатные формы (например, для отправки по электронной почте по отдельности), вам придется добавлять строки в КоллекцияПечатныхФорм с разными именами файлов, но это уже более сложный сценарий.
Чтобы вызвать форму перед печатью:
ДобавитьКомандыПечати) меняем Обработчик команды на свою клиентскую функцию.ОписаниеОповещения.ДополнительныеПараметры команды и возвращаем управление в БСП.Печать) читаем параметры и формируем документ с учетом выбора пользователя.