Многие из нас знакомы с замечательным механизмом БСП "Макеты печатных форм", позволяющим пользователю самостоятельно менять макеты из метаданных конфигурации. В этой статье мы рассмотрим, как при помощи механизма расширения (модулей) данный функционал можно распространить на дополнительные обработки внешних печатных форм.
Приступим!
Релизацию задачи можно разделить на три шага перехвата и модернизации механизмов:
- Заполнения типового списка макетов
- Получения макета по имени объекта метаданных
- Записи модернизированного пользовательского макета
При разработке внешних печатных форм следует придерживаться определенных правил.
Разберем каждый из шагов подробнее.
1. Перехват и модернизации механизма заполнения типового списка макетов
Механизм находится в процедуре формы РегистрСведений.ПользовательскиеМакетыПечати.Форма.МакетыПечатныхФорм.ЗаполнитьТаблицуМакетовПечатныхФорм().
Добавляем форму МакетыПечатныхФорм в расширение, в модуле формы прописываем следующий код:
&Перед("ЗаполнитьТаблицуМакетовПечатныхФорм")
Процедура simПМВПФ_ЗаполнитьТаблицуМакетовПечатныхФорм()
//Ищем запросом все неотключенные внешние обработки печатных форм
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДополнительныеОтчетыИОбработки.Ссылка КАК Ссылка
|ИЗ
| Справочник.ДополнительныеОтчетыИОбработки КАК ДополнительныеОтчетыИОбработки
|ГДЕ
| ДополнительныеОтчетыИОбработки.Вид = &ВПФ
| И ДополнительныеОтчетыИОбработки.Публикация <> &Отключена
| И НЕ ДополнительныеОтчетыИОбработки.ПометкаУдаления";
Запрос.УстановитьПараметр("ВПФ" , Перечисления.ВидыДополнительныхОтчетовИОбработок.ПечатнаяФорма);
Запрос.УстановитьПараметр("Отключена" , Перечисления.ВариантыПубликацииДополнительныхОтчетовИОбработок.Отключена);
РезультатЗапроса = Запрос.Выполнить();
ВДЗ = РезультатЗапроса.Выбрать();
Пока ВДЗ.Следующий() Цикл
//Получаем обработку-объект, для доступа к ее макетам, через метаданные
ОбработкаОбъект = ДополнительныеОтчетыИОбработки.ОбъектВнешнейОбработки(ВДЗ.Ссылка);
ОбъектМетаданныхКоллекции = ОбработкаОбъект.Метаданные();
//Перебираем макеты
Для каждого ОбъектМетаданныхМакет из ОбъектМетаданныхКоллекции.Макеты Цикл
//Вызов нетиповой функции проверки соответствия префикса в макете
ТипМакета = sim_ТипМакета(ОбъектМетаданныхМакет.Имя, ОбработкаОбъект);
Если ТипМакета = Неопределено Тогда
Продолжить;
КонецЕсли;
//Добавление макета на форму
ДобавитьОписаниеМакета(ОбъектМетаданныхКоллекции.ПолноеИмя() + "." + ОбъектМетаданныхМакет.Имя, ОбъектМетаданныхМакет.Синоним, ОбъектМетаданныхКоллекции.Синоним, ТипМакета);
КонецЦикла;
КонецЦикла;
КонецПроцедуры
&НаСервере
Функция sim_ТипМакета(ИмяОбъектаМетаданныхМакета, ОбработкаОбъект)
Позиция = СтрНайти(ИмяОбъектаМетаданныхМакета, "ПФ_");
Если Позиция = 0 Тогда
Возврат Неопределено;
КонецЕсли;
//получаем требуемый макет
МакетПечатнойФормы = ОбработкаОбъект.ПолучитьМакет(ИмяОбъектаМетаданныхМакета);
ТипМакета = Неопределено;
Если ТипЗнч(МакетПечатнойФормы) = Тип("ТабличныйДокумент") Тогда
ТипМакета = "MXL";
ИначеЕсли ТипЗнч(МакетПечатнойФормы) = Тип("ДвоичныеДанные") Тогда
ТипМакета = ВРег(УправлениеПечатьюСлужебный.ОпределитьРасширениеФайлаДанныхПоСигнатуре(МакетПечатнойФормы));
КонецЕсли;
Возврат ТипМакета;
КонецФункции
Схематично вышеприведенный код работает следующим образом:
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. Особенности печати пользовательского макета для внешней печатной формы
Разрабатывая внешнюю печатную форму, необходимо помнить о нескольких несложных правилах:
- Имя каждой отдельной внешней обработки должно быть уникально
- Макеты, которые вы хотите дать на редактирование пользователям, должны префиксироваться согласно правилам БСП (ПФ_MXL_,ПФ_DOC_)
- Получать макет необходимо функцией БСП УправлениеПечатью.МакетПечатнойФормы(ПутьКМакету)
- Получая область макета для печати, необходимо сначала проверить ее наличие (пользователь мог ее удалить) методом Найти(ИмяОбласти)
Макет = ЭтотОбъект.ПолучитьМакет("СоставПоказателей");
ТекОбласть = Макет.Области.Найти(ИмяОбласти); - Заполнять параметры макета следует процедурой ЗаполнитьЗначенияСвойств(Область.Параметры, Источник), ровно по той же причине (пользователь может удалить параметры)
Вот, собственно, и все. Буду раз вашим комментариям.
Системные требования
Платформа: не ниже v8.3.11
БСП: v2.4.4.145 - v3.0.1.355