Появилось несколько задач сделать внешние отчеты без ограничения прав, обычные формы, клиент- сервер, УПП 1.3.
Дано: пользователь с ролями "Пользователь ", "Вывод информации", надо сделать для него отчет, например, по Основным средствам.
При этом:
1. Роли добавить пользователю нельзя;
2. УстановитьПривилегированныйРежим(Истина) — во внешних отчетах на обычных формах не работает;
3. Сделать отчет на управляемых формах и встроить отчет в конфигурацию — заказчик против, т.к. у него уже настроен доступ ко внешним отчетам и обработкам через RLS.
в остается формирование отчета в привилегированном модуле.
За основу взят "Шаблон типового отчета СКД" https://its.1c.ru/db/metod8dev#content:3048:hdoc, формирование результата вынесено в привилегированный модуль.
Реализация такая:
1. В конфигурацию добавляем общий модуль ДоработкиПривилегированный с галками Сервер, Вызов сервера, Привилегированный, в который помещаем функцию формирования отчета, см.код ниже.
2. В отчете, как обычно, в основной схеме компоновки добавляем набор данных — Запрос, делаем настройки. При запуске отчета создается программно еще одна схема — но с набором данных — Объект. Копируются все поля, параметры и ресурсы. Эта схема используется в отчете для инициализации компоновщика настроек и пользователь может настроить структуру без красных крестиков на полях.
Настройки, которые установил пользователь передаются на сервер, загружаются в настройки компоновщика основной схемы
Отчет по основной схеме формируется на сервере в общем модуле ДоработкиПривилегированный, возвращается назад уже готовый результат — Табличный документ.
Расшифровку полностью победить не удалось, оставила только одно действие — ОткрытьЗначение. Если у пользователя есть права на объект — при двойном клике по объекту — откроется значение, если прав нет, выводится стандартное сообщение "У пользователя недостаточно прав для исполнение операции над базой данных".
Очень важно!!! В основной схеме компоновки данных в наборах данных обязательно должны быть указаны типы значений полей.
Функция СформироватьОтчет из модуля отчета:
Функция СформироватьОтчет(Результат = Неопределено, ДанныеРасшифровки = Неопределено, ВыводВФормуОтчета = Истина) Экспорт
//ЗначениеПанелипользователя = ТиповыеОтчеты.ПолучитьЗначенияНастроекПанелиПользователяОбъекта(ЭтотОбъект);
НастрокаПоУмолчанию = КомпоновщикНастроек.ПолучитьНастройки();
ТиповыеОтчеты.ПолучитьПримененуюНастройку(ЭтотОбъект);
//++начало
Результат.Очистить();
ТабличныйДокумент = Новый ТабличныйДокумент;
ТиповыеОтчеты.ДоработатьТиповойОтчетПередВыводом(ЭтотОбъект);
ТиповыеОтчеты.ВыводЗаголовкаТиповогоОтчета(ЭтотОбъект, ТабличныйДокумент);
ДоработатьКомпоновщикПередВыводом();
СтруктураОтчета = Новый Структура("Схема, ДанныеРасшифровки, НастройкиКомпоновщика, ТабличныйДокумент", ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных"), ДанныеРасшифровки, Новый ХранилищеЗначения(КомпоновщикНастроек.Настройки), ТабличныйДокумент);
АдресСтруктураОтчета = ПоместитьВоВременноеХранилище(СтруктураОтчета, Новый УникальныйИдентификатор);
АдресРезультат = ДоработкиПривилегированный.СформироватьОтчетПривРежТабличныйДокумент(АдресСтруктураОтчета);
РезультатСтруктура = ПолучитьИзВременногоХранилища(АдресРезультат);
РезультатТабличныйДокумент = РезультатСтруктура.ТабличныйДокумент;
ДанныеРасшифровки = РезультатСтруктура.ДанныеРасшифровки;
ДанныеРасшифровки.Настройки = КомпоновщикНастроек.Настройки;
Результат.Вывести(РезультатТабличныйДокумент);
Результат.ФиксацияСверху = РезультатТабличныйДокумент.ФиксацияСверху;
Результат.Показать();
УдалитьИзВременногоХранилища(АдресСтруктураОтчета);
УдалитьИзВременногоХранилища(АдресРезультат);
//--конец
//ТиповыеОтчеты.СформироватьТиповойОтчет(ЭтотОбъект, Результат, ДанныеРасшифровки, ВыводВФормуОтчета);
КомпоновщикНастроек.ЗагрузитьНастройки(НастрокаПоУмолчанию);
КонецФункции
Функция из модуля отчета, которая создает схему с набором данных Объект:
Функция ПолучитьСхемуДляНастройки()
//Схема для настройки
Схема = Новый СхемаКомпоновкиДанных;
ИД = Схема.ИсточникиДанных.Добавить();
ИД.Имя = "ИсточникДанных";
ИД.ТипИсточникаДанных = "Local";
НаборДанныхДляНастройки = Схема.НаборыДанных.Добавить(Тип("НаборДанныхОбъектСхемыКомпоновкиДанных"));
НаборДанныхДляНастройки.Имя = "ВнешниеДанные";
НаборДанныхДляНастройки.ИмяОбъекта = "ВнешниеДанные";
НаборДанныхДляНастройки.ИсточникДанных = "ИсточникДанных";
//поля
Для каждого НаборДанных из СхемаКомпоновкиДанных.НаборыДанных Цикл
Для каждого Поле из НаборДанных.Поля Цикл
Если НаборДанныхДляНастройки.Поля.Найти(Поле) = Неопределено Тогда
ПолеНабораДляНастройки = НаборДанныхДляНастройки.Поля.Добавить(Тип("ПолеНабораДанныхСхемыКомпоновкиДанных"));
ЗаполнитьЗначенияСвойств(ПолеНабораДляНастройки, Поле);
КонецЕсли;
КонецЦикла;
КонецЦикла;
//параметры
Для каждого Параметр из СхемаКомпоновкиДанных.Параметры Цикл
Если Схема.Параметры.Найти(Параметр) = Неопределено Тогда
ПараметрДляНастройки = Схема.Параметры.Добавить();
ЗаполнитьЗначенияСвойств(ПараметрДляНастройки, Параметр);
КонецЕсли;
КонецЦикла;
//выч поля
Для каждого ВычПоле из СхемаКомпоновкиДанных.ВычисляемыеПоля Цикл
Если Схема.ВычисляемыеПоля.Найти(ВычПоле) = Неопределено Тогда
ВычПолеДляНастройки = Схема.ВычисляемыеПоля.Добавить();
ЗаполнитьЗначенияСвойств(ВычПолеДляНастройки, ВычПоле);
КонецЕсли;
КонецЦикла;
//ресурсы
Для каждого Ресурс из СхемаКомпоновкиДанных.ПоляИтога Цикл
Если Схема.ПоляИтога.Найти(Ресурс) = Неопределено Тогда
РесурсДляНастройки = Схема.ПоляИтога.Добавить();
ЗаполнитьЗначенияСвойств(РесурсДляНастройки, Ресурс);
КонецЕсли;
КонецЦикла;
ТиповыеОтчеты.СкопироватьНастройкиКомпоновкиДанных(Схема.НастройкиПоУмолчанию, СхемаКомпоновкиДанных.НастройкиПоУмолчанию);
Возврат Схема;
КонецФункции
В процедуре ПрименитьНастройку модуля отчета:
Процедура ПрименитьНастройку() Экспорт
//СхемаДляНастройки = ТиповыеОтчеты.ПолучитьСхемуКомпоновкиОбъекта(ЭтотОбъект);
//++начало
Схема = ПолучитьСхемуДляНастройки();
//--конец
// Считываение структуры настроек отчета
Если Не СохраненнаяНастройка.Пустая() Тогда
СтруктураНастроек = СохраненнаяНастройка.ХранилищеНастроек.Получить();
Если Не СтруктураНастроек = Неопределено Тогда
КомпоновщикНастроек.ЗагрузитьНастройки(СтруктураНастроек.НастройкиКомпоновщика);
ЗаполнитьЗначенияСвойств(ЭтотОбъект, СтруктураНастроек);
Иначе
КомпоновщикНастроек.ЗагрузитьНастройки(Схема.НастройкиПоУмолчанию);
КонецЕсли;
Иначе
КомпоновщикНастроек.ЗагрузитьНастройки(Схема.НастройкиПоУмолчанию);
КонецЕсли;
КонецПроцедуры
В модуле отчета основную схему компоновки данных подменяем на созданную нами схему:
СхемаКомпоновкиДанных = ПолучитьСхемуДляНастройки();
Функция в общем модуле ДоработкиПривилегированный (почти копия ТиповыеОтчеты.УниверсальныйМеханизмФормированияОтчета)
Функция СформироватьОтчетПривРежТабличныйДокумент(АдресСтруктураОтчета) Экспорт
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
СтруктураОтчета = ПолучитьИзВременногоХранилища(АдресСтруктураОтчета);
Схема = СтруктураОтчета.Схема;
ДанныеРасшифровки = СтруктураОтчета.ДанныеРасшифровки;
НастройкиКомпоновщика = СтруктураОтчета.НастройкиКомпоновщика.Получить();
ТабличныйДокумент = СтруктураОтчета.ТабличныйДокумент;
ВнешниеНаборыДанных = Неопределено;
Если СтруктураОтчета.Свойство("ВнешниеНаборыДанных") Тогда
ВнешниеНаборыДанных = СтруктураОтчета.ВнешниеНаборыДанных;
КонецЕсли;
КомпоновщикНастроек = Новый КомпоновщикНастроекКомпоновкиДанных;
КомпоновщикНастроек.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(Схема));
КомпоновщикНастроек.ЗагрузитьНастройки(НастройкиКомпоновщика);
МакетКомпоновки = КомпоновщикМакета.Выполнить(Схема, НастройкиКомпоновщика, ДанныеРасшифровки);
ТиповыеОтчеты.ДополнитьМакетыМакетаКомпоновкиРасшифровкойРесурсов(МакетКомпоновки, КомпоновщикНастроек);
//Создадим и инициализируем процессор компоновки
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
Если ВнешниеНаборыДанных = Неопределено Тогда
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки, Истина);
Иначе
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки, Истина);
КонецЕсли;
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ТабличныйДокумент);
//Обозначим начало вывода
ПроцессорВывода.НачатьВывод();
ТаблицаЗафиксирована = Ложь;
ТабличныйДокумент.ФиксацияСверху = 0;
//определим нужна ли фиксация в отчете. Если элементов структуры больше 2-х
КолВоВключеныхЭлементов = 0;
Для каждого ЭлементСтруктуры из НастройкиКомпоновщика.Структура Цикл
Если ЭлементСтруктуры.Использование тогда
КолВоВключеныхЭлементов = КолВоВключеныхЭлементов + 1;
КонецЕсли;
Если КолВоВключеныхЭлементов > 1 тогда
ТаблицаЗафиксирована = истина;
КонецЕсли;
КонецЦикла;
//Основной цикл вывода отчета
Пока Истина Цикл
//Получим следующий элемент результата компоновки
ЭлементРезультата = ПроцессорКомпоновки.Следующий();
Если ЭлементРезультата = Неопределено Тогда
//Следующий элемент не получен - заканчиваем цикл вывода
Прервать;
Иначе
Если Не ТаблицаЗафиксирована
И ЭлементРезультата.ЗначенияПараметров.Количество() > 0
И ТипЗнч(НастройкиКомпоновщика.Структура[0]) <> Тип("ДиаграммаКомпоновкиДанных") Тогда
ТаблицаЗафиксирована = Истина;
ТабличныйДокумент.ФиксацияСверху = ТабличныйДокумент.ВысотаТаблицы;
ОбластьШапки = ТабличныйДокумент.Область(3, ,ТабличныйДокумент.ВысотаТаблицы, );
ТабличныйДокумент.ПовторятьПриПечатиСтроки = ОбластьШапки;
КонецЕсли;
//Элемент получен - выведем его при помощи процессора вывода
ПроцессорВывода.ВывестиЭлемент(ЭлементРезультата);
КонецЕсли;
КонецЦикла;
//Обозначем завершение вывода
ПроцессорВывода.ЗакончитьВывод();
РезультатОтчета = Новый Структура("ТабличныйДокумент, ДанныеРасшифровки", ТабличныйДокумент, ДанныеРасшифровки);
Возврат ПоместитьВоВременноеХранилище(РезультатОтчета, Новый УникальныйИдентификатор);
КонецФункции
В архиве внешний отчет и текст для общего модуля. Тестировался на Управление производственным предприятием, редакция 1.3 (1.3.82.1). Не проверяла на сложных схемах с произвольными макетами, вложенными схемами.
Если отчет встраивать в конфу нельзя. То почему общие модули можно?
За код спасибо. Но полезность сомнительна.
Спасибо автору. Только такой подход сработал. Много времени потратил на поиск решения проблемы в интернет.
вызываемые функции будут всёравно в безопасном режиме вызываться, интересно, или нет ?
(3) У всех пользователей, которые пользуются этими отчетами стоит галка «Защита от опасных действий», на ошибки никто не жалуется.
(4) а это фиолетово. все внешние функции, вызываемые при компоновке, ранее вызывались в безопасном режиме несмотря вообще ни на что, даже если они располагались в модуле прив. режима… если можете проверить — проверьте, очень интересно.
Подскажите, этот шаблон поможет при ошибке
при открытии отчета под пользователем без полных прав.
—
(6)не знаю, должно помочь, проверьте сами.
(8)Спасибо, работает, только не рассчитывается пару параметров, которые зависят от НачалоПериода и КонецПериода. Из-за чего это может быть?
(9)Я думаю надо из оригинального отчета перенести код процедуры ДоработатьКомпоновщикПередВыводом. Обычно там устанавливаются дополнительные параметры.
(10)
, не, в типовом тоже пустая процедура… Я примерно так и подумал что в этой процедуре надо что-то доработать…
(11) все дело в премудростях СКД, указал в запросе в параметрах в таком виде {(&КонецПериодОС)}, и все появилось )
Я так понимаю СКД не видел это параметр без {}…