///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2024, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
	
#Область СлужебныеПроцедурыИФункции

// Выполняет подготовку структуры КомпонентыОбмена.
// Параметры:
//   НаправлениеОбмена - Строка - отправка или Получение.
//   ВерсияФорматаОбменаПриЗагрузке - Строка - версия формата, которая должна применяться при загрузке данных.
//
// Возвращаемое значение:
//   Структура - компоненты обмена.
//
Функция КомпонентыОбмена(НаправлениеОбмена, ВерсияФорматаОбменаПриЗагрузке = "", РасширениеФорматаПриЗагрузке = "") 
	
	КомпонентыОбмена     = ОбменДаннымиXDTOСервер.ИнициализироватьКомпонентыОбмена(НаправлениеОбмена);
	ТекВерсияФормата     = ?(НаправлениеОбмена = "Отправка", ВерсияФормата, ВерсияФорматаОбменаПриЗагрузке);
	ТекРасширениеФормата = ?(НаправлениеОбмена = "Отправка", РасширениеФормата, РасширениеФорматаПриЗагрузке);
	Если ЗначениеЗаполнено(УзелОбмена) И НаправлениеОбмена = "Отправка" Тогда
		КомпонентыОбмена.ЭтоОбменЧерезПланОбмена = Истина;
		КомпонентыОбмена.УзелКорреспондента = УзелОбмена;
		КомпонентыОбмена.ТаблицаПравилаРегистрацииОбъектов = ОбменДаннымиXDTOСервер.ПравилаРегистрацииОбъектов(УзелОбмена);
		КомпонентыОбмена.СвойстваУзлаПланаОбмена = ОбменДаннымиXDTOСервер.СвойстваУзлаПланаОбмена(УзелОбмена);
		КомпонентыОбмена.ОбменЧерезОбработкуВыгрузкаЗагрузкаED = Истина;
	Иначе
		КомпонентыОбмена.ЭтоОбменЧерезПланОбмена = Ложь;
	КонецЕсли;
	КомпонентыОбмена.КлючСообщенияЖурналаРегистрации = НСтр("ru = 'Перенос данных через буфер обмена'", ОбщегоНазначения.КодОсновногоЯзыка());
	КомпонентыОбмена.ВерсияФорматаОбмена = ТекВерсияФормата;
	КомпонентыОбмена.XMLСхема = "http://v8.1c.ru/edi/edi_stnd/EnterpriseData/" + ТекВерсияФормата;
	ОбменДаннымиXDTOСервер.ВключитьПространствоИмен(КомпонентыОбмена, ТекРасширениеФормата, "ext");
	
	МенеджерОбменаВнутренний = Ложь;
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		МенеджерОбменаВнутренний = Истина;
	ИначеЕсли ЗначениеЗаполнено(ПутьКМенеджеруОбменаВыгрузки)
		Или ЗначениеЗаполнено(ПутьКМенеджеруОбменаЗагрузки) Тогда
		ВызватьИсключение
			НСтр("ru = 'Внешняя обработка отладки, загружаемая из файла на диске, не поддерживается.'");
	ИначеЕсли ЗначениеЗаполнено(УзелОбмена)
		И ОбщегоНазначения.ЕстьРеквизитОбъекта("ПутьКМенеджеруОбмена", УзелОбмена.Метаданные()) Тогда
		ПутьКМенеджеруОбмена = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(УзелОбмена, "ПутьКМенеджеруОбмена");
		Если ЗначениеЗаполнено(ПутьКМенеджеруОбмена) Тогда
			ВызватьИсключение
				НСтр("ru = 'Внешняя обработка отладки, загружаемая из файла на диске, не поддерживается.'");
		Иначе
			МенеджерОбменаВнутренний = Истина;
		КонецЕсли;
	Иначе
		МенеджерОбменаВнутренний = Истина;
	КонецЕсли;
	Если МенеджерОбменаВнутренний Тогда
		ВерсииФорматаОбмена = Новый Соответствие;
		ОбменДаннымиПереопределяемый.ПриПолученииДоступныхВерсийФормата(ВерсииФорматаОбмена);
		КомпонентыОбмена.МенеджерОбмена = ВерсииФорматаОбмена.Получить(ТекВерсияФормата);
		Если КомпонентыОбмена.МенеджерОбмена = Неопределено Тогда
			ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Не поддерживается версия формата обмена: <%1>.'"), ТекВерсияФормата);
		КонецЕсли;
	КонецЕсли;
	
	ОбменДаннымиXDTOСервер.ИнициализироватьТаблицыПравилОбмена(КомпонентыОбмена);
	
	Если КомпонентыОбмена.ЭтоОбменЧерезПланОбмена Тогда
		ОбменДаннымиXDTOСервер.ЗаполнитьСтруктуруНастроекXDTO(КомпонентыОбмена);
		ОбменДаннымиXDTOСервер.ЗаполнитьПоддерживаемыеОбъектыXDTO(КомпонентыОбмена);
	КонецЕсли;
	
	ОбменДаннымиXDTOСервер.ПослеИнициализацииКомпонентыОбмена(КомпонентыОбмена);
	
	Возврат КомпонентыОбмена;
	
КонецФункции

// Выполняет выгрузку данных в соответствии с настройками.
// Параметры:
//   СтруктураПараметров - Структура - параметры выполнения обработки:
//    * МестоВыгрузки - Число - 0 (в файл), 1 (в текст).
//    * ЭтоФоновоеЗадание - Булево - признак вызова процедуры из фонового задания.
//      При запуске из фонового задания в структуре содержатся данные для заполнения реквизитов объекта.
//   АдресДляРазмещенияРезультата - Строка - адрес во временном хранилище.
// 
// Возвращаемое значение:
//   Строка - адрес во временном хранилище, куда помещен результат выгрузки.
//
Функция РезультатВыгрузкиВXML(СтруктураПараметров, АдресДляРазмещенияРезультата = Неопределено) Экспорт
	
	МестоВыгрузки = СтруктураПараметров.МестоВыгрузки;
	
	Если СтруктураПараметров.Свойство("ЭтоФоновоеЗадание") Тогда
		ЗаполнитьЗначенияСвойств(ЭтотОбъект, СтруктураПараметров);
	КонецЕсли;
	
	СписокДополнениеКВыгрузке.Очистить();
	ЗаполнитьСписокОбъектовКВыгрузке();
	
	АдресНаСервере = ПолучитьИмяВременногоФайла("xml");
	РезультатВыгрузки = ВыгрузитьДанныеВXML(АдресНаСервере);
	
	Если РезультатВыгрузки.ЕстьОшибки Тогда
		ТекстСообщения = НСтр("ru = 'В ходе выполнения операции возникли ошибки'") + ": "
			+ Символы.ПС + РезультатВыгрузки.ТекстОшибки
			+ Символы.ПС + НСтр("ru = 'Данные могут быть выгружены не полностью.'");
		ОбщегоНазначения.СообщитьПользователю(ТекстСообщения);
	ИначеЕсли Не РезультатВыгрузки.ЕстьВыгруженныеОбъекты Тогда
		ТекстСообщения = НСтр("ru = 'Не найдено ни одного объекта к выгрузке.'");
		ОбщегоНазначения.СообщитьПользователю(ТекстСообщения);
	КонецЕсли;
	
	Если МестоВыгрузки = 0 Тогда // в файл
		
		СодержимоеХранилища = Новый ДвоичныеДанные(АдресНаСервере);
		
	Иначе // в текст
		
		ТекстовыйДокумент = Новый ТекстовыйДокумент;
		ТекстовыйДокумент.Прочитать(АдресНаСервере);
		СодержимоеХранилища = ТекстовыйДокумент.ПолучитьТекст();
		
	КонецЕсли;
	
	Если АдресДляРазмещенияРезультата = Неопределено Тогда
		
		АдресХранения = ПоместитьВоВременноеХранилище(СодержимоеХранилища);
		
	Иначе
		
		ПоместитьВоВременноеХранилище(СодержимоеХранилища, АдресДляРазмещенияРезультата);
		АдресХранения = АдресДляРазмещенияРезультата;
		
	КонецЕсли;
	УдалитьФайлы(АдресНаСервере);
	
	Возврат АдресХранения;
	
КонецФункции

// Выгружает объекты по настройкам, указанным в реквизитах обработки и возвращает результат выгрузки.
// 
// Возвращаемое значение:
//   Структура - структура с результатом выгрузки:
//     * ТекстВыгрузки - Строка - текст сообщения обмена.
//     * ЕстьВыгруженныеОбъекты - Булево - Истина, если выгружен хотя бы один объект.
//     * ЕстьОшибки - Булево - Истина, если при выгрузке произошли ошибки.
//     * ТекстОшибки - Строка - текст ошибки при выгрузке.
//     * ВыгруженныеОбъекты - Массив - массив выгруженных объектов по настройкам обработки.
//     * ВыгруженныеПоСсылкеОбъекты - Массив - массив выгруженных объектов по ссылкам.
//
Функция ВыгрузитьДанныеВXML(ИмяФайлаОбмена = "")
	
	КомпонентыОбмена = КомпонентыОбмена("Отправка");
	
	// Открываем файл обмена.
	ОбменДаннымиXDTOСервер.ОткрытьФайлВыгрузки(КомпонентыОбмена, ИмяФайлаОбмена);
	
	ЕстьВыгруженныеОбъекты      = Ложь;
	ЕстьОшибкиПередКонвертацией = Ложь;
	
	УстановитьПривилегированныйРежим(Истина);
	
	Попытка
		КомпонентыОбмена.МенеджерОбмена.ПередКонвертацией(КомпонентыОбмена);
	Исключение
		ЕстьОшибкиПередКонвертацией = Истина;
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Событие: %1.
				|Обработчик: ПередКонвертацией.
				|
				|Ошибка выполнения обработчика.
				|%2.'"),
			КомпонентыОбмена.НаправлениеОбмена,
			ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
		ОбменДаннымиXDTOСервер.ЗаписатьВПротоколВыполнения(КомпонентыОбмена, ТекстОшибки);
	КонецПопытки;
	
	Если ЗначениеЗаполнено(УзелОбмена)
		И Не ЕстьОшибкиПередКонвертацией Тогда
		
		СоставПланаОбмена = УзелОбмена.Метаданные().Состав;
		
		// Внутреннее соединение с основной таблицей необходимо для исключения из выгрузки битых ссылок.
		ШаблонТекстаЗапроса =
		"ВЫБРАТЬ 
		|	ТаблицаИзменений.Ссылка
		|ИЗ 
		|	&ПолноеИмяТаблицыМетаданныхИзменения КАК ТаблицаИзменений
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ &ПолноеИмяТаблицыМетаданных КАК ОсновнаяТаблица
		|		ПО ОсновнаяТаблица.Ссылка = ТаблицаИзменений.Ссылка
		|ГДЕ ТаблицаИзменений.Узел = &Узел";
		
		Запрос = Новый Запрос;
		Запрос.УстановитьПараметр("Узел", УзелОбмена);
		
		Для Каждого ЭлементСостава Из СоставПланаОбмена Цикл
			
			Если Не ОбщегоНазначения.ЭтоОбъектСсылочногоТипа(ЭлементСостава.Метаданные) Тогда
				
				Продолжить;
				
			КонецЕсли;
			
			ПолноеИмяОбъекта = ЭлементСостава.Метаданные.ПолноеИмя();
			ПравилоОбработки = КомпонентыОбмена.ПравилаОбработкиДанных.Найти(ЭлементСостава.Метаданные, "ОбъектВыборкиМетаданные");
			Если ПравилоОбработки = Неопределено Тогда
				
				Продолжить;
				
			КонецЕсли;
			
			ПолноеИмяОбъектаИзменения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("%1.Изменения", ПолноеИмяОбъекта);
			ТекстЗапроса = СтрЗаменить(ШаблонТекстаЗапроса, "&ПолноеИмяТаблицыМетаданныхИзменения", ПолноеИмяОбъектаИзменения);
			Запрос.Текст = СтрЗаменить(ТекстЗапроса, "&ПолноеИмяТаблицыМетаданных", ПолноеИмяОбъекта);
			
			Выборка = Запрос.Выполнить().Выбрать();
			Пока Выборка.Следующий() Цикл
				ОбменДаннымиXDTOСервер.ВыгрузкаОбъектаВыборки(КомпонентыОбмена, Выборка.Ссылка.ПолучитьОбъект(), ПравилоОбработки);
				ЕстьВыгруженныеОбъекты = ЕстьВыгруженныеОбъекты Или Не КомпонентыОбмена.ФлагОшибки;
			КонецЦикла;
		КонецЦикла;
	КонецЕсли;
	
	Если СписокДополнениеКВыгрузке.Количество() > 0 Тогда
		
		СобытиеНачало = НСтр("ru='ВыгрузкаЗагрузкаEnterpriseData.Начало'", ОбщегоНазначения.КодОсновногоЯзыка());
		СобытиеОкончание = НСтр("ru='ВыгрузкаЗагрузкаEnterpriseData.Окончание'", ОбщегоНазначения.КодОсновногоЯзыка());
		
		Для Каждого ЭлементСписка Из СписокДополнениеКВыгрузке Цикл
			СсылкаВыгрузки = ЭлементСписка.Значение;
			МетаданныеСсылкиВыгрузки = СсылкаВыгрузки.Метаданные();
			ЛогированиеПроцесса(СобытиеНачало, МетаданныеСсылкиВыгрузки, СсылкаВыгрузки);
			ПравилоОбработки = КомпонентыОбмена.ПравилаОбработкиДанных.Найти(МетаданныеСсылкиВыгрузки, "ОбъектВыборкиМетаданные");
			Если ПравилоОбработки = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			Если ОбщегоНазначения.ЭтоОбъектСсылочногоТипа(МетаданныеСсылкиВыгрузки) Тогда
				ОбъектДляВыгрузки = СсылкаВыгрузки.ПолучитьОбъект();
			Иначе
				ОбъектДляВыгрузки = СсылкаВыгрузки;
			КонецЕсли;
			ОбменДаннымиXDTOСервер.ВыгрузкаОбъектаВыборки(КомпонентыОбмена, ОбъектДляВыгрузки, ПравилоОбработки);
			ЛогированиеПроцесса(СобытиеОкончание, МетаданныеСсылкиВыгрузки, СсылкаВыгрузки);
			ЕстьВыгруженныеОбъекты = Истина;
		КонецЦикла;
	КонецЕсли;
	
	Если Не ЕстьОшибкиПередКонвертацией Тогда
		Попытка
			КомпонентыОбмена.МенеджерОбмена.ПослеКонвертации(КомпонентыОбмена);
		Исключение
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Событие: %1.
					|Обработчик: ПослеКонвертации.
					|
					|Ошибка выполнения обработчика.
					|%2.'"),
				КомпонентыОбмена.НаправлениеОбмена,
				ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
			ОбменДаннымиXDTOСервер.ЗаписатьВПротоколВыполнения(КомпонентыОбмена, ТекстОшибки);
		КонецПопытки;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Ложь);
	
	КомпонентыОбмена.ФайлОбмена.ЗаписатьКонецЭлемента(); // Body
	КомпонентыОбмена.ФайлОбмена.ЗаписатьКонецЭлемента(); // Message
	КомпонентыОбмена.ФайлОбмена.Закрыть();
	
	РезультатВыгрузки = Новый Структура;
	РезультатВыгрузки.Вставить("ЕстьВыгруженныеОбъекты",     ЕстьВыгруженныеОбъекты);
	РезультатВыгрузки.Вставить("ЕстьОшибки",                 КомпонентыОбмена.ФлагОшибки);
	РезультатВыгрузки.Вставить("ТекстОшибки",                КомпонентыОбмена.СтрокаСообщенияОбОшибке);
	РезультатВыгрузки.Вставить("ВыгруженныеОбъекты",         КомпонентыОбмена.ВыгруженныеОбъекты);
	РезультатВыгрузки.Вставить("ВыгруженныеПоСсылкеОбъекты", КомпонентыОбмена.ВыгруженныеПоСсылкеОбъекты);
	
	Возврат РезультатВыгрузки;
	
КонецФункции

// Выполняет загрузку сообщения.
// Параметры:
//   СтруктураПараметров - Структура:
//    * ТекстXML - Строка - сообщение которое следует загрузить (в случае загрузки из текста).
//    * АдресНаСервере - Строка - имя временного файла с данными для загрузки (при загрузке из файла)
//   АдресРезультата - Строка - адрес для размещения результата, при запуске из фонового задания.
//
Процедура ЗагрузкаСообщения(СтруктураПараметров, АдресРезультата) Экспорт
	
	ЧтениеXML = Новый ЧтениеXML;
	
	Если СтруктураПараметров.Свойство("ЭтоФоновоеЗадание") Тогда
		
		ЗаполнитьЗначенияСвойств(ЭтотОбъект, СтруктураПараметров);
		
	КонецЕсли;
	
	АдресНаСервере = "";
	Если СтруктураПараметров.Свойство("АдресНаСервере", АдресНаСервере) Тогда
		
		ЧтениеXML.ОткрытьФайл(СтруктураПараметров.АдресНаСервере);
		
	Иначе
		
		ЧтениеXML.УстановитьСтроку(СтруктураПараметров.ТекстXML);
		
	КонецЕсли;
	
	РезультатЗагрузки = ЗагрузитьДанныеИзXML(ЧтениеXML);
	ЧтениеXML.Закрыть();
	
	Попытка
		
		Если ЗначениеЗаполнено(АдресНаСервере) Тогда
		
			УдалитьФайлы(АдресНаСервере);
			
		КонецЕсли;
		
	Исключение
		
		СобытиеЖурнала = ОбменДаннымиСервер.СобытиеЖурналаРегистрацииОбменДанными();
		ОписаниеОшибки = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
		
		ЗаписьЖурналаРегистрации(СобытиеЖурнала, УровеньЖурналаРегистрации.Ошибка, , , ОписаниеОшибки);
		
	КонецПопытки;
	
	Если РезультатЗагрузки.ЕстьОшибки Тогда
		
		ВызватьИсключение РезультатЗагрузки.ТекстОшибки;
		
	КонецЕсли;

КонецПроцедуры

// Загружает данные из сообщения обмена.
//
// Параметры:
//  ЧтениеXML	 - ЧтениеXML - инициализированный сообщением обмена объект ЧтениеXML.
// 
// Возвращаемое значение:
//   Структура:
//    * ЕстьОшибки - Булево - признак, что во время загрузки сообщения обмена возникли ошибки.
//    * ТекстОшибки - Строка - текст сообщения об ошибке.
//    * ЗагруженныеОбъекты - Массив - массив загруженных объектов.
//
Функция ЗагрузитьДанныеИзXML(ЧтениеXML)
	
	РезультатЗагрузки = Новый Структура;
	РезультатЗагрузки.Вставить("ЕстьОшибки", Ложь);
	РезультатЗагрузки.Вставить("ТекстОшибки", "");
	РезультатЗагрузки.Вставить("ЗагруженныеОбъекты", Новый Массив);
	
	ЧтениеXML.Прочитать(); // Message
	ЧтениеXML.Прочитать(); // Header
	ЗаголовокСообщенияXDTO = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML, ФабрикаXDTO.Тип("http://www.1c.ru/SSL/Exchange/Message", "Header"));
	Если ЧтениеXML.ТипУзла <> ТипУзлаXML.НачалоЭлемента
		Или ЧтениеXML.ЛокальноеИмя <> "Body" Тогда
		
		РезультатЗагрузки.ЕстьОшибки = Истина;
		РезультатЗагрузки.ТекстОшибки = НСтр("ru = 'Ошибка чтения сообщения загрузки. Неверный формат сообщения.'");
		
		Возврат РезультатЗагрузки;
	КонецЕсли;
	
	СтруктураФормата = СтрРазделить(ЗаголовокСообщенияXDTO.Format, " ", Ложь);
	
	ФорматОбмена = РазложитьФорматОбмена(СтруктураФормата[0]);
	ВерсияФорматаДляЗагрузки = ФорматОбмена.Версия;
	РасширениеФорматаДляЗагрузки = "";
	
	Если СтруктураФормата.Количество() > 0 Тогда
		РасширениеФорматаДляЗагрузки = СтруктураФормата[СтруктураФормата.ВГраница()];
	КонецЕсли;
	
	КомпонентыОбмена = КомпонентыОбмена("Получение", ВерсияФорматаДляЗагрузки, РасширениеФорматаДляЗагрузки);
	
	ЧтениеXML.Прочитать(); // Body
	КомпонентыОбмена.Вставить("ФайлОбмена", ЧтениеXML);
	
	УстановитьПривилегированныйРежим(Истина);
	ОбменДаннымиСлужебный.ОтключитьОбновлениеКлючейДоступа(Истина);
	Попытка
		ОбменДаннымиXDTOСервер.ПроизвестиЧтениеДанных(КомпонентыОбмена);
		ОбменДаннымиСлужебный.ОтключитьОбновлениеКлючейДоступа(Ложь);
	Исключение
		ОбменДаннымиСлужебный.ОтключитьОбновлениеКлючейДоступа(Ложь);
		ВызватьИсключение;
	КонецПопытки;
	УстановитьПривилегированныйРежим(Ложь);
	
	Если КомпонентыОбмена.ФлагОшибки Тогда
		РезультатЗагрузки.ЕстьОшибки = Истина;
		РезультатЗагрузки.ТекстОшибки = КомпонентыОбмена.СтрокаСообщенияОбОшибке;
	КонецЕсли;
	
	РезультатЗагрузки.ЗагруженныеОбъекты = КомпонентыОбмена.ЗагруженныеОбъекты.ВыгрузитьКолонку("СсылкаНаОбъект");
	
	Возврат РезультатЗагрузки;
	
КонецФункции

// Заполняет перечень доступных к выгрузке объектов метаданных в соответствии с компонентами обмена.
Процедура ЗаполнениеПравилВыгрузки() Экспорт
	КомпонентыОбмена = КомпонентыОбмена("Отправка");
	ТаблицаПравилВыгрузки.Строки.Очистить();
	УзелДереваСправочники = ТаблицаПравилВыгрузки.Строки.Добавить();
	УзелДереваСправочники.ЭтоГруппа = Истина;
	УзелДереваСправочники.Наименование = НСтр("ru = 'Справочники'");
	
	УзелДереваДокументы = ТаблицаПравилВыгрузки.Строки.Добавить();
	УзелДереваДокументы.ЭтоГруппа = Истина;
	УзелДереваДокументы.Наименование = НСтр("ru = 'Документы'");
	УзелДереваДокументы.ОтборПоПериоду = Истина;

	УзелДереваПВХ = ТаблицаПравилВыгрузки.Строки.Добавить();
	УзелДереваПВХ.ЭтоГруппа = Истина;
	УзелДереваПВХ.Наименование = НСтр("ru = 'Планы видов характеристик'");
	
	Для Каждого СтрокаПОД Из КомпонентыОбмена.ПравилаОбработкиДанных Цикл
		ТекМД = СтрокаПОД.ОбъектВыборкиМетаданные;
		Если ТекМД = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		
		ТекИмя = ТекМД.Имя;
		ТекСиноним = ТекМД.Синоним;
		ПолноеИмяМДСтрокой = "";
		НовСтрока = Неопределено;
		Если Метаданные.Справочники.Содержит(ТекМД) Тогда
			НовСтрока = УзелДереваСправочники.Строки.Добавить();
			ПолноеИмяМДСтрокой = "Справочник." + ТекИмя;
			Представление = НСтр("ru = 'Справочник %1'");
		ИначеЕсли Метаданные.Документы.Содержит(ТекМД) Тогда
			НовСтрока = УзелДереваДокументы.Строки.Добавить();
			НовСтрока.ОтборПоПериоду = Истина;
			ПолноеИмяМДСтрокой = "Документ." + ТекИмя;
			Представление = НСтр("ru = 'Документ %1'");
		ИначеЕсли  Метаданные.ПланыВидовХарактеристик.Содержит(ТекМД) Тогда
			НовСтрока = УзелДереваПВХ.Строки.Добавить();
			ПолноеИмяМДСтрокой = "ПланВидовХарактеристик." + ТекИмя;
			Представление = НСтр("ru = 'План видов характеристик %1'");
		Иначе
			// Выгрузка других объектов метаданных не поддерживается.
			Продолжить;
		КонецЕсли;
		Представление = СтрЗаменить(Представление, "%1", ТекСиноним);
		НовСтрока.ЭтоГруппа = Ложь;
		НовСтрока.Наименование = ТекСиноним;
		НовСтрока.ПолноеИмяМетаданных = ПолноеИмяМДСтрокой;
		НовСтрока.Представление = Представление;
		СтруктураОтбор = Новый Структура("ПолноеИмяМетаданных", ПолноеИмяМДСтрокой);
		НастройкиОтбора = ДополнительнаяРегистрация.НайтиСтроки(СтруктураОтбор);
		НовСтрока.ПредставлениеОтбора = "";
		Если НастройкиОтбора.Количество() > 0 Тогда
			НовСтрока.Включить = Истина;
			Для Каждого ТекНастройка Из НастройкиОтбора Цикл
				Если ЗначениеЗаполнено(ТекНастройка.ОтборСтрокой) Тогда
					НовСтрока.ПредставлениеОтбора = НовСтрока.ПредставлениеОтбора + ", "+ СокрЛП(ТекНастройка.ОтборСтрокой);
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
	КонецЦикла;
	// Удалим лишние ветки
	Если УзелДереваСправочники.Строки.Количество() = 0 Тогда
		ТаблицаПравилВыгрузки.Строки.Удалить(УзелДереваСправочники);
	КонецЕсли;
	Если УзелДереваДокументы.Строки.Количество() = 0 Тогда
		ТаблицаПравилВыгрузки.Строки.Удалить(УзелДереваДокументы);
	КонецЕсли;
	Если УзелДереваПВХ.Строки.Количество() = 0 Тогда
		ТаблицаПравилВыгрузки.Строки.Удалить(УзелДереваПВХ);
	КонецЕсли;
КонецПроцедуры

//  Возвращает компоновщик для отборов одного вида метаданных.
//
//  Параметры:
//      ПолноеИмяМетаданных  - Строка - имя таблицы для построения компоновщика. Возможно там будут 
//                                      идентификаторы для "всех документов" или "всех справочников"
//                                      или ссылка на группу.
//      Представление        - Строка - представление объекта в отборе.
//      Отбор                - ОтборКомпоновкиДанных - отбор компоновки для заполнения.
//      АдресСохраненияСхемы - Строка
//                           - УникальныйИдентификатор - адрес временного хранилища для сохранения схемы
//                             компоновки.
//
// Возвращаемое значение:
//      КомпоновщикНастроекКомпоновкиДанных - инициализированный компоновщик.
//
Функция КомпоновщикНастроекПоИмениТаблицы(ПолноеИмяМетаданных, Представление = Неопределено, Отбор = Неопределено, АдресСохраненияСхемы = Неопределено) Экспорт
	
	СхемаКомпоновки = Новый СхемаКомпоновкиДанных;
	
	Источник = СхемаКомпоновки.ИсточникиДанных.Добавить();
	Источник.Имя = "Источник";
	Источник.ТипИсточникаДанных = "local";
	
	ДобавляемыеТаблицы = СоставУкрупненнойГруппыМетаданных(ПолноеИмяМетаданных);
	
	Для Каждого ИмяТаблицы Из ДобавляемыеТаблицы Цикл
		ДобавитьНаборВСхемуКомпоновки(СхемаКомпоновки, ИмяТаблицы, Представление);
	КонецЦикла;
	
	Компоновщик = Новый КомпоновщикНастроекКомпоновкиДанных;
	Компоновщик.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(
		ПоместитьВоВременноеХранилище(СхемаКомпоновки, АдресСохраненияСхемы)));
	
	Если Отбор <> Неопределено Тогда
		ДобавитьЗначенияОтбораКомпоновки(Компоновщик.Настройки.Отбор.Элементы, Отбор.Элементы);
		Компоновщик.Восстановить(СпособВосстановленияНастроекКомпоновкиДанных.ПроверятьДоступность);
	КонецЕсли;
	
	Возврат Компоновщик;
КонецФункции

// Возвращает массив имен таблиц метаданных по составному типу параметра "ПолноеИмяМетаданных".
//
// Параметры:
//      ПолноеИмяМетаданных - Строка
//                          - ДеревоЗначений - имя таблицы (например "Справочник.Валюты") или имя
//                            предопределенной группы
//                            (например "ВсеДокументы") или дерево значений, описывающее группу.
//
// Возвращаемое значение:
//      Массив - имена метаданных.
//
Функция СоставУкрупненнойГруппыМетаданных(ПолноеИмяМетаданных) 
	ОбходМетаданных = Ложь;
	ТаблицыСостава = Новый Массив;
	Если ТипЗнч(ПолноеИмяМетаданных) <> Тип("Строка") Тогда
		// Дерево значений с группой отбора. Корень - описание, в строках - имена метаданных.
		Для Каждого СтрокаГруппы Из ПолноеИмяМетаданных.Строки Цикл
			Для Каждого СтрокаСоставаГруппы Из СтрокаГруппы.Строки Цикл
				ТаблицыСостава.Добавить(СтрокаСоставаГруппы.ПолноеИмяМетаданных);
			КонецЦикла;
		КонецЦикла;
		
	ИначеЕсли ПолноеИмяМетаданных = "ВсеДокументы" Тогда
		ОбходМетаданных = Истина;
		МетаОбъекты = Метаданные.Документы;
	ИначеЕсли ПолноеИмяМетаданных = "ВсеСправочники" Тогда
		ОбходМетаданных = Истина;
		МетаОбъекты = Метаданные.Справочники;
	ИначеЕсли ПолноеИмяМетаданных = "ВсеПланыВидовХарактеристик" Тогда
		ОбходМетаданных = Истина;
		МетаОбъекты = Метаданные.ПланыВидовХарактеристик;
	Иначе
		// Одиночная таблица метаданных.
		ТаблицыСостава.Добавить(ПолноеИмяМетаданных);
	КонецЕсли;
	Если ОбходМетаданных Тогда
		Для Каждого МетаОбъект Из МетаОбъекты Цикл
			ТаблицыСостава.Добавить(МетаОбъект.ПолноеИмя());
		КонецЦикла;
	КонецЕсли;
	
	Возврат ТаблицыСостава;
КонецФункции

// Возвращает описание периода и отбора строкой.
//
//  Параметры:
//      Период - СтандартныйПериод     - период для описания отбора.
//      Отбор  - ОтборКомпоновкиДанных - отбор компоновки данных для описания.
//      ОписаниеПустогоОтбора - Строка - значение, возвращаемое в случае пустого отбора.
//  Возвращаемое значение:
//   Строка - строковое представление отбора.
//
Функция ПредставлениеОтбора(Период, Отбор, Знач ОписаниеПустогоОтбора = Неопределено) Экспорт
	НашОтбор = ?(ТипЗнч(Отбор)=Тип("КомпоновщикНастроекКомпоновкиДанных"), Отбор.Настройки.Отбор, Отбор);
	
	ПериодСтрокой = ?(ЗначениеЗаполнено(Период), Строка(Период), "");
	ОтборСтрокой  = Строка(НашОтбор);
	
	Если ПустаяСтрока(ОтборСтрокой) Тогда
		Если ОписаниеПустогоОтбора=Неопределено Тогда
			ОтборСтрокой = НСтр("ru = 'Все объекты'");
		Иначе
			ОтборСтрокой = ОписаниеПустогоОтбора;
		КонецЕсли;
	КонецЕсли;
	
	Если Не ПустаяСтрока(ПериодСтрокой) Тогда
		ОтборСтрокой =  ПериодСтрокой + ", " + ОтборСтрокой;
	КонецЕсли;
	
	Возврат ОтборСтрокой;
КонецФункции

// Добавляет отбор в конец отбора с возможной коррекцией полей.
//
//  Параметры:
//      ЭлементыПриемника - КоллекцияЭлементовОтбораКомпоновкиДанных - приемник.
//      ЭлементыИсточника - КоллекцияЭлементовОтбораКомпоновкиДанных - источник.
//      СоответствиеПолей - Соответствие - данные для коррекции полей:
//                          Ключ - исходный путь к данным поля, Значение - путь 
//                          для результата. Например для замены полей типа.
//                          "Ссылка.Наименование" -> "ОбъектСсылка.Наименование"
//                          надо передать Новый Структура("Ссылка", "ОбъектСсылка").
//
Процедура ДобавитьЗначенияОтбораКомпоновки(ЭлементыПриемника, ЭлементыИсточника, СоответствиеПолей = Неопределено) 
	
	Для Каждого Элемент Из ЭлементыИсточника Цикл
		
		Тип=ТипЗнч(Элемент);
		ЭлементОтбора = ЭлементыПриемника.Добавить(Тип);
		ЗаполнитьЗначенияСвойств(ЭлементОтбора, Элемент);
		Если Тип=Тип("ГруппаЭлементовОтбораКомпоновкиДанных") Тогда
			ДобавитьЗначенияОтбораКомпоновки(ЭлементОтбора.Элементы, Элемент.Элементы, СоответствиеПолей);
			
		ИначеЕсли СоответствиеПолей<>Неопределено Тогда
			ИсходноеПолеСтрокой = Элемент.ЛевоеЗначение;
			Для Каждого КлючЗначение Из СоответствиеПолей Цикл
				КонтрольНовое     = НРег(КлючЗначение.Ключ);
				ДлинаКонтроля     = 1 + СтрДлина(КонтрольНовое);
				КонтрольИсходного = НРег(Лев(ИсходноеПолеСтрокой, ДлинаКонтроля));
				Если КонтрольИсходного=КонтрольНовое Тогда
					ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных(КлючЗначение.Значение);
					Прервать;
				ИначеЕсли КонтрольИсходного=КонтрольНовое + "." Тогда
					ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных(КлючЗначение.Значение + Сред(ИсходноеПолеСтрокой, ДлинаКонтроля));
					Прервать;
				КонецЕсли;
			КонецЦикла;
			
		КонецЕсли;
		
	КонецЦикла;
	
КонецПроцедуры

// Готовит список объектов, подлежащих выгрузке в соответствии с настройками.
Процедура ЗаполнитьСписокОбъектовКВыгрузке() 
	УстановитьПривилегированныйРежим(Истина);
	// Объекты обрабатываются порциями.
	// Отдельно - объекты без настроек отбора.
	// Отдельно - объекты с настройками отбора.
	МассивМетаданныхБезОтборов = Новый Массив;
	МассивМетаданныхОтборПоПериоду = Новый Массив;
	Для Каждого Строка Из ДополнительнаяРегистрация Цикл
		ПолноеИмяМетаданных = Строка.ПолноеИмяМетаданных;
		ТекущийОтбор = Строка.Отбор; // ОтборКомпоновкиДанных
		Если ТекущийОтбор.Элементы.Количество() = 0 Тогда
			Если Строка.ВыборПериода И ЗначениеЗаполнено(ПериодОтбораВсехДокументов) Тогда
				МассивМетаданныхБезОтборов.Добавить(ПолноеИмяМетаданных);
			Иначе
				МассивМетаданныхОтборПоПериоду.Добавить(ПолноеИмяМетаданных);
			КонецЕсли;
		Иначе
			МассивОтбор = Новый Массив;
			МассивОтбор.Добавить(ПолноеИмяМетаданных);
			ДополнитьСписокОбъектовКВыгрузке(МассивОтбор);
		КонецЕсли;
	КонецЦикла;
	Если МассивМетаданныхБезОтборов.Количество() > 0 Тогда
		ДополнитьСписокОбъектовКВыгрузке(МассивМетаданныхБезОтборов);
	КонецЕсли;
	Если МассивМетаданныхОтборПоПериоду.Количество() > 0 Тогда
		ДополнитьСписокОбъектовКВыгрузке(МассивМетаданныхОтборПоПериоду);
	КонецЕсли;
КонецПроцедуры

// Возвращает расширенное представление объекта.
// Параметры:
//  ПараметрОбъект - Произвольный - строка с полным именем объекта метаданных или объект метаданных.
// Возвращаемое значение:
//  Строка - представление объекта.
//
Функция ПредставлениеОбъекта(ПараметрОбъект) 
	
	Если ПараметрОбъект = Неопределено Тогда
		Возврат "";
	КонецЕсли;
	МетаданныеОбъекта = ?(ТипЗнч(ПараметрОбъект) = Тип("Строка"), Метаданные.НайтиПоПолномуИмени(ПараметрОбъект), ПараметрОбъект);
	
	// Реквизитов представления может не быть, обходим через структуру.
	Представление = Новый Структура("РасширенноеПредставлениеОбъекта, ПредставлениеОбъекта");
	ЗаполнитьЗначенияСвойств(Представление, МетаданныеОбъекта);
	Если Не ПустаяСтрока(Представление.РасширенноеПредставлениеОбъекта) Тогда
		Возврат Представление.РасширенноеПредставлениеОбъекта;
	ИначеЕсли Не ПустаяСтрока(Представление.ПредставлениеОбъекта) Тогда
		Возврат Представление.ПредставлениеОбъекта;
	КонецЕсли;
	
	Возврат МетаданныеОбъекта.Представление();
КонецФункции

//  Устанавливает наборы данных в схему и инициализирует компоновщик.
//  Опирается на значения реквизитов:
//    "ДополнительнаяРегистрация", "ПериодОтбораВсехДокументов", "КомпоновщикОтбораВсехДокументов".
//
//  Параметры:
//      СписокИменМетаданных - Массив - имена метаданных (деревья значений группы ограничений, служебных
//                                      идентификаторов
//                                      "все документов" или "все НСИ"), для которых будет построена  схема. 
//                                      Если не указано, то для всего состава узла.
//
//      АдресСохраненияСхемы - Строка
//                           - УникальныйИдентификатор - адрес временного хранилища для сохранения схемы
//                             компоновки.
//
//  Возвращаемое значение:
//      Структура:
//         * ТаблицаМетаданныхСоставаУзла - ТаблицаЗначений - описание состава узла.
//         * СхемаКомпоновки - СхемаКомпоновкиДанных - инициированное значение.
//         * КомпоновщикНастроек - КомпоновщикНастроекКомпоновкиДанных - инициированное значение.
//
Функция ИнициализироватьКомпоновщик(СписокИменМетаданных = Неопределено, АдресСохраненияСхемы = Неопределено)
	
	СхемаКомпоновки = ПолучитьМакет("СхемаКомпоновкиДанных");
	ИсточникДанных = СхемаКомпоновки.ИсточникиДанных.Получить(0).Имя;

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

				// Добавляем элементы отбора с коррекцией полей "Ссылка" -> "ОбъектСсылка".
				ПараметрыДобавления = Новый Структура;
				ПараметрыДобавления.Вставить("ИмяДобавляемойТаблицы", ИмяДобавляемойТаблицы);
				ДобавитьДополнительныеОтборыКомпоновкиТабличныхЧастей(
					ГруппаОтбора.Элементы, ТекущийОтбор.Элементы, ПараметрыДобавления);
			КонецЦикла;
		КонецЦикла;
		
	КонецЕсли;
	
	Возврат Новый Структура("СхемаКомпоновки,КомпоновщикНастроек", 
		СхемаКомпоновки, КомпоновщикНастроек);
КонецФункции

//  Добавляет в схему компоновки набор данных с одним полем "Ссылка" по имени таблицы.
//
//  Параметры:
//      СхемаКомпоновкиДанных - СхемаКомпоновкиДанных - схема, в которую происходит добавление.
//      ИмяТаблицы:           - Строка - имя таблицы данных.
//      Представление:        - Строка - представление для поля "ссылка".
//
Процедура ДобавитьНаборВСхемуКомпоновки(СхемаКомпоновкиДанных, ИмяТаблицы, Представление = Неопределено)
	
	ШаблонТекстаЗапроса = "ВЫБРАТЬ ПсевдонимТаблицыМД.Ссылка ИЗ &ИмяТаблицыМетаданных КАК ПсевдонимТаблицыМД";
	
	Набор = СхемаКомпоновкиДанных.НаборыДанных.Добавить(Тип("НаборДанныхЗапросСхемыКомпоновкиДанных"));
	Набор.Запрос = СтрЗаменить(ШаблонТекстаЗапроса, "&ИмяТаблицыМетаданных", ИмяТаблицы);
	Набор.АвтоЗаполнениеДоступныхПолей = Истина;
	Набор.ИсточникДанных = СхемаКомпоновкиДанных.ИсточникиДанных.Получить(0).Имя;
	Набор.Имя = "Набор" + Формат(СхемаКомпоновкиДанных.НаборыДанных.Количество()-1, "ЧН=; ЧГ=");
	
	Поле = Набор.Поля.Добавить(Тип("ПолеНабораДанныхСхемыКомпоновкиДанных"));
	Поле.Поле = "Ссылка";
	Поле.Заголовок = ?(Представление=Неопределено, ПредставлениеОбъекта(ИмяТаблицы), Представление);
	
КонецПроцедуры

//  Добавляет одиночный элемент отбора в список.
//
//  Параметры:
//      ЭлементыОтбора  - ЭлементОтбораКомпоновкиДанных - ссылка на проверяемый объект.
//      ПутьПоляКДанным - Строка - путь к данным для добавляемого элемента.
//      ВидСравнения    - ВидСравненияКомпоновкиДанных - вид сравнения для добавляемого элемента.
//      Значение        - Произвольный - значение сравнения для добавляемого элемента.
//      Представление    -Строка - необязательное представление поля.
//      
Процедура ДобавитьЭлементОтбора(ЭлементыОтбора, ПутьПоляКДанным, ВидСравнения, Значение, Представление = Неопределено)
	
	Элемент = ЭлементыОтбора.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
	Элемент.Использование  = Истина;
	Элемент.ЛевоеЗначение  = Новый ПолеКомпоновкиДанных(ПутьПоляКДанным);
	Элемент.ВидСравнения   = ВидСравнения;
	Элемент.ПравоеЗначение = Значение;
	
	Если Представление<>Неопределено Тогда
		Элемент.Представление = Представление;
	КонецЕсли;
КонецПроцедуры

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

Процедура ДобавитьДополнительныеОтборыКомпоновкиТабличныхЧастей(ЭлементыПриемника, ЭлементыИсточника, ПараметрыДобавления)
	
	ИмяДобавляемойТаблицы = ПараметрыДобавления.ИмяДобавляемойТаблицы;
	МетаОбъекта = Метаданные.НайтиПоПолномуИмени(ИмяДобавляемойТаблицы);
	
	Для Каждого Элемент Из ЭлементыИсточника Цикл
		// Ветка разбора аналогична ветке в ДобавитьДополнительныеНаборыКомпоновкиТабличныхЧастей.
		
		Тип = ТипЗнч(Элемент);
		Если Тип = Тип("ГруппаЭлементовОтбораКомпоновкиДанных") Тогда
			// Группу копируем
			ЭлементОтбора = ЭлементыПриемника.Добавить(Тип);
			ЗаполнитьЗначенияСвойств(ЭлементОтбора, Элемент);
			
			ДобавитьДополнительныеОтборыКомпоновкиТабличныхЧастей(
				ЭлементОтбора.Элементы, Элемент.Элементы, ПараметрыДобавления);
			Продолжить;
		КонецЕсли;
		
		// Это элемент, смотрим чтобы попало на нашу табличку.
		ИмяПоля = Строка(Элемент.ЛевоеЗначение);
		Если ИмяПоля = "Ссылка" Тогда
			ЭлементОтбора = ЭлементыПриемника.Добавить(Тип);
			ЗаполнитьЗначенияСвойств(ЭлементОтбора, Элемент);
			ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ОбъектСсылка");
			Продолжить;
			
		ИначеЕсли СтрНачинаетсяС(ИмяПоля, "Ссылка.") Тогда
			ИмяПоля = Сред(ИмяПоля, 8);
			
		ИначеЕсли СтрНачинаетсяС(ИмяПоля, "ОбъектСсылка.") Тогда
			ИмяПоля = Сред(ИмяПоля, 14);
			
		Иначе
			ЭлементОтбора = ЭлементыПриемника.Добавить(Тип);
			ЗаполнитьЗначенияСвойств(ЭлементОтбора, Элемент);
			Продолжить;
			
		КонецЕсли;
			
		Позиция = СтрНайти(ИмяПоля, "."); 
		ИмяТаблицы   = Лев(ИмяПоля, Позиция - 1);
		МетаТабличнаяЧасть = МетаОбъекта.ТабличныеЧасти.Найти(ИмяТаблицы);
			
		Если Позиция = 0 Тогда
			// Отбор к шапке, будет получен через ссылку.
			ЭлементОтбора = ЭлементыПриемника.Добавить(Тип);
			ЗаполнитьЗначенияСвойств(ЭлементОтбора, Элемент);
			ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ОбъектСсылка." + ИмяПоля);
			Продолжить;
			
		ИначеЕсли МетаТабличнаяЧасть = Неопределено Тогда
			// Табличная часть указана, но не наша. Усиливаем отбор.
			ЭлементОтбора = ЭлементыПриемника.Добавить(Тип);
			ЗаполнитьЗначенияСвойств(ЭлементОтбора, Элемент);
			ЭлементОтбора.ЛевоеЗначение  = Новый ПолеКомпоновкиДанных("ПолноеИмяМетаданных");
			ЭлементОтбора.ВидСравнения   = ВидСравненияКомпоновкиДанных.Равно;
			ЭлементОтбора.Использование  = Истина;
			ЭлементОтбора.ПравоеЗначение = "";
			
			Продолжить;
		КонецЕсли;
		
		// Отбор к табличной части
		ПутьКДанным = Сред(ИмяПоля, Позиция + 1);
		Если СтрНачинаетсяС(ПутьКДанным + ".", "Ссылка.") Тогда
			// Переадресуем на головную таблицу.
			ЭлементОтбора = ЭлементыПриемника.Добавить(Тип);
			ЗаполнитьЗначенияСвойств(ЭлементОтбора, Элемент);
			ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ОбъектСсылка." + Сред(ПутьКДанным, 8));
			
		ИначеЕсли ПутьКДанным <> "НомерСтроки" И ПутьКДанным <> "Ссылка"
			И МетаТабличнаяЧасть.Реквизиты.Найти(ПутьКДанным) = Неопределено Тогда
			// Табличная часть совпадает, но реквизит не наш. Усиливаем отбор.
			ЭлементОтбора = ЭлементыПриемника.Добавить(Тип);
			ЗаполнитьЗначенияСвойств(ЭлементОтбора, Элемент);
			ЭлементОтбора.ЛевоеЗначение  = Новый ПолеКомпоновкиДанных("ПолноеИмяМетаданных");
			ЭлементОтбора.ВидСравнения   = ВидСравненияКомпоновкиДанных.Равно;
			ЭлементОтбора.Использование  = Истина;
			ЭлементОтбора.ПравоеЗначение = "";
			
		Иначе
			// Корректируем имя
			ЭлементОтбора = ЭлементыПриемника.Добавить(Тип);
			ЗаполнитьЗначенияСвойств(ЭлементОтбора, Элемент);
			ПутьКДанным = СтрЗаменить(ИмяДобавляемойТаблицы + ИмяТаблицы, ".", "") + ПутьКДанным;
			ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных(ПутьКДанным);
		КонецЕсли;
		
	КонецЦикла;
	
КонецПроцедуры

// Только для внутреннего использования.
//
// Параметры:
//   МетаТабличнаяЧасть - ОбъектМетаданныхТабличнаяЧасть - метаданные табличной части.
//   Префикс - Строка - префикс имени реквизита.
//
Функция РеквизитыТабличнойЧастиДляЗапроса(Знач МетаТабличнаяЧасть, Знач Префикс = "")
	
	ПоляЗапроса = ", НомерСтроки КАК " + Префикс + "НомерСтроки
	              |, Ссылка      КАК " + Префикс + "Ссылка
	              |";
	
	ИменаПолей  = Новый Массив;
	ИменаПолей.Добавить(Префикс + "НомерСтроки");
	ИменаПолей.Добавить(Префикс + "Ссылка");
	
	Для Каждого МетаРеквизит Из МетаТабличнаяЧасть.Реквизиты Цикл
		Имя       = МетаРеквизит.Имя;
		Псевдоним = Префикс + Имя;
		ПоляЗапроса = ПоляЗапроса + ", " + Имя + " КАК " + Псевдоним + Символы.ПС;
		ИменаПолей.Добавить(Псевдоним);
	КонецЦикла;
	
	Возврат Новый Структура("ПоляЗапроса, ИменаПолей", ПоляЗапроса, ИменаПолей);
КонецФункции

Функция РазложитьФорматОбмена(Знач ФорматОбмена)
	
	Результат = Новый Структура("БазовыйФормат, Версия");
	
	ЭлементыФормата = СтрРазделить(ФорматОбмена, "/");
	
	Если ЭлементыФормата.Количество() = 0 Тогда
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Неканоническое имя формата обмена <%1>'"), ФорматОбмена);
	КонецЕсли;
	
	Результат.Версия = ЭлементыФормата[ЭлементыФормата.ВГраница()];
	
	Версии = СтрРазделить(Результат.Версия, ".");
	
	Если Версии.Количество() = 0 Тогда
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Неканоническое представление версии формата обмена: <%1>.'"), Результат.Версия);
	КонецЕсли;
	
	ЭлементыФормата.Удалить(ЭлементыФормата.ВГраница());
	
	Результат.БазовыйФормат = СтрСоединить(ЭлементыФормата, "/");
	
	Возврат Результат;
КонецФункции

Процедура ДополнитьСписокОбъектовКВыгрузке(МассивМетаданныхОтбор)
	ДанныеКомпоновки = ИнициализироватьКомпоновщик(МассивМетаданныхОтбор);
	
	// Сохраняем отборы
	НастройкиОтборов = ДанныеКомпоновки.КомпоновщикНастроек.ПолучитьНастройки();
	
	// Нужный вариант
	ДанныеКомпоновки.КомпоновщикНастроек.ЗагрузитьНастройки(
		ДанныеКомпоновки.СхемаКомпоновки.ВариантыНастроек["ПользовательскиеДанные"].Настройки);
	
	// Восстанавливаем отборы
	ДобавитьЗначенияОтбораКомпоновки(ДанныеКомпоновки.КомпоновщикНастроек.Настройки.Отбор.Элементы, 
		НастройкиОтборов.Отбор.Элементы);
	
	НастройкиКомпоновщика = ДанныеКомпоновки.КомпоновщикНастроек.ПолучитьНастройки();

	КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
	Макет = КомпоновщикМакета.Выполнить(ДанныеКомпоновки.СхемаКомпоновки, НастройкиКомпоновщика, , , Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
	
	Процессор = Новый ПроцессорКомпоновкиДанных;
	Процессор.Инициализировать(Макет, , , Истина);
	
	Вывод = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
	Вывод.УстановитьОбъект(Новый ТаблицаЗначений);
	КоллекцияРезультат = Вывод.Вывести(Процессор);
	Для Каждого СтрокаТЗ Из КоллекцияРезультат Цикл
		Если ЗначениеЗаполнено(СтрокаТЗ.ОбъектСсылка) Тогда
			СписокДополнениеКВыгрузке.Добавить(СтрокаТЗ.ОбъектСсылка);
		КонецЕсли;
	КонецЦикла;
КонецПроцедуры

Процедура ЛогированиеПроцесса(СобытиеЛога, МетаданныеСсылкиВыгрузки, СсылкаВыгрузки)
	
	Если НЕ ЛогироватьВыгрузкуОбъектов Тогда
		
		Возврат;
		
	КонецЕсли;
	
	ЗаписьЖурналаРегистрации(СобытиеЛога, УровеньЖурналаРегистрации.Информация, МетаданныеСсылкиВыгрузки, СсылкаВыгрузки, Строка(СсылкаВыгрузки));
	
КонецПроцедуры

#КонецОбласти
#Иначе
ВызватьИсключение НСтр("ru = 'Недопустимый вызов объекта на клиенте.'");
#КонецЕсли