Создание мульти-базовых отчетов. Все, что вы хотели об этом знать

Программирование - Практика программирования

В статье описано создание отчета по данным из клиент-серверных баз кластера 1С. Подключение к базам осуществляется через com-соединение. В качестве примера создается отчет «Права доступа мульти-базовый», который оформлен в виде внешнего отчета с двумя вариантами выполнения. Для построения собственного отчета на базе описанного достаточно будет изменить функции «ПолучитьТекстЗапроса», в модуле объекта отчета и построить свой макет запроса. Также описан код вызова отчета путем нажатия кнопки с передачей параметров в макет запроса и форму отчета. Описание примера отчета показывает, что создание отчетов по нескольким базам данных в 1С, не является сверхсложной задачей и достигается простыми методами

     Рассмотрим пример создания внешнего отчета с двумя вариантами выполнения. Примеры разрабатывались на платформе 8.3.10.2561. Варианты созданы на базе таблиц и показывают "Роли пользователей" группируемые по "Профилям" с разворотом по базам данных и подразделениям. В первом варианте колонками отчета являются базы данных с разворотом по подразделениям. Этот вариант удобен для сравнения ролей одного пользователя по базам. Во втором варианте колонками отчета являются сами пользователи и этот вариант удобен для сравнения ролей пользователей с по одной базе данных. И так приступим.

  1. Создание реквизитов объекта отчета

      Для подключения к кластеру 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.

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

Наименование Файл Версия Размер
ПраваДоступаМультибвзоыйОтчет
.erf 21,09Kb
22.03.18
1
.erf 21,09Kb 1 Скачать
ОткрытьДополнительныйОтчетСПараметрами
.epf 7,84Kb
22.03.18
1
.epf 7,84Kb 1 Скачать

См. также

В этой теме еще нет сообщений.
Оставьте свое сообщение