При разработке команд печати с использованием Библиотеки стандартных подсистем (БСП) программисты часто сталкиваются с ситуацией, когда логику печати необходимо реализовать на стороне клиента. Это требуется в случаях, когда нужно выгрузить данные во временный файл, запустить внешнее приложение или взаимодействовать с локальным оборудованием пользователя. Согласно стандартам БСП, мы регистрируем команду в модуле менеджера, указывая тип обработчика "Форма".
Проблема заключается в том, что такая команда автоматически появляется не только в форме самого документа, но и в форме списка. Система ожидает, что в модуле каждой из этих форм будет определена соответствующая экспортная процедура. В результате разработчик вынужден копировать один и тот же объемный код в два (или более) места, что нарушает принцип DRY (Don't Repeat Yourself) и усложняет дальнейшую поддержку. Рассмотрим, как решить эту задачу правильно и эффективно.
Самым архитектурно верным и рекомендуемым способом является вынос всей клиентской логики в отдельный общий модуль. Многие разработчики опасаются создавать общий модуль ради одного документа, однако в масштабах развивающейся системы это гораздо лучше, чем дублирование кода в формах.
Проанализируем алгоритм действий:
Расш1_ПечатьДокументовКлиент) с установленными флажками Клиент (управляемое приложение).ЭтаФорма) и массив объектов для печати.В модуле менеджера документа при регистрации команды мы по-прежнему указываем обработчик в форме:
// В модуле менеджера
Процедура ДобавитьКомандыПечати(КомандыПечати) Экспорт
Команда = КомандыПечати.Добавить();
Команда.Идентификатор = "МояКомандаПечати";
Команда.Представление = НСтр("ru = 'Печать с внешней обработкой'");
Команда.Обработчик = "Продажи_Клиент_ПечатьВнешняя"; // Имя процедуры в формах
Команда.СписокФорм = "ФормаОбъекта,ФормаСписка";
КонецПроцедуры
Теперь в модулях обеих форм (объекта и списка) нам достаточно написать короткий вызов:
&НаКлиенте
Процедура Продажи_Клиент_ПечатьВнешняя(ПараметрыПечати) Экспорт
// Вызываем единый обработчик из общего модуля
Расш1_ПечатьДокументовКлиент.ВыполнитьПечать(ЭтотОбъект, ПараметрыПечати);
КонецПроцедуры
Такой подход позволяет хранить сложный код (например, работу с ЗапуститьПриложение или асинхронными вызовами НачатьПомещениеФайла) в одном месте.
Если по каким-то причинам вы не хотите создавать общий модуль, существует способ заставить форму списка "притвориться" формой объекта и вызвать ее методы. Рассмотрим ситуацию, когда код уже написан в форме документа, и мы хотим вызвать его из формы списка, используя выделенные строки.
Для этого нам потребуется написать универсальную процедуру-прокладку. Выясним причину, почему прямой вызов невозможен: модули форм в 1С инкапсулированы, и мы не можем просто обратиться к методу другой формы. Однако мы можем программно получить форму объекта по ссылке и инициировать выполнение команды через механизм БСП ПодключаемыеКомандыКлиент.
Проанализируем пример реализации такой функции в общем модуле или в модуле формы списка:
&НаКлиенте
Процедура ВыполнитьКомандуПечатиДляВыделенныхСтрок(Знач ФормаСписка, Знач ПараметрыПечати) Экспорт
Перем ИДСтроки, ИмяФормыОбъекта, КомандаФормыОбъекта, ТекущиеДанные, ФормаОбъекта;
Для Каждого ИДСтроки Из ФормаСписка.Элементы.Список.ВыделенныеСтроки Цикл
ТекущиеДанные = ФормаСписка.Элементы.Список.ДанныеСтроки(ИДСтроки);
Если ТекущиеДанные <> Неопределено Тогда
// Определяем полное имя формы объекта динамически
Если ИмяФормыОбъекта = Неопределено Тогда
// Здесь можно использовать стандартную функцию БСП для получения имени метаданных
ИмяФормыОбъекта = ПолучитьИмяФормыОбъекта(ТекущиеДанные.Ссылка);
КонецЕсли;
// Получаем форму объекта (без открытия пользователю)
ПараметрыФормы = Новый Структура("Ключ", ТекущиеДанные.Ссылка);
ФормаОбъекта = ПолучитьФорму(ИмяФормыОбъекта, ПараметрыФормы);
// Находим команду в форме объекта по идентификатору из БСП
ИмяКомандыВФорме = НайтиИмяКомандыПоИдентификатору(ФормаОбъекта, ПараметрыПечати.Идентификатор);
КомандаФормыОбъекта = ФормаОбъекта.Команды.Найти(ИмяКомандыВФорме);
Если КомандаФормыОбъекта <> Неопределено Тогда
// Выполняем команду контекстно, как будто нажал пользователь
ПодключаемыеКомандыКлиент.ВыполнитьКоманду(ФормаОбъекта, КомандаФормыОбъекта, ФормаОбъекта.Объект);
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Важное предупреждение: Метод ПолучитьФорму в цикле является ресурсоемким. При инициализации формы происходит обращение к серверу и выполнение серверных событий (ПриСозданииНаСервере, ПриЧтенииНаСервере). Если пользователь выделит 100 строк в списке, система выполнит 100 серверных вызовов, что приведет к значительным задержкам. Данный метод стоит использовать только в исключительных случаях или при гарантированно малом количестве обрабатываемых объектов.
В современных версиях БСП (начиная с 3.1.10) структура ПараметрыПечати была изменена, и найти имя экспортной процедуры в форме стало сложнее. Чтобы программно связать команду в списке с командой в объекте, можно воспользоваться служебными данными, которые БСП хранит в параметрах формы.
Разберем, как получить внутреннее имя команды по ее идентификатору, используя временное хранилище БСП:
&НаСервереБезКонтекста
Функция ИмяВФормеПоИдентификатору(Знач АдресНастроек, Знач Идентификатор) Экспорт
// Получаем таблицу команд из временного хранилища БСП
Команды = ПолучитьИзВременногоХранилища(АдресНастроек);
СтрокаТаблицы = Команды.Найти(Идентификатор, "Идентификатор");
Если СтрокаТаблицы <> Неопределено Тогда
Возврат СтрокаТаблицы.ИмяВФорме;
КонецЕсли;
Возврат "";
КонецФункции
Этот метод позволяет "прокинуть" вызов из списка в объект, зная только идентификатор команды, который остается неизменным для обеих форм.
Если вы хотите полностью изолировать код печати от конфигурации и избежать изменений в модулях форм, рассмотрим вариант создания Внешней печатной формы (ВПФ). БСП предоставляет мощный механизм для работы с ними.
Преимущества этого подхода:
.epf.Печать в модуле обработки, где в параметрах передается признак клиентского выполнения.Проанализируем структуру процедуры во внешней обработке:
&НаКлиенте
Процедура ВыполнитьКоманду(ИдентификаторКоманды, ОбъектыНазначения, ПараметрыВыполнения) Экспорт
// Здесь размещается вся клиентская логика
// ОбъектыНазначения — это массив ссылок, выбранных в форме
Для Каждого Ссылка Из ОбъектыНазначения Цикл
// Обработка каждого документа
КонецЦикла;
КонецПроцедуры
Для эффективной работы с клиентской печатью в БСП и исключения дублирования кода мы рекомендуем:
ПолучитьФорму в циклах по списку документов, чтобы не перегружать сервер.Соблюдение этих правил сделает ваш код более читаемым, а систему — производительной и легкой в обновлении.