Вопросы безопасности пока опустим, это тема для отдельных разговоров, но это решение позволяло нам через GPRS соединение в филиале обновлять выборочные печатный формы «на фоне», не останавливая работу в 1С и не отвлекая филиал от работы. Им даже никаких действия для обновления предпринимать не надо, по мере обновления файлов в папке, формы автоматические будут подхватываться.
Описывая далее наше решение, следует помнить, что в разработке собственной конфигурации мы умышленно не используем «библиотеку стандартных процедур», дабы не увеличивать размер и нагрузку для конфигурации филиала.
Конечно, некоторые общие модули кастрировав, мы все же перенесли в свою конфигурацию (например: «АдресныйКлассификатор, СтроковыеФункцииКлиентСервер»).
И так план решения поставленной задачи:
- Выделяем каталог где хранятся печатные внешние формы. В нашем случае: «%КаталогБД%/ExtForms/PrnForms»
- В данный каталог складываем одноимённые файлы печатной формы ERF и XML с описанием формы, например: «ЗакупочныйАкт.erf» и «ЗакупочныйАкт.xml»
- При открытии любой формы документов (справочников) собираются список печатных форм из файлов XML и создаются на форме соответствующие кнопки печать.
- При старте формирования печатной формы, загружается внешняя печатная форма и открывается ее форма.
Реализуем:
1. Этап
Создаем константу для хранения каталога печатных форм: «КаталогПечатныхФорм». Тип(«Строка») . Длина(255). Ее, кстати, заполнять не обязательно, если не заполнена будем использовать условленный в плане путь «по умолчанию».
Создаем Общий модуль «_ВнешниеПечатныеФормы» доступный с клиентов и с сервера.
Добавляем функцию:
//Получает каталог Печатных форм
Функция КаталогПечатныхФорм() Экспорт
СтрКаталогПечатныхФорм = СокрЛП(ОбщегоНазначения.ЗначениеКонстантыПоИмени("КаталогПечатныхФорм"));
#Если ВебКлиент Тогда
Если ПустаяСтрока(СтрКаталогПечатныхФорм) Тогда
СтрКаталогПечатныхФорм = "D:\Filial82\ExtForms\PrnForms\";
КонецЕсли;
Возврат СтрКаталогПечатныхФорм;
#Иначе
Если ПустаяСтрока(СтрКаталогПечатныхФорм) Тогда
СтрКаталогПечатныхФорм = НСтр(СтрокаСоединенияИнформационнойБазы(), "File");
Если ЗначениеЗаполнено(СтрКаталогПечатныхФорм) Тогда //Файловая версия
Возврат СтрКаталогПечатныхФорм + ?(Прав(СтрКаталогПечатныхФорм,1)="\","","\")+"ExtForms\PrnForms\";
Иначе
СтрКаталогПечатныхФорм = КаталогПрограммы();
Возврат СтрКаталогПечатныхФорм + ?(Прав(СтрКаталогПечатныхФорм,1)="\","","\")+"ExtForms\PrnForms\";
КонецЕсли;
Иначе
Возврат СтрКаталогПечатныхФорм;
КонецЕсли;
#КонецЕсли
КонецФункции
2. Этап
Определяем для себя формат файла XML
Например для печатной формы "Закупочный акт" создаем файл: «ЗакупочныйАкт.xml»
<?xml version="1.0" encoding="windows-1251"?>
<ПечатнаяФорма ИД="ЗакупочныйАкт" ИмяФормы="ЗакупочныйАкт" ИмяВСпискеМеню="Закупочный акт" ИмяКнопки="Закупочный акт" ОтображатьКнопку="Авто" Иконка="Печать">
<СсылкаНаОбъект>ДокументСсылка.ЗакупочныйАкт</СсылкаНаОбъект>
</ПечатнаяФорма>
Добавляем в общий модуль «_ВнешниеПечатныеФормы» функцию получения массива печатных форм для объекта
//Возвращает массив печатных форм
//Пример1: ПолучитьМассивПечатныхФорм("ДокументСсылка","РКО");
//Пример2: ПолучитьМассивПечатныхФорм("СправочникСсылка","Контрагенты");
Функция ПолучитьМассивПечатныхФорм(стрТипОбъекта,стрВидОбъекта) Экспорт
Перем МассивПечатныхФорм;
МассивПечатныхФорм=Новый Массив;
КаталогПечатныхФорм = Новый Файл(КаталогПечатныхФорм());
Если НЕ КаталогПечатныхФорм.Существует() Тогда
Сообщить("Каталог печатных форм не существует: "+КаталогПечатныхФорм());
Возврат МассивПечатныхФорм;
КонецЕсли;
Для каждого ФайлXML из НайтиФайлы(КаталогПечатныхФорм.ПолноеИмя,"*.xml",Ложь)
Цикл
Попытка
ФайлERF=Новый Файл(Сред(ФайлXML.ПолноеИмя,1,СтрДлина(ФайлXML.ПолноеИмя)-4)+".erf");
Если НЕ ФайлERF.Существует() Тогда
Продолжить;
КонецЕсли;
ЧитатьХМЛ = Новый ЧтениеXML;
ЧитатьХМЛ.ОткрытьФайл(ФайлXML.ПолноеИмя);
ПостроитьДУМ = Новый ПостроительDOM;
ДокументДУМ = ПостроитьДУМ.Прочитать(ЧитатьХМЛ);
КорневойУзел = ДокументДУМ.ПервыйДочерний;
Если НЕ КорневойУзел.ЕстьАтрибуты() Тогда
Продолжить;
КонецЕсли;
Если КорневойУзел.ЕстьДочерниеУзлы() Тогда
Для Каждого УзелСсылкаНаОбъект ИЗ КорневойУзел.ПолучитьЭлементыПоИмени("СсылкаНаОбъект") Цикл
Если (стрТипОбъекта+"."+стрВидОбъекта = УзелСсылкаНаОбъект.ТекстовоеСодержимое) Тогда
СтруктураДанныхВМассив = Новый Структура("ТипОбъекта,ВидОбъекта,ФайлПолныйПуть,ИД,ИмяФормы,ИмяВСпискеМеню,ИмяКнопки,ОтображатьКнопку,Иконка");
СтруктураДанныхВМассив.ТипОбъекта = стрТипОбъекта;
СтруктураДанныхВМассив.ВидОбъекта = стрВидОбъекта;
СтруктураДанныхВМассив.ФайлПолныйПуть = ФайлERF.ПолноеИмя;
СтруктураДанныхВМассив.ИД = КорневойУзел.ПолучитьАтрибут("ИД");
СтруктураДанныхВМассив.ИмяФормы = КорневойУзел.ПолучитьАтрибут("ИмяФормы");
СтруктураДанныхВМассив.ИмяВСпискеМеню = КорневойУзел.ПолучитьАтрибут("ИмяВСпискеМеню");
СтруктураДанныхВМассив.ИмяКнопки = КорневойУзел.ПолучитьАтрибут("ИмяКнопки");
СтруктураДанныхВМассив.ОтображатьКнопку = КорневойУзел.ПолучитьАтрибут("ОтображатьКнопку");
СтруктураДанныхВМассив.Иконка = КорневойУзел.ПолучитьАтрибут("Иконка");
МассивПечатныхФорм.Добавить(СтруктураДанныхВМассив);
Прервать;
КонецЕсли;
КонецЦикла;
КонецЕсли;
ПостроитьДУМ = Неопределено;
ЧитатьХМЛ.Закрыть();
Исключение
КонецПопытки;
КонецЦикла;
Возврат МассивПечатныхФорм;
КонецФункции
Тут конечно можно все оптимизировать… но это я оставляю для вашего творчества 😉
3. Этап
Добавляем в модуль «_ВнешниеПечатныеФормы» процедуру генерации кнопок «Печать», вызываемую с формы документа на основании полученного массива XML из 2 этапа
Процедура СоздатьКнопкиПечатныхФорм(массивПечФорм, ЭтаФорма, Элементы, Команды) Экспорт
текПечФорм = Новый Cтруктура("ТипОбъекта,ВидОбъекта,ФайлПолныйПуть,ИД,ИмяФормы,ИмяВСпискеМеню,ИмяКнопки,ОтображатьКнопку,Иконка");
СписокДобавленныхКоманд= Новый Массив;
Если массивПечФорм.ВГраница()>-1 Тогда
Для Каждого текПечФорм Из массивПечФорм Цикл
Если (текПечФорм.ОтображатьКнопку="Всегда") ИЛИ (массивПечФорм.ВГраница()=0) Тогда
КомандаФормы = Команды.Добавить(Строка(текПечФорм.ИД));
КомандаФормы.Действие = "ПроцедураОбработкиНажатияПечать";
ТекКнопка = Элементы.Добавить(Строка(текПечФорм.ИД),Тип("КнопкаФормы"),ЭтаФорма.КоманднаяПанель);
ТекКнопка.Заголовок = текПечФорм.ИмяКнопки;
Если ЗначениеЗаполнено(текПечФорм.Иконка) Тогда
ТекКнопка.Картинка = ПолучитьИконкуКнопки(текПечФорм.Иконка);
ТекКнопка.Отображение = ОтображениеКнопки.КартинкаИТекст;
Иначе
ТекКнопка.Отображение = ОтображениеКнопки.Текст;
КонецЕсли;
ТекКнопка.ИмяКоманды = Строка(текПечФорм.ИД);
КонецЕсли;
КонецЦикла;
КонецЕсли;
Если массивПечФорм.ВГраница()>0 Тогда
ГруппаПечать = Элементы.Добавить("ГруппаПечать",Тип("ГруппаФормы"),ЭтаФорма.КоманднаяПанель);
ГруппаПечать.Заголовок="Печать...";
Для Каждого текПечФорм Из массивПечФорм Цикл
КомандаФормы = Команды.Добавить(Строка(текПечФорм.ИД)+"Список");
КомандаФормы.Действие = "ПроцедураОбработкиНажатияПечать";
ТекКнопка = Элементы.Добавить(Строка(текПечФорм.ИД)+"Список",Тип("КнопкаФормы"),ГруппаПечать);
ТекКнопка.Заголовок = текПечФорм.ИмяВСпискеМеню;
Если ЗначениеЗаполнено(текПечФорм.Иконка) Тогда
ТекКнопка.Картинка = ПолучитьИконкуКнопки(текПечФорм.Иконка);
ТекКнопка.Отображение = ОтображениеКнопки.КартинкаИТекст;
Иначе
ТекКнопка.Отображение = ОтображениеКнопки.Текст;
КонецЕсли;
ТекКнопка.ИмяКоманды = Строка(текПечФорм.ИД)+"Список";
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Сюда же прикладываем небольшую заглушку для подхвата иконок на кнопки "Печать"
Функция ПолучитьИконкуКнопки(ИмяИконки)
Попытка
Если ИмяИконки="Файл" Тогда
Возврат БиблиотекаКартинок.Печать; //Пока так
Иначе
Возврат БиблиотекаКартинок[ИмяИконки];
КонецЕсли;
Исключение
Возврат БиблиотекаКартинок.Печать;
КонецПопытки;
КонецФункции
4. Этап
Теперь во всех формах документов добавляем следующий код:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
//необходимо для генерации кнопок печать на форме
МассивПечатныхФорм=_ВнешниеПечатныеФормы.ПолучитьМассивПечатныхФорм("ДокументСсылка",Объект.Ссылка.Метаданные().Имя);
_ВнешниеПечатныеФормы.СоздатьКнопкиПечатныхФорм(МассивПечатныхФорм,ЭтаФорма,Элементы,Команды);
КонецПроцедуры
//ВНЕШНИЕ ПЕЧАТНЫЕ ФОРМЫ
&НаКлиенте
Функция ПроверкаНаЗаписьПередПечатью(ЭтаФорма) Экспорт
//тут ваши проверки перед печатью
//…
Возврат Истина; //или Ложь если не разрешить печать
КонецФункции
&НаСервере
Функция ПодключитьВнешнююОбработку(АдресХранилища)
Возврат ВнешниеОтчеты.Подключить(АдресХранилища,,Ложь);
КонецФункции
&НаСервере
Функция ПолучитьМассив()
Возврат _ВнешниеПечатныеФормы.ПолучитьМассивПечатныхФорм("ДокументСсылка",Объект.Ссылка.Метаданные().Имя);
КонецФункции
&НаКлиенте
Процедура ПроцедураОбработкиНажатияПечать(Элемент)
Если НЕ Объект.Проведен Тогда
Сообщить("Перед выводом на печать необходимо провести документ.");
Возврат;
КонецЕсли;
МассивПечатныхФорм = ПолучитьМассив();
ИмяВызваннойКнопки = Элемент.Имя;
АдресХранилища = "";
Для Каждого элМассиваПечФорм из МассивПечатныхФорм Цикл
Если (ИмяВызваннойКнопки = элМассиваПечФорм.ИД) или (ИмяВызваннойКнопки = (элМассиваПечФорм.ИД + "Список"))Тогда
Если НЕ ПроверкаНаЗаписьПередПечатью(ЭтаФорма) Тогда
Возврат;
КонецЕсли;
ДополнительныеПараметры = Неопределено;//В некоторых документах возможно тут нужны дополнительные параметры
Результат = ПоместитьФайл(АдресХранилища, элМассиваПечФорм.ФайлПолныйПуть, , Ложь);
ИмяОбработки = ПодключитьВнешнююОбработку(АдресХранилища);
ФормаОтчета= ПолучитьФорму("ВнешнийОтчет." + ИмяОбработки + ".Форма");
Попытка
ФормаОтчета.Отчет.СсылкаНаОбъект = Объект.Ссылка;
Исключение
Сообщить("Не удалось установить ссылку на объект в печатной форме.");
Возврат;
КонецПопытки;
Если НЕ ДополнительныеПараметры = Неопределено Тогда
Попытка
ФормаОтчета.Отчет.ДополнительныеПараметры = ДополнительныеПараметры;
Исключение
//ну и не надо значит
КонецПопытки;
Конецесли;
ФормаОтчета.Открыть();
КонецЕсли;
КонецЦикла;
КонецПроцедуры
В некоторых случаях можно добавить реквизит «ДополнительныеПараметры» с любым типом, чтобы получать дополнительный параметр при вызове из формы документа.
В модуль объекта добавляем экспортную процедуру «Печать»:
Функция Печать() Экспорт
ТабДокумент = Новый ТабличныйДокумент
//….
Возврат ТабДокумент;
КонецФункции
И в модуль формы внешней печатной формы добавляем:
&НаСервере
Функция ВызватьПечать()
ЭтотОбъект = РеквизитФормыВЗначение("Отчет");
ТабДок = ЭтотОбъект.Печать();
Возврат ТабДок;
КонецФункции
&НаКлиенте
Процедура ПриОткрытии(Отказ)
ТабДок = ВызватьПечать();
ТабДок.ОтображатьСетку = Ложь;
ТабДок.Защита = Ложь;
ТабДок.ТолькоПросмотр = Ложь;
ТабДок.ОтображатьЗаголовки = Ложь;
ТабДок.ОриентацияСтраницы = ОриентацияСтраницы.Портрет;
ТабДок.АвтоМасштаб = Истина;
ТабДок.Показать();
Отказ = Истина;
КонецПроцедуры
В принципе готово.
В справочниках все тоже самое, только вместо слов «ДокументСсылка» ставим «СправочникСсылка».
Работает эта система уже несколько лет, с 2013 года без сбоев. А если учесть то, что печатные формы, из-за постоянного изменения законодательства, приходится менять(добавлять) чуть ли не квартал, то такой подход сохранил нам много времени на обновлениях при 160 филиалах.