Рассмотрим пример создания внешнего отчета с двумя вариантами выполнения. Примеры разрабатывались на платформе 8.3.10.2561. Варианты созданы на базе таблиц и показывают "Роли пользователей" группируемые по "Профилям" с разворотом по базам данных и подразделениям. В первом варианте колонками отчета являются базы данных с разворотом по подразделениям. Этот вариант удобен для сравнения ролей одного пользователя по базам. Во втором варианте колонками отчета являются сами пользователи и этот вариант удобен для сравнения ролей пользователей с по одной базе данных. И так приступим.
- Создание реквизитов объекта отчета
Для подключения к кластеру 1с нам понадобятся следующие реквизиты объекта отчета:
«Сервер» – IP адрес кластера серверов 1с, отличается от IP адреса 1с сервера, номером порта ;
«COM_соединение» – имя установленного com соединения (стандартное имя для текущей платформы - V83.COMConnector) .
Для подключения к внешним базам данных создадим реквизиты:
«СерверБазы» – IP адрес 1с сервера;
«Пользователь» и «Пароль» – логин и пароль пользователя.
Дополнительные реквизиты:
«ЭтаБаза» - содержит имя базы из которой запускается отчет;
«ИмяВариантаНастроек» - имя выполняемого варианта настроек макета отчета.
Для хранения списка баз кластера 1с, создадим табличный реквизит «ТаблицаБаз» , содержащий следующие колонки : «Выбор» - тип «Булево», «ИмяБазы» и «ОписаниеБазы» тип «Строка».
Рис. 1.
2. Создание формы отчета
Продвинутые программисты могут пропустить этот раздел статьи, поскольку работа ведется исключительно в конструкторе формы.
Форму отчета создадим на базе двух закладок. Сразу установим свойство формы, АитоматическоеСохранениеДанныхВНастройках = «Использовать».
Первая закладка «Настройки подключения к базам», включает в себя реквизиты необходимые, для создания com-подключений к базам данных кластера 1с.
Рис. 2.
Вторая закладка содержит непосредственно сам отчет. Для его создания создадим реквизиты «ДанныеРасшифровки», «НаименованиеТекущегоВарианта» с типом «Строка», и «Результат» с типом «ТабличныйДокумент». Отмечу, что имена реквизитов строго регламентированы.
Для удобства выбора варианта отчета создадим «Группу4», как «Обычная группа», установим свойство «Группировка» = «Горизонтальная» и поместим в нее реквизит «НаименованиеТекущегоВарианта». Откроем закладку конструктора формы, «Команды/Стандартные команды» и раскроем элемент «Форма». Выберем подчиненный элемент «Выбрать вариант» (Рис.3.) и перетащим его в «Группу4».
Создадим панель управления отчетом путем перетаскивания корневого элемента «Форма» на закладку «Отчет» .
Перейдем на закладку «Реквизиты» конструктора формы, раскроем объект «Отчет», подчиненный элемент «КомпановщикНастроек» и добавим на закладку «Отчет», элемент «ПользовательскиеНастройки». Установим свойство ПоложениеКоманднойПанели = «Нет», и уберем флаг «Шапка».
И последнее. Добавим реквизит «Результат», на закладку «Отчет».
Создание формы отчета на этом закончено. Переходим к программированию в модуле формы отчета.
Рис. 3.
3. Процедуры и функции модуля формы
Код снабжен подробными комментариями и не нуждается в дополнительном разъяснении.
&НаКлиенте
Процедура ПриОткрытии(Отказ)
// Если имя пользователя не заполнено то вызываем серверную процедуру ПолучитьПользователя(),
// которая возвращает имя текущего пользователя
Если Отчет.ИмяПользователя = "" Тогда
Отчет.ИмяПользователя = ПолучитьПользователя();
КонецЕсли;
Если Отчет.COM_соединение = "" Тогда
Отчет.COM_соединение = "V83.COMConnector";
КонецЕсли;
// Плучим данные по серверам
СерверПолучить();
// Если табличный реквизит Отчет.ТаблицаБаз уже содержит записи, то предполагается, что эти записи с именами баз заполнены
// при вызове отчета из другой формы конфигурации, поэтому перейдем сразу на закладку отчет.
// Если записи отсутствуют, то вызовем процедуру ПолучитьСписокБаз()
Если Отчет.ТаблицаБаз.Количество() = 0 Тогда
ПолучитьСписокБаз();
Иначе
Элементы.Группа1.ТекущаяСтраница = Элементы.Группа1.ПодчиненныеЭлементы.Отчет;
КонецЕсли;
КонецПроцедуры
&НаСервере
Функция ПолучитьПользователя()
Возврат (ПользователиИнформационнойБазы.ТекущийПользователь().Имя);
КонецФункции
&НаСервере
Процедура СерверПолучить()
ИБ = СтрокаСоединенияИнформационнойБазы();
// Заполним имя текушей базы
Отчет.ЭтаБаза = Сред(ИБ,СтрНайти(ИБ,";")+6,((СтрДлина(ИБ)-(СтрНайти(ИБ,";")+6)))-1);
Если не Отчет.Сервер = "" И не Отчет.СерверБазы = "" Тогда
Возврат;
КонецЕсли;
П1 = Сред(ИБ, 7, 32);
СерверПорт = Лев(П1, СтрНайти(П1, "Ref")-3);
// Поскольку IP адреса кластера 1с и сервера 1с могут отличаться, при нестандартных номерах портов, то адрес сервера 1с
// заносится в реквизит Отчет.СерверБазы, а адрес кластера 1с заносится в реквизит Отчет.Сервер
Отчет.СерверБазы= СерверПорт;
ДвоеточиеПоложение = СтрНайти(СерверПорт, ":");
Если ДвоеточиеПоложение=0 Тогда
Сервер = СерверПорт;
Иначе
// Как правило номер порта кластера 1с на 1 меньше номера порта сервера 1с - вычислим этот порт.
Сервер = Лев(СерверПорт, ДвоеточиеПоложение-1)+":"+СтрЗаменить((Число(Сред(СерверПорт, ДвоеточиеПоложение+1, 4)) -1), Символы.НПП, "");
КонецЕсли;
Отчет.Сервер = Сервер;
КонецПроцедуры
&НаСервере
Процедура ПолучитьСписокБаз()
// Создаем объект Соединитель
Попытка
Соединитель = Новый COMОбъект(Отчет.COM_соединение);
Исключение
Сообщить("Ошибка создания "+Отчет.COM_соединение+". "+ОписаниеОшибки());
Возврат;
КонецПопытки;
// Подключаемся к агенту кластера 1с
// Если номера портов кластера и сервера 1с стандартные (1540 и 1541), то их можно не вводить
Попытка
АгентСервера = Соединитель.ConnectAgent("tcp://" + Отчет.Сервер);
Исключение
Если СтрНайти(Отчет.Сервер, ":") > 1 Тогда
Сообщить("Внимание!!! Проверьте номер порта. " + ОписаниеОшибки());
Иначе
Сообщить("Ошибка создания агента сервера. " + ОписаниеОшибки());
КонецЕсли;
Возврат;
КонецПопытки;
// Загрузим кластеры сервера 1с
КластерыСерверов = АгентСервера.GetClusters().Выгрузить();
Для Каждого Кластер Из КластерыСерверов Цикл
Попытка
// Если Вы не являетесь администратором кластера серверов, то Вам придется раскомментировать
// нижеследующую строку и ввести данные подключения к кластеру
//АгентСервера.Authenticate(Кластер, АдминистраторКластера, ПарольАдмКластера);
АгентСервера.Authenticate(Кластер, "", "");
Исключение
Сообщить(ОписаниеОшибки());
Возврат ;
КонецПопытки;
// Загрузим список баз кластера в таблицу Отчет.ТаблицаБаз
Базы = АгентСервера.GetInfoBases(Кластер).Выгрузить();
Для каждого База Из Базы Цикл
СтрТабБаз = Отчет.ТаблицаБаз.Добавить();
СтрТабБаз.ИмяБазы = База.Name;
СтрТабБаз.ОписаниеБазы = База.Descr;
КонецЦикла;
Прервать;
КонецЦикла;
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьСписокБаз(Команда)
ПолучитьСписокБаз();
КонецПроцедуры
На Рис. 4, представлен результат работы формы отчета, для закладки "Настройки подключения к базам"
Рис. 4.
4. Создание макета отчета
Создание макета отчета имеет некоторые особенности, поэтому необходимо этот раздел прочитать обязательно.
Откроем схему компоновки данных закладку «Наборы данных» и создадим «Набор данных – объект»-"ОтчетПоВнешнимДанным" . Создадим поля набора данных согласно Рис. 5. Установим «Имя объекта, содержащего данные» = ТаблицаДанных.
Рис. 5.
Перейдем на закладку «Ресурсы» и создадим ресурсы макета согласно Рис. 6.
Рис. 6.
Первый ресурс считает количество ролей, второй показывает доступность роли - "Да" или "Нет", для этого ресурса установим в колонке "Рассчитывать по...", значение "Роль".
Перейдем на закладку «Параметры» и создадим параметры согласно Рис. 7.
Рис. 7.
Для параметра «ПрофилиГруппДоступа» установим тип «СправочникСсылка.ПрофилиГруппДоступа», для параметра «Пользователи» установим составной тип «СправочникСсылка.Пользователи, Строка»-составной тип сделан в связи с тем, что не во всех базах могут быть одинаковые списки пользователей. Поэтому открыта возможность ввести имя пользователя в ручную. Необходимо особо отметить, что у параметров, кроме "ВариантОтчета", необходимо установить флаг «Доступен список значений». Параметр «ВариантОтчета», нам необходим для того, чтобы отслеживать имя выполняемого варианта отчета, в отборах по запросу он не участвует.
Откроем закладку «Настройки», создадим два варианта отчета согласно Рис.8 и Рис.9 и для параметра «Вариант отчета» установим значения «Вариант1» и «Вариант2» соответственно. Единственным выбранным полем для вариантов отчета, является поле ресурс «РольДоступна».
Как видно из рисунков, варианты отчета выполнены в виде таблиц. Первый вариант разворачивает роли пользователя по базам данных и подразделениям. Второй вариант разворачивает роли базы данных и подразделения по пользователям. Для параметров "Пользователи" и "Вариант отчета" установим флаг "Включать в пользовательские настройки"
Рис. 8.
Рис. 9.
5. Модуль объекта отчета
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
// Осуществим проверку на выбор баз для формирования отчета, и в случае если выбранных баз нет сформируем предупреждающее сообщение.
ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("Выбор", Истина);
СтрокиБаз = ТаблицаБаз.НайтиСтроки(ПараметрыОтбора);
Если СтрокиБаз.Количество() = 0 Тогда
Сообщить("Необходимо выбрать хотябы одну базу!");
Возврат;
КонецЕсли;
// Создадим таблицу значений для формирования отчета.
// Имена полей возьмем из созданного нами набора данных "ОтчетПоВнешнимДанным", в макете отчета
ТаблицаДляСКД = Новый ТаблицаЗначений;
ПоляНабораДанных = СхемаКомпоновкиДанных.НаборыДанных.ОтчетПоВнешнимДанным.Поля;
Для каждого Строка из ПоляНабораДанных Цикл
ТаблицаДляСКД.Колонки.Добавить(Строка.Поле);
КонецЦикла;
// Цикл по массиву выбранных база.
Для Каждого СтрТабБаз из СтрокиБаз Цикл
// Если имя базы совпадает с именем текущей базы, то обработаем запрос к текущей базе.
// Понятно, что подключаться к текущей базе данных через com-соединение довольно затратно по ресурсам, времени и поэтому не производительно.
// Поэтому этот процесс разделеяется в нижеследующем коде.
Если СтрТабБаз.ИмяБазы = ЭтаБаза Тогда
РезультатЗапроса = ЗапросКТекущейБазе(СтрТабБаз.ИмяБазы);
Иначе
// иначе сформируем строку подключения к базе и вызовем процедуру запроса к внешней базе
СтрокаКоннекта = "Srvr="""+СерверБазы+""";Ref="""+СтрТабБаз.ИмяБазы+""";Usr="""+ИмяПользователя+""";Pwd="""+Пароль+"""";
РезультатЗапроса = ЗапосКВнешнейБазе(СтрокаКоннекта, СтрТабБаз.ИмяБазы);
КонецЕсли;
Если РезультатЗапроса = Неопределено Тогда
Продолжить;
КонецЕсли;
Для Каждого СтрТЗ Из РезультатЗапроса Цикл
// Построчно перенесем результаты каждого запроса в таблицу значений "ТаблицаДляСКД" и добавим имя базы данных в соответствующую колонку
СтрТабСкд=ТаблицаДляСКД.Добавить();
Для Каждого Колонка из ТаблицаДляСКД.Колонки Цикл
Если Колонка.Имя = "ИмяБазыДанных" Тогда
СтрТабСкд[Колонка.Имя] = СтрТабБаз.ИмяБазы;
Иначе
СтрТабСкд[Колонка.Имя] = СтрТЗ[Колонка.Имя];
КонецЕсли;
КонецЦикла;
КонецЦикла;
КонецЦикла;
// Далее идет стандартный код формирования отчета из итоговой таблицы значений "ТаблицаДляСКД"
ВнешнийНабор = Новый Структура("ТаблицаДанных", ТаблицаДляСКД);
Настройки = КомпоновщикНастроек.Настройки;
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешнийНабор);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
// Этот код сворачивает группировки до уровней профиля пользователя в зависимости от варианта отчета
Если ИмяВариантаНастроек = "Вариант1" Тогда
ДокументРезультат.ПоказатьУровеньГруппировокСтрок(1);
ИначеЕсли ИмяВариантаНастроек = "Вариант2" Тогда
ДокументРезультат.ПоказатьУровеньГруппировокСтрок(2);
КонецЕсли;
// Данный код является универсальным не зависимости от набора данных в макете отчета
КонецПроцедуры
Функция ЗапосКВнешнейБазе(СтрокаКоннекта, ИмяБазы)
// Подключимся к внешней базе
Попытка
Соединитель = Новый COMОбъект(COM_соединение);
Исключение
Ошибка = ОписаниеОшибки();
Сообщить("Ошибка создания "+COM_соединение+". "+Ошибка);
Возврат Неопределено;
КонецПопытки;
Попытка
ДругаяБаза=Соединитель.Connect(СтрокаКоннекта);
Исключение
Ошибка = ОписаниеОшибки();
Сообщить(Ошибка);
Возврат Неопределено;
КонецПопытки;
// Создадим запрос во внешней базе
Запрос = ДругаяБаза.NewObject("Запрос");
// Текст запроса у нас одинаков, как для внешней, так и для текущей базы
Запрос.Текст = ПолучитьТекстЗапроса();
// Вызовем процедуру установки параметров запроса
УстановитьПараметрыЗапроса(Запрос, ДругаяБаза);
Попытка
ТЗ = Запрос.Выполнить().Выгрузить();
Возврат(ТЗ);
Исключение
Ошибка = ОписаниеОшибки();
Сообщить(Ошибка);
Возврат Неопределено;
КонецПопытки;
КонецФункции
Функция ЗапросКТекущейБазе(ИмяБазы)
// Запрос к текущей базе является стандартным
Запрос = Новый Запрос;
УстановитьПараметрыЗапроса(Запрос);
Запрос.Текст = ПолучитьТекстЗапроса();
ТЗ = Запрос.Выполнить().Выгрузить();
Возврат ТЗ;
КонецФункции
Функция ПолучитьТекстЗапроса()
// Текст запроса по выходным полям строго соответствует именам полей набора данных в макете отчета.
// Все выходные поля запроса необходимо привести к простым типам: число, строка, дата, булево ...
// Особо важно отметить, что имена параметров запроса строго согласованны с именами параметров макета отчета
// Поскольку используемая конфигурация является не стандартной, то для своей конфигурации необходимо будет запрос отредактировать.
// В тексте запроса даны пояснительные комментарии
ТекстЗапроса =
"ВЫБРАТЬ
| Таблица.Пользователь1С КАК Пользователь1С,
| Таблица.ПользовательКод КАК ПользовательКод,
| Таблица.ПользовательОС КАК ПользовательОС
|ПОМЕСТИТЬ ПользователиОС
|ИЗ
| &ТаблицаПользователей КАК Таблица
|;
// Обратите внимание, для получения значения ПользовательОС и ПользовательКод в таблицу передается
// параметр таблица значений "ТаблицаПользователей", которая формируется в процедуре УстановитьПараметрыЗапроса().
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ГруппыДоступаПользователи.Пользователь.Наименование КАК Пользователь,
| ГруппыДоступа.Профиль.Наименование КАК Профиль,
| ПрофилиГруппДоступаРоли.Роль КАК Роль,
| ВЫБОР
| КОГДА ПрофилиГруппДоступаРоли.Роль ЕСТЬ NULL
| ТОГДА 0
| ИНАЧЕ 1
| КОНЕЦ КАК РольДоступна,
| """" КАК ИмяБазыДанных,
| ПользователиПодразделения.Подразделение.Наименование КАК Подразделение,
| ГруппыДоступаПользователи.Пользователь.Код КАК ПользовательКод
|ПОМЕСТИТЬ ПраваПользователей
|ИЗ
| Справочник.ГруппыДоступа.Пользователи КАК ГруппыДоступаПользователи
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ГруппыДоступа КАК ГруппыДоступа
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ПрофилиГруппДоступа КАК ПрофилиГруппДоступа
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ПрофилиГруппДоступа.Роли КАК ПрофилиГруппДоступаРоли
| ПО (ПрофилиГруппДоступаРоли.Ссылка = ПрофилиГруппДоступа.Ссылка)
| ПО ГруппыДоступа.Профиль = ПрофилиГруппДоступа.Ссылка
| ПО ГруппыДоступаПользователи.Ссылка = ГруппыДоступа.Ссылка
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Пользователи.Подразделения КАК ПользователиПодразделения
| ПО ГруппыДоступаПользователи.Пользователь.Ссылка = ПользователиПодразделения.Ссылка
|ГДЕ
| НЕ ГруппыДоступаПользователи.Ссылка.ЭтоГруппа
| И НЕ ГруппыДоступаПользователи.Ссылка.ПометкаУдаления
| И НЕ ГруппыДоступаПользователи.Пользователь.ПометкаУдаления
// Имя параметра начинающееся с "Использовать..." объединяется с значением самого параметра.
// В результате если параметр "Использовать..." = Ложь, то значение объединенного параметра игнорируется.
// Таким образом мы согласуем использование этих параметров с параметрами получаемыми из макета запроса
// Второй объединяемый параметр должен иметь условие "В списке значений".
| И (НЕ &ИспользоватьПользователи
| ИЛИ ГруппыДоступаПользователи.Пользователь.Наименование В (&Пользователи))
| И (НЕ &ИспользоватьПрофилиГруппДоступа
| ИЛИ ГруппыДоступа.Профиль.Наименование В (&ПрофилиГруппДоступа))
|
|ОБЪЕДИНИТЬ ВСЕ
|
// Особенностью запроса является, то что искусственно создается роль "Дополнительные настройки прав"
// для регистра сведений "Настройки пользователя".
|ВЫБРАТЬ
| НастройкиПользователей.Пользователь.Наименование,
| ""Дополнительные настройки прав"",
| НастройкиПользователей.Настройка.Наименование,
| 1,
| """",
| ПользователиПодразделения.Подразделение.Наименование,
| НастройкиПользователей.Пользователь.Код
|ИЗ
| РегистрСведений.НастройкиПользователей КАК НастройкиПользователей
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Пользователи.Подразделения КАК ПользователиПодразделения
| ПО НастройкиПользователей.Пользователь = ПользователиПодразделения.Ссылка
|ГДЕ
| НастройкиПользователей.Значение = ИСТИНА
| И (НЕ &ИспользоватьПользователи
| ИЛИ НастройкиПользователей.Пользователь.Наименование В (&Пользователи))
| И НЕ &ИспользоватьПрофилиГруппДоступа
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ПраваПользователей.Пользователь КАК Пользователь,
| ПраваПользователей.Профиль КАК Профиль,
| ПраваПользователей.Роль КАК Роль,
| ПраваПользователей.РольДоступна КАК РольДоступна,
| ПраваПользователей.ИмяБазыДанных КАК ИмяБазыДанных,
| ПраваПользователей.Подразделение КАК Подразделение,
| ЕСТЬNULL(ПользователиОС.ПользовательКод, ПраваПользователей.ПользовательКод) КАК ПользовательКод,
| ЕСТЬNULL(ПользователиОС.ПользовательОС, """") КАК ПользовательОС
|ИЗ
| ПраваПользователей КАК ПраваПользователей
| ЛЕВОЕ СОЕДИНЕНИЕ ПользователиОС КАК ПользователиОС
| ПО ПраваПользователей.Пользователь = ПользователиОС.Пользователь1С"
;
Возврат ТекстЗапроса;
КонецФункции
Процедура УстановитьПараметрыЗапроса(Запрос, ДругаяБаза = Неопределено)
// Для передачи параметров в запрос, сформируем цикл по пользовательским настройкам
ПользовательскиеНастройкиЭлементы = КомпоновщикНастроек.ПользовательскиеНастройки.Элементы;
Для каждого Элемент из ПользовательскиеНастройкиЭлементы Цикл
Если не Строка(Элемент) = "ЗначениеПараметраНастроекКомпоновкиДанных" Тогда
Продолжить;
КонецЕсли;
ИмяПараметра = Строка(Элемент.Параметр);
// Параметр вариат отчета в запрос не передается
Если ИмяПараметра = "ВариантОтчета" Тогда
Продолжить;
КонецЕсли;
// Все другие параметры у нас имеют флаг "доступен список значений", поэтому для их передачи в запрос, создадим массив-"МассивПараметра".
// Понятно, что передача ссылок объектов в качестве параметров не имеет смысла поскольку одинаково названные объекты разных базах не будут равны.
// Как вариант можно осуществлять поиск ссылки объекта во внешней базе по наименованию и передавать уже массив ссылок в качестве параметра.
// Для параметра ТаблицаПользователей создадим таблицы значений с соответствующими полями
Если ДругаяБаза = Неопределено Тогда
МассивПараметра = Новый Массив;
ТаблицаПользователей = Новый ТаблицаЗначений;
ТаблицаПользователей.Колонки.Добавить("ПользовательОС", Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(64)));
ТаблицаПользователей.Колонки.Добавить("Пользователь1С", Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(64)));
ТаблицаПользователей.Колонки.Добавить("ПользовательКод", Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(64)));
// Поскольку поля параметра ТаблицаПользователей - используется только в варианте 1, то и заполнять эту таблицу из массива пользователей, мы будем только для варианта отчета 1
Если ИмяВариантаНастроек = "Вариант1" Тогда
// Получим данные по пользователям функцией 1с
МассивПользователей = ПользователиИнформационнойБазы.ПолучитьПользователей();
КонецЕсли;
Иначе
// Если параметр функции ДругаяБаза определен, то массив-"МассивПараметра" создается во внешней базе
МассивПараметра = ДругаяБаза.NewObject("Массив");
// Передаваемый параметр таблица значений "ТаблицаПользователей", так же создадим в внешней базе
ТаблицаПользователей = ДругаяБаза.NewObject("ТаблицаЗначений");
ТаблицаПользователей.Колонки.Добавить("ПользовательОС", ДругаяБаза.NewObject("ОписаниеТипов", "Строка", ДругаяБаза.NewObject("КвалификаторыСтроки", 64)));
ТаблицаПользователей.Колонки.Добавить("Пользователь1С", ДругаяБаза.NewObject("ОписаниеТипов", "Строка", ДругаяБаза.NewObject("КвалификаторыСтроки", 64)));
ТаблицаПользователей.Колонки.Добавить("ПользовательКод", ДругаяБаза.NewObject("ОписаниеТипов", "Строка", ДругаяБаза.NewObject("КвалификаторыСтроки",64)));
// Для варианта отчета "Вариант1" заполним массив пользователей, для последующего копирования в таблицу значений "ТаблицаПользователей
Если ИмяВариантаНастроек = "Вариант1" Тогда
МассивПользователей = ДругаяБаза.ПользователиИнформационнойБазы.ПолучитьПользователей();
КонецЕсли;
КонецЕсли;
Если Элемент.Использование и не Элемент.Значение = Неопределено Тогда
// Запишем значения параметров запроса в соответствующие массивы
// Отметим, что здесь применено правило автоматического формирования имени параметра запроса,
// из имени параметра пользовательских настроек, которого необходимо строго придерживаться для автоматизации процесса программирования
Если ТипЗнч(Элемент.Значение) = Тип("СправочникСсылка." + ИмяПараметра) Тогда
МассивПараметра.Добавить(СокрП(Элемент.Значение.Наименование));
ИначеЕсли ТипЗнч(Элемент.Значение) = Тип("Строка") Тогда
МассивПараметра.Добавить(СокрП(Элемент.Значение));
ИначеЕсли Элемент.Значение.Количество() > 0 Тогда
Для каждого Параметр из Элемент.Значение Цикл
Если ТипЗнч(Параметр.Значение) = Тип("СправочникСсылка." + ИмяПараметра) Тогда
МассивПараметра.Добавить(СокрП(Параметр.Значение.Наименование));
Иначе
МассивПараметра.Добавить(СокрП(Параметр.Значение));
КонецЕсли;
КонецЦикла;
Иначе
Элемент.Использование = Ложь;
КонецЕсли;
Иначе
Элемент.Использование = Ложь;
КонецЕсли;
// Установим параметры в запрос.
Запрос.УстановитьПараметр("Использовать" + ИмяПараметра, Элемент.Использование);
Запрос.УстановитьПараметр(ИмяПараметра, МассивПараметра);
КонецЦикла;
// Если используется вариант1 отчета, то заполним параметр "ТаблицаПользователей".
Если ИмяВариантаНастроек = "Вариант1" Тогда
Для Каждого Строка Из МассивПользователей Цикл
// В данном случае используются всего два поля таблицы "ПользователиИнформационнойБазы".
// Если Вам неолбходима и другая информация по пользователям, то ее легко здесь добавить
НоваяСтрока = ТаблицаПользователей.Добавить();
НоваяСтрока.Пользователь1С = Строка(Строка.ПолноеИмя);
НоваяСтрока.ПользовательОС = Строка.ПользовательОС;
НоваяСтрока.ПользовательКод = Строка.Имя;
КонецЦикла;
КонецЕсли;
Запрос.УстановитьПараметр("ТаблицаПользователей", ТаблицаПользователей);
// На код процедуры является универсальным (за исключение передачи параметра "ТаблицаПользователей")
// Одно из условий универсальности - установка флага "доступен список значений" и соответственно
// в запросе параметры должны иметь условие "В списке значений"
// Второе условие универсальности - придерживаться правил наименования пользовательских параметров.
// Таким образом, если исключить код создания и заполнения параметра "ТаблицаПользователей", процедура будет универсальна для всех отчетов.
КонецПроцедуры
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
Если Пароль="" Тогда
Сообщить("Необходимо ввести пароль пользователя!");
Отказ = Истина;
КонецЕсли;
// Параметр "ВариантОтчета" используется для определения выполняемого варианта отчета.
// Установив значение параметра на стадии разработки макета отчета, мы легко получаем информацию о
// текущем варианте отчета.
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
Если не Настройки = Неопределено Тогда
// Заполняем реквизит ИмяВариантаНастроек
ИмяВариантаНастроек = Настройки.ПараметрыДанных.Элементы.Найти("ВариантОтчета").Значение;
КонецЕсли;
КонецПроцедуры
Фактически данный отчет можно использовать в качестве шаблона для построения других мульти-базовых отчетов. Если придерживаться предложенных правил наименований пользовательских параметров, редактируется только процедура «ПолучитьТекстЗапроса» и макет отчета. Варианты сформированного отчета представлены на Рис. 10 и Рис. 11.
Рис. 10.
Рис. 11.
6. Вызываем отчет из другой формы
Для вызова отчета нам необходимо сохранить его в справочнике «Дополнительные отчеты и обработки» конфигурации. Далее следуют процедуры и функции модуля формы, кнопки «ОткрытьОтчет».
&НаКлиенте
Процедура ОткрытьОтчет(Команда)
ИмяОтчета = "ПраваДоступаМультибвзоый";
// Для загрузки внешнего отчета в стандартных конфигурациях можно воспользоваться готовой процедурой,
// которая расположена в общем модуле - ДополнительныеОтчетыИОбработки.ПодключитьВнешнююОбработку
// Мы не ищем легких путей и напишем такую процедуру сами.
ИмяОбработки = ЗагрузитьВнешнююОбработку(ИмяОтчета);
Если ИмяОбработки = Неопределено Тогда
Сообщить("Отчет " + ИмяОтчета + "не найден в базе данных!");
Возврат;
КонецЕсли;
// Получаем форму и из нее достаем КомпоновщикНастроекКомпоновкиДанных.ПользовательскиеНастройки
Форма = ПолучитьФорму("ВнешнийОтчет."+ ИмяОбработки +".Форма");
КомпоновщикНастроекКомпоновкиДанных = Форма.Отчет.КомпоновщикНастроек;
ПользовательскиеНастройки = КомпоновщикНастроекКомпоновкиДанных.ПользовательскиеНастройки;
ПолеКомпановкиПараметр = Новый ПараметрКомпоновкиДанных("ИмяПользователя");
Для Каждого Элемент Из ПользовательскиеНастройки.Элементы Цикл
Если ТипЗнч(Элемент) = Тип("ЗначениеПараметраНастроекКомпоновкиДанных") и Элемент.Параметр = ПолеКомпановкиПараметр Тогда
// В функции ПолучитьСписокПользователей() мы возвращаем СписокЗначений с занесенными в него ФИО пользователей с типом строка
// Как заполнить этот список, полностью ваша задача, для примера введено два пользователя
Список = ПолучитьСписокПользователей();
Если Список = Неопределено Тогда
Продолжить;
КонецЕсли;
// Заполняем параметр отчета "ИмяПользователя"
Элемент.Значение = Список;
Элемент.Использование = Истина;
Прервать;
КонецЕсли;
КонецЦикла;
// Получим имя команды дополнительного отчета
ВыполняемаяКоманда = ПолучитьПараметрыКомандыВнешнегоОтчета(ИмяОтчета);
Если ВыполняемаяКоманда = Неопределено Тогда
Возврат;
КонецЕсли;
ПараметрыОбработки = Новый Структура("ИдентификаторКоманды, ДополнительнаяОбработкаСсылка, ИмяФормы, КлючСессии, ПользовательскиеНастройки, СформироватьПриОткрытии");
ПараметрыОбработки.ИдентификаторКоманды = ВыполняемаяКоманда.Идентификатор;
ПараметрыОбработки.ДополнительнаяОбработкаСсылка = ВыполняемаяКоманда.Ссылка;
ПараметрыОбработки.ИмяФормы = Неопределено;
ПараметрыОбработки.КлючСессии = ВыполняемаяКоманда.Ссылка.УникальныйИдентификатор();
//ПараметрыОбработки.СформироватьПриОткрытии = Истина;
ПараметрыОбработки.СформироватьПриОткрытии = Ложь;
// Установим пользовательские настройки в отчет
ПараметрыОбработки.ПользовательскиеНастройки = ПользовательскиеНастройки;
Форма = ПолучитьФорму("ВнешнийОтчет."+ ИмяОбработки +".Форма", ПараметрыОбработки);
//Форма получена, можно заполнить список баз. Для примера добавленно имя двух баз
ТаблицаБаз = Форма.Отчет.ТаблицаБаз;
СтрТабБаз = ТаблицаБаз.Добавить();
СтрТабБаз.ИмяБазы = "База1";
СтрТабБаз.ОписаниеБазы = "Добавлено программно";
СтрТабБаз.Выбор = Истина;
СтрТабБаз = ТаблицаБаз.Добавить();
СтрТабБаз.ИмяБазы = "База2";
СтрТабБаз.ОписаниеБазы = "Добавлено программно";
СтрТабБаз.Выбор = Истина;
// Занесение имен баз непосредственно в таблицу формы отчета не позволяет нам сразу выполнить отчет при открытии
// поэтому строка - ПараметрыОбработки.СформироватьПриОткрытии = Истина закомментирована. При желании этот вопрос
// можно решить путем создания параметра СписокБаз и передавать в этот параметр имена баз. Правда код процедуры ПриКомпоновкеРезультата
// в модуле объекта отчета, необходимо будет переделать. Организовать цикл по выбранным именам баз, по значениям элементов этого параметра.
// Код конечно усложнится, но, что ни сделаешь для удовлетворения желаний заказчика. Зато отчет будет выполняться при открытии отчета.
// Список пользователей заполнен, список баз заполнен - можно открыть отчет.
ОткрытьФорму(Форма);
КонецПроцедуры
&НаСервере
Функция ЗагрузитьВнешнююОбработку(ИмяОбъекта, БезопасныйРежим = Ложь)
// Общий модуль - ДополнительныеОтчетыИОбработки.ПодключитьВнешнююОбработку
УстановитьПривилегированныйРежим(Истина);
// Получаем двоичный код обработки из справочника
ВнешняяОбработкаСсылка = Справочники.ДополнительныеОтчетыИОбработки.НайтиПоНаименованию(ИмяОбъекта);
Если ВнешняяОбработкаСсылка = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
ДвоичныеДанныеОбработки = ВнешняяОбработкаСсылка.ХранилищеОбработки.Получить();
АдресВоВременномХранилище = ПоместитьВоВременноеХранилище(ДвоичныеДанныеОбработки);
Если ВнешняяОбработкаСсылка.Вид = Перечисления.ВидыДополнительныхОтчетовИОбработок.Отчет
ИЛИ ВнешняяОбработкаСсылка.Вид = Перечисления.ВидыДополнительныхОтчетовИОбработок.ДополнительныйОтчет Тогда
Возврат ВнешниеОтчеты.Подключить(АдресВоВременномХранилище,, БезопасныйРежим);
ИначеЕсли ВнешняяОбработкаСсылка.Вид = Перечисления.ВидыДополнительныхОтчетовИОбработок.ДополнительнаяОбработка Тогда
Возврат ВнешниеОбработки.Подключить(АдресВоВременномХранилище,, БезопасныйРежим);
Иначе
Сообщить("Объект не является внешней обработкой или отчетом.", СтатусСообщения.БезСтатуса);
КонецЕсли;
КонецФункции
&НаСервере
Функция ПолучитьПараметрыКомандыВнешнегоОтчета(ИмяОтчета)
ОтчетСсылка = Справочники.ДополнительныеОтчетыИОбработки.НайтиПоНаименованию(ИмяОтчета);
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("Ссылка", ОтчетСсылка);
Запрос.Текст =
"ВЫБРАТЬ
| ТаблицаКоманды.Ссылка,
| ТаблицаКоманды.Идентификатор,
| ТаблицаКоманды.ВариантЗапуска,
| ТаблицаКоманды.Представление КАК Представление,
| ТаблицаКоманды.ПоказыватьОповещение,
| ТаблицаКоманды.Модификатор
|ИЗ
| Справочник.ДополнительныеОтчетыИОбработки.Команды КАК ТаблицаКоманды
|ГДЕ
| ТаблицаКоманды.Ссылка = &Ссылка"
;
Результат = Запрос.Выполнить().Выбрать();
Если Результат.Количество() = 0 Тогда
Возврат Неопределено;
Иначе
// При условии, чио команда отчета 1 одна цикла нет
Результат.Следующий();
ВыполняемаяКоманда = Новый Структура(
"Ссылка, Представление,
|Идентификатор, ВариантЗапуска, ПоказыватьОповещение,
|Модификатор, ОбъектыНазначения, ЭтоОтчет, Вид");
ЗаполнитьЗначенияСвойств(ВыполняемаяКоманда, Результат);
ВыполняемаяКоманда.ЭтоОтчет = Истина;
ВыполняемаяКоманда.Вид = Перечисления.ВидыДополнительныхОтчетовИОбработок.Отчет;
Возврат ВыполняемаяКоманда;
КонецЕсли;
КонецФункции
&НаСервере
Функция ПолучитьСписокПользователей()
СписокЗнч = Новый СписокЗначений;
Пользователь = Справочники.Пользователи.НайтиПоНаименованию("Пользователь 1");
СписокЗнч.Добавить(Пользователь);
Пользователь = Справочники.Пользователи.НайтиПоНаименованию("Пользователь 2");
СписокЗнч.Добавить(Пользователь);
Возврат СписокЗнч;
КонецФункции
На рисунках Рис. 12 и Рис. 13 показаны закладки отчета после нажатия кнопки «ОткрытьОтчет»
Рис. 12.
Рис. 13.
В версии 1.1 добавлена возможность отбора пользователей по логину операционной системы и ФИО пользователя, с условием подобия (Рис. 14). Очень удобное дополнение для администратора. Можно зная часть логина или ФИО пользователя посмотреть его права в базах данных. Файл меньше так как удалены подробные комментарии.
Рис. 14.
Для получения универсальной обработки параметра в коде процедуры, с условием подобно, используется ключевая последовательность символов в имени параметра "подобно" - Рис. 15.
Рис. 15.
Код процедуры УстановитьПараметрыЗапроса, изменен следующим образом:
Процедура УстановитьПараметрыЗапроса(Запрос, ДругаяБаза = Неопределено)
Для каждого Элемент из КомпоновщикНастроек.ПользовательскиеНастройки.Элементы Цикл
Если не Строка(Элемент) = "ЗначениеПараметраНастроекКомпоновкиДанных" Тогда
Продолжить;
КонецЕсли;
ИмяПараметра = Строка(Элемент.Параметр);
Если ИмяПараметра = "ВариантОтчета" Тогда
Продолжить;
КонецЕсли;
Если ДругаяБаза = Неопределено Тогда
МассивПараметра = Новый Массив;
ТаблицаПользователей = Новый ТаблицаЗначений;
ТаблицаПользователей.Колонки.Добавить("ПользовательОС", Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(64)));
ТаблицаПользователей.Колонки.Добавить("Пользователь1С", Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(64)));
ТаблицаПользователей.Колонки.Добавить("ПользовательКод", Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(64)));
Если ИмяВариантаНастроек = "Вариант1" Тогда
МассивПользователей = ПользователиИнформационнойБазы.ПолучитьПользователей();
КонецЕсли;
Иначе
МассивПараметра = ДругаяБаза.NewObject("Массив");
ТаблицаПользователей = ДругаяБаза.NewObject("ТаблицаЗначений");
ТаблицаПользователей.Колонки.Добавить("ПользовательОС", ДругаяБаза.NewObject("ОписаниеТипов", "Строка", ДругаяБаза.NewObject("КвалификаторыСтроки", 64)));
ТаблицаПользователей.Колонки.Добавить("Пользователь1С", ДругаяБаза.NewObject("ОписаниеТипов", "Строка", ДругаяБаза.NewObject("КвалификаторыСтроки", "64")));
ТаблицаПользователей.Колонки.Добавить("ПользовательКод", ДругаяБаза.NewObject("ОписаниеТипов", "Строка", ДругаяБаза.NewObject("КвалификаторыСтроки", "64")));
Если ИмяВариантаНастроек = "Вариант1" Тогда
МассивПользователей = ДругаяБаза.ПользователиИнформационнойБазы.ПолучитьПользователей();
КонецЕсли;
КонецЕсли;
Если Элемент.Использование и не Элемент.Значение = Неопределено Тогда
ТипЗнчЭлемента = ТипЗнч(Элемент.Значение);
Попытка
ТипСправочник = Тип("СправочникСсылка." + ИмяПараметра);
Исключение
ТипСправочник = Неопределено;
КонецПопытки;
Если ТипЗнчЭлемента = Тип("Строка") Тогда
ЭлементЗначение = СокрЛП(Элемент.Значение);
// Обработка заполненных параметров с условием "подобно"
Если СтрНайти(ИмяПараметра, "подобно")>1 Тогда
ЭлементЗначение = "%"+ЭлементЗначение+"%";
Запрос.УстановитьПараметр("Использовать" + ИмяПараметра, Истина);
Запрос.УстановитьПараметр(ИмяПараметра, ЭлементЗначение);
Продолжить;
КонецЕсли;
МассивПараметра.Добавить(ЭлементЗначение);
ИначеЕсли ТипЗнчЭлемента = ТипСправочник Тогда
МассивПараметра.Добавить(СокрП(Элемент.Значение.Наименование));
ИначеЕсли Элемент.Значение.Количество() > 0 Тогда
Для каждого Параметр из Элемент.Значение Цикл
Если ТипСправочник <> Неопределено и ТипЗнч(Параметр.Значение) = ТипСправочник Тогда
МассивПараметра.Добавить(СокрП(Параметр.Значение.Наименование));
Иначе
МассивПараметра.Добавить(СокрП(Параметр.Значение));
КонецЕсли;
КонецЦикла;
Иначе
Элемент.Использование = Ложь;
КонецЕсли;
Иначе
Элемент.Использование = Ложь;
// Обработка пустых параметров с условием "подобно"
Если СтрНайти(ИмяПараметра, "подобно")>1 Тогда
Запрос.УстановитьПараметр("Использовать" + ИмяПараметра, Элемент.Использование);
Запрос.УстановитьПараметр(ИмяПараметра, "% %");
Продолжить;
КонецЕсли;
КонецЕсли;
Запрос.УстановитьПараметр("Использовать" + ИмяПараметра, Элемент.Использование);
Запрос.УстановитьПараметр(ИмяПараметра, МассивПараметра);
КонецЦикла;
Если ИмяВариантаНастроек = "Вариант1" Тогда
Для Каждого Строка Из МассивПользователей Цикл
НоваяСтрока = ТаблицаПользователей.Добавить();
НоваяСтрока.Пользователь1С = Строка(Строка.ПолноеИмя);
НоваяСтрока.ПользовательОС = Строка.ПользовательОС;
НоваяСтрока.ПользовательКод = Строка.Имя;
КонецЦикла;
КонецЕсли;
Запрос.УстановитьПараметр("ТаблицаПользователей", ТаблицаПользователей);
КонецПроцедуры
Соответственно в запросе устанавливаем отборы на выходной запрос:
|ГДЕ
| (НЕ &ИспользоватьПользовательОСподобно
| ИЛИ ПользователиОС.ПользовательОС ПОДОБНО &ПользовательОСподобно)
| И (НЕ &ИспользоватьФИОпользователя_подобно
| ИЛИ ПраваПользователей.Пользователь ПОДОБНО &ФИОпользователя_подобно)
Таким образом мы можем добавлять любые отборы по символьным полям запроса с условием - ПОДОБНО.