Пользовательские макеты для внешних печатных форм (обработок)

Печать - Универсальные печатные формы

БСП пользовательские макеты внешние печатные формы внешние обработки расширения программирование 1С

9
Расширение БСП по реализации механизма пользовательских макетов для обработок внешних печатных форм.

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

Приступим!

Релизацию задачи можно разделить на три шага перехвата и модернизации механизмов:

  1. Заполнения типового списка макетов
  2. Получения макета по имени объекта метаданных 
  3. Записи модернизированного пользовательского макета

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

Разберем каждый из шагов подробнее.

1. Перехват и модернизации механизма заполнения типового списка макетов

Механизм находится в процедуре формы РегистрСведений.ПользовательскиеМакетыПечати.Форма.МакетыПечатныхФорм.ЗаполнитьТаблицуМакетовПечатныхФорм().

Добавляем форму МакетыПечатныхФорм в расширение, в модуле формы прописываем следующий код:

&Перед("ЗаполнитьТаблицуМакетовПечатныхФорм")
Процедура simПМВПФ_ЗаполнитьТаблицуМакетовПечатныхФорм()
	
	//Ищем запросом все неотключенные внешние обработки печатных форм
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	ДополнительныеОтчетыИОбработки.Ссылка КАК Ссылка
		|ИЗ
		|	Справочник.ДополнительныеОтчетыИОбработки КАК ДополнительныеОтчетыИОбработки
		|ГДЕ
		|	ДополнительныеОтчетыИОбработки.Вид = &ВПФ
		|	И ДополнительныеОтчетыИОбработки.Публикация <> &Отключена
		|	И НЕ ДополнительныеОтчетыИОбработки.ПометкаУдаления";
	
	Запрос.УстановитьПараметр("ВПФ"			, Перечисления.ВидыДополнительныхОтчетовИОбработок.ПечатнаяФорма);
	Запрос.УстановитьПараметр("Отключена"	, Перечисления.ВариантыПубликацииДополнительныхОтчетовИОбработок.Отключена);
	
	РезультатЗапроса = Запрос.Выполнить();
	
	ВДЗ = РезультатЗапроса.Выбрать();
	
	Пока ВДЗ.Следующий() Цикл
		
		//Получаем обработку-объект, для доступа к ее макетам, через метаданные
		ОбработкаОбъект 			= ДополнительныеОтчетыИОбработки.ОбъектВнешнейОбработки(ВДЗ.Ссылка);
		ОбъектМетаданныхКоллекции	= ОбработкаОбъект.Метаданные();
		
		//Перебираем макеты
		Для каждого ОбъектМетаданныхМакет из ОбъектМетаданныхКоллекции.Макеты Цикл
			
			//Вызов типовой функции проверки соответствия префикса в макете
			ТипМакета = ТипМакета(ОбъектМетаданныхМакет.Имя);
			Если ТипМакета = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			
			//Добавление макета на форму
			ДобавитьОписаниеМакета(ОбъектМетаданныхКоллекции.ПолноеИмя() + "." + ОбъектМетаданныхМакет.Имя, ОбъектМетаданныхМакет.Синоним, ОбъектМетаданныхКоллекции.Синоним, ТипМакета);
	
		КонецЦикла;
	КонецЦикла;
		
КонецПроцедуры

Схематично вышеприведенный код работает следующим образом:


 

2. Перехват и модернизации механизма получения макета по имени объекта метаданных

Механизм находится в функции ОбщийМодуль.УправлениеПечатью.МакетПечатнойФормы(ПутьКМакету)

Добавляем общий модуль УправлениеПечатью в расширение. В модуле расширения пишем следующий код:


// Возвращает макет печатной формы по полному пути к макету.
//
// Параметры:
//  ПутьКМакету - Строка - полный путь к макету в формате:
//                         "Документ.<ИмяДокумента>.<ИмяМакета>"
//                         "Обработка.<ИмяОбработки>.<ИмяМакета>"
//                         "ОбщийМакет.<ИмяМакета>".
//						   "ВнешняяОбработка.<ИмяОбработки>.<ИмяМакета>"
// Возвращаемое значение:
//  ТабличныйДокумент - для макета типа MXL.
//  ДвоичныеДанные    - для макетов DOC и ODT.
//
&Вместо("МакетПечатнойФормы")
Функция simПМВПФ_МакетПечатнойФормы(ПутьКМакету) Экспорт
	
	//Вычленим старший тип объекта метаданных
	ЧастиПути = СтрРазделить(ПутьКМакету, ".", Истина);
	
	//Нас интересуют только внешние обработки
	Если ЧастиПути[0]="ВнешняяОбработка" Тогда
		
		//Типовой код
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Макет ""%1"" не найден. Операция прервана.'"), ПутьКМакету);
		
		ИмяМакета = ЧастиПути[ЧастиПути.ВГраница()];
		ЧастиПути.Удалить(ЧастиПути.ВГраница());
		ИмяОбъекта = СтрСоединить(ЧастиПути, ".");
		
		ТекстЗапроса = 
		"ВЫБРАТЬ
		|	ПользовательскиеМакетыПечати.Макет КАК Макет
		|ИЗ
		|	РегистрСведений.ПользовательскиеМакетыПечати КАК ПользовательскиеМакетыПечати
		|ГДЕ
		|	ПользовательскиеМакетыПечати.Объект = &Объект
		|	И ПользовательскиеМакетыПечати.ИмяМакета ПОДОБНО &ИмяМакета
		|	И ПользовательскиеМакетыПечати.Использование";
		
		Запрос = Новый Запрос(ТекстЗапроса);
		Запрос.Параметры.Вставить("Объект", ИмяОбъекта);
		Запрос.Параметры.Вставить("ИмяМакета", "%" + ИмяМакета + "%");
		
		УстановитьОтключениеБезопасногоРежима(Истина);
		УстановитьПривилегированныйРежим(Истина);
		
		Выборка = Запрос.Выполнить().Выбрать();
		
		УстановитьПривилегированныйРежим(Ложь);
		УстановитьОтключениеБезопасногоРежима(Ложь);
		
		//+sim если макет нашелся в регистре - берем его
		Если Выборка.Следующий() Тогда
			Возврат Выборка.Макет.Получить();
		Иначе //иначе получаем макет непосредственно из обработки
			_Макет = simПМВПФ_ПолучитьМакетВнешнейОбработки(ИмяОбъекта,ИмяМакета); 
			Если _Макет <> Неопределено Тогда
				Возврат _Макет;
			Иначе
				ВызватьИсключение ТекстОшибки;
			КонецЕсли;
			
		КонецЕсли;
		//-sim
		
	КонецЕсли;	
	
	//!!!Продолжаем типовой вызов
	Возврат ПродолжитьВызов(ПутьКМакету);
	
КонецФункции


// Возвращает макет внешней печатной формы имени объекта и имени мкета.
//
// Параметры:
//  ПутьКМакету - Строка - полный путь к макету в формате:
//                         "ВнешняяОбработка.<ИмяОбработки>.<ИмяМакета>"
//                         "<ИмяОбработки>.<ИмяМакета>".
// Возвращаемое значение:
//  ТабличныйДокумент 	- для макета типа MXL.
//  ДвоичныеДанные    	- для макетов DOC и ODT.
//  Неопределено		- если макет не найден
&НаСервере
Функция simПМВПФ_ПолучитьМакетВнешнейОбработки(ИмяОбъекта,ИмяМакета)
	
	//ищем запросом нужную нам обработку
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	ДополнительныеОтчетыИОбработки.Ссылка КАК Ссылка
		|ИЗ
		|	Справочник.ДополнительныеОтчетыИОбработки КАК ДополнительныеОтчетыИОбработки
		|ГДЕ
		|	НЕ ДополнительныеОтчетыИОбработки.ПометкаУдаления И
		|	ДополнительныеОтчетыИОбработки.Вид = &ВПФ
		|	И ДополнительныеОтчетыИОбработки.Публикация <> &Отключена
		|	И ДополнительныеОтчетыИОбработки.ИмяОбъекта = &ИмяОбъекта";
	
	Запрос.УстановитьПараметр("ИмяОбъекта"	, СтрЗаменить(ИмяОбъекта,"ВнешняяОбработка.",""));
	Запрос.УстановитьПараметр("ВПФ"			, Перечисления.ВидыДополнительныхОтчетовИОбработок.ПечатнаяФорма);
	Запрос.УстановитьПараметр("Отключена"	, Перечисления.ВариантыПубликацииДополнительныхОтчетовИОбработок.Отключена);
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	РезультатЗапроса = Запрос.Выполнить();	
	
	
	ВДЗ = РезультатЗапроса.Выбрать();
	
	Если ВДЗ.Следующий() Тогда
		
		//получаем обработку-объект
		ОбработкаОбъект = ДополнительныеОтчетыИОбработки.ОбъектВнешнейОбработки(ВДЗ.Ссылка);
		
		УстановитьПривилегированныйРежим(Ложь);
		УстановитьОтключениеБезопасногоРежима(Ложь); 
		
		//получаем требуемый макет
		Возврат ОбработкаОбъект.ПолучитьМакет(ИмяМакета);
					
	КонецЕсли;
	
	Возврат Неопределено;
	
КонецФункции

Схематично вышеприведенный код работает следующим образом:

3. Перехват и модернизации механизма записи модернизированного пользовательского макета

Механизм находится в функции ОбщийМодуль.УправлениеПечатью.ЗаписатьМакет(ИмяОбъектаМетаданныхМакета, АдресМакетаВоВременномХранилище).

Общий модуль УправлениеПечатью мы уже добавили в наше расширение, поэтому сразу добавляем в модуль УравлениеПечатью следующий код:

// Сохраняет пользовательский макет печати в информационной базе.
&Вместо("ЗаписатьМакет")
Процедура simПМВПФ_ЗаписатьМакет(ИмяОбъектаМетаданныхМакета, АдресМакетаВоВременномХранилище) Экспорт
	
	//Вычленим старший тип объекта метаданных
	ЧастиИмени = СтрРазделить(ИмяОбъектаМетаданныхМакета, ".");
	
	//Нас интересуют только внешние обработки
	Если ЧастиИмени[0] = "ВнешняяОбработка" Тогда  
		
		//Типовой код
		ИмяМакета = ЧастиИмени[ЧастиИмени.ВГраница()];
		ИзмененныйМакет = ПолучитьИзВременногоХранилища(АдресМакетаВоВременномХранилище);
		ИмяВладельца = "";
		Для НомерЧасти = 0 По ЧастиИмени.ВГраница()-1 Цикл
			Если Не ПустаяСтрока(ИмяВладельца) Тогда
				ИмяВладельца = ИмяВладельца + ".";
			КонецЕсли;
			ИмяВладельца = ИмяВладельца + ЧастиИмени[НомерЧасти];
		КонецЦикла;
		
		//+sim нетиповое получение макета для ВнешнейОбработки
		МакетИзМетаданных = simПМВПФ_ПолучитьМакетВнешнейОбработки(ИмяВладельца,ИмяМакета);
		//-sim
		
		Запись = РегистрыСведений.ПользовательскиеМакетыПечати.СоздатьМенеджерЗаписи();
		Запись.Объект = ИмяВладельца;
		Запись.ИмяМакета = ИмяМакета;
		Если МакетыРазличаются(МакетИзМетаданных, ИзмененныйМакет) Тогда
			Запись.Использование = Истина;
			Запись.Макет = Новый ХранилищеЗначения(ИзмененныйМакет, Новый СжатиеДанных(9));
			Запись.Записать();
		Иначе
			Запись.Прочитать();
			Если Запись.Выбран() Тогда
				Запись.Удалить();
			КонецЕсли;
		КонецЕсли;
		
		Возврат;
		
	КонецЕсли;
	
	//!!! продолжаем вызов тиоповой процедуры
	ПродолжитьВызов(ИмяОбъектаМетаданныхМакета, АдресМакетаВоВременномХранилище);
	
КонецПроцедуры

Схема работы кода выглядит аналогично п.2.

4. Особенности печати пользовательского макета для внешней печатной формы

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

  1. Имя каждой отдельной внешней обработки должно быть уникально
  2. Макеты, которые вы хотите дать на редактирование пользователям, должны префиксироваться согласно правилам БСП (ПФ_MXL_,ПФ_DOC_)
  3. Получать макет необходимо функцией БСП УправлениеПечатью.МакетПечатнойФормы(ПутьКМакету)
  4. Получая область макета для печати, необходимо сначала проверить ее наличие (пользователь мог ее удалить) методом Найти(ИмяОбласти)
    Макет = ЭтотОбъект.ПолучитьМакет("СоставПоказателей");
    ТекОбласть = Макет.Области.Найти(ИмяОбласти);
  5. Заполнять параметры макета следует процедурой ЗаполнитьЗначенияСвойств(Область.Параметры, Источник), ровно по той же причине (пользователь может удалить параметры)

Вот, собственно, и все. Буду раз вашим комментариям.

Системные требования

Платформа: не ниже v8.3.11

БСП: проверял на v2.4.4.145, если потестируете на других версиях, будет очень здорово.

9

Скачать файлы

Наименование Файл Версия Размер
Расширение для конфигураций на БСП 2.4.4.х
.cfe 21,28Kb
05.03.18
1
.cfe 1.0.0 21,28Kb 1 Скачать
Печать счета из заказа клиента (УТ 11.4)
.epf 13,81Kb
05.03.18
2
.epf 13,81Kb 2 Скачать

См. также

Комментарии
Избранное Подписка Сортировка: Древо
1. simy4 8 06.03.18 11:10 Сейчас в теме
Мысли об оптимизации.
В этой (1.0) версии, для получения макета непосредственно из внешней обработки, выполнятся запрос к базе данных, хотя контекст, из которого вызывается функция МакетПечатнойФормы(), в большинстве случаев уже несет в себе ОбработкуОбъект.

Можно написать отдельную функцию получения макета, где входящим параметром нужно передавать ОбработкуОбъект, но такое решение усложняет, т.к. необходимо во внешних обработках переписывать место получения макета.

Если бы расширение позволяло добавлять в перехватываемую функцию необязательные параметры...
Смотрю в сторону дополнительных свойств ОбработкиОбъекта. Тогда тем, кому важна временнАя оптимизация получения макета могли бы добавлять ОбработкуОбъект в дополнительные свойства перед вызовом функции МакетПечатнойФормы().
2. simy4 8 06.03.18 19:28 Сейчас в теме
(1)нет, не выйдет ничего с дополнительными свойствами, т.к. они живут только вместе с ОбработкойОбъектом, а её (кажется) можно передать только параметром.
3. ybrjkm 29.08.18 13:33 Сейчас в теме
Добрый день! Есть внешняя печатная обработка, есть макет в формате "Двоичные данные" - документ "Word". Макет попадает в "Макеты печатных форм", редактируется, сохраняется. Но при печати выводится старый, неизмененный макет из внешней обработки, хотя отладчиком я попадаю на строку Возврат Выборка.Макет.Получить(); функции simПМВПФ_МакетПечатнойФормы() (то есть в регистр "Пользовательские макеты печати") я попадаю. Что здесь не так? Может, что-то мною упущено? Спасибо.
8. simy4 8 28.09.18 15:07 Сейчас в теме
(3)Добрый день. К сожалению не смогу вам помочь. Слишком мало данных для анализа. Может стоит попробовать почистить кеш.
4. KliMich 10.09.18 15:59 Сейчас в теме
Добрый день!
Однозначно плюс! Работает и с WORD.

Только маленький вопрос.
В моей обработке используется "ВызовКлиентскогоМетода" в отличии от "ВызовСерверногоМетода" (в примере _ДемоПечатьСчетовНаОплатуПокупателю).

При этом в форме строка кода:
Макет = УправлениеПечатью.МакетПечатнойФормы(ЭтотОбъект.Метаданные().ПолноеИмя() + "." + ИдентификаторКоманды);
выдает ошибку:
{ВнешняяОбработка.ТрудовойДоговорWORD2018.Форма.Форма.Форма(831)}: Метод объекта не обнаружен (Метаданные)
Макет = УправлениеПечатью.МакетПечатнойФормы(ЭтотОбъект.Метаданные().ПолноеИмя() + "." + ИдентификаторКоманды);

Если явно указываю
Макет = УправлениеПечатью.МакетПечатнойФормы("ВнешняяОбработка.ТрудовойДоговорWORD2018" + "." + ИдентификаторКоманды);
то все работает...

Подскажите, как получить имя внешней обработки из формы (&НаКлиенте или &НаСервере)
6. simy4 8 28.09.18 15:03 Сейчас в теме
(4)Добрый день. В принципе, можно сделать так как сделали вы, просто при создании новой печатной формы копированием, нужно не забывать менять данный параметр.

Что бы обратиться к метаданным обработки объекта с клиента, вам нужно вызвать серверную функцию вида:

&НаКлиенте
Процедура ВашаКлиентскаяПроцедура()
	Макет = УправлениеПечатью.МакетПечатнойФормы(ПолучитьИмяМетаданныхОбъекта() + "." + ИдентификаторКоманды);
КонецПроцедуры

&НаСервере
Процедура ПолучитьИмяМетаданныхОбъекта()
	Возврат РеквизитФормыВЗначение("Объект").Метаданные().ПолноеИмя();
КонецПроцедуры
Показать
7. simy4 8 28.09.18 15:06 Сейчас в теме
(6)А еще лучше, что бы лишний раз не дергать сервер, в ПриСозданииНаСервере закешировать полное имя из метаданных в реквизит формы. В последствии вы просто обращаетесь к этому реквизиту при необходимости.
5. kassbar 20 20.09.18 16:19 Сейчас в теме
по моему вот этих 2-х пунктов

- Макеты, которые вы хотите дать на редактирование пользователям, должны префиксироваться согласно правилам БСП (ПФ_MXL_,ПФ_DOC_)
- Получать макет необходимо функцией БСП УправлениеПечатью.МакетПечатнойФормы(ПутьКМакету)

вполне достаточно для редактирования макетов в пользовательском режиме. Для чего весь остальной код ?
9. simy4 8 28.09.18 15:09 Сейчас в теме
(5)Добрый день. Вы проверили ваше утверждение, или только предполагаете?
10. kassbar 20 28.09.18 23:12 Сейчас в теме
(9) я делаю печатные формы только через расширения, чтобы пользователи сами меняли макеты через Еще-Изменить макет.
И точно знаю что Префикс печатной формы + получение макета через путь к нему = автоматическое редактирование макетов пользователем )
11. simy4 8 29.09.18 01:13 Сейчас в теме
(10) в данной публикации речь идет о дополнительных внешних обработках печати, подключение которых не предполагет использования расширения (и редактирования макета пользователем).
12. kassbar 20 29.09.18 13:03 Сейчас в теме
(11) надеюсь кто-то найдет это материал полезным
Оставьте свое сообщение