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

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

#Область ОписаниеПеременных

Перем ДоступностьОбъектовПоОпциям Экспорт;

#КонецОбласти

#Область ПрограммныйИнтерфейс

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

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

	ТекНомерСтроки = 1;
	СформироватьУровеньМетаданных(ТекНомерСтроки, ТекПараметры, 1,  2,  Ложь,   "Константы",               НСтр("ru = 'Константы'"));
	СформироватьУровеньМетаданных(ТекНомерСтроки, ТекПараметры, 3,  4,  Истина, "Справочники",             НСтр("ru = 'Справочники'"));
	СформироватьУровеньМетаданных(ТекНомерСтроки, ТекПараметры, 5,  6,  Истина, "Последовательности",      НСтр("ru = 'Последовательности'"));
	СформироватьУровеньМетаданных(ТекНомерСтроки, ТекПараметры, 7,  8,  Истина, "Документы",               НСтр("ru = 'Документы'"));
	СформироватьУровеньМетаданных(ТекНомерСтроки, ТекПараметры, 9,  10, Истина, "ПланыВидовХарактеристик", НСтр("ru = 'Планы видов характеристик'"));
	СформироватьУровеньМетаданных(ТекНомерСтроки, ТекПараметры, 11, 12, Истина, "ПланыСчетов",             НСтр("ru = 'Планы счетов'"));
	СформироватьУровеньМетаданных(ТекНомерСтроки, ТекПараметры, 13, 14, Истина, "ПланыВидовРасчета",       НСтр("ru = 'Планы видов расчета'"));
	СформироватьУровеньМетаданных(ТекНомерСтроки, ТекПараметры, 15, 16, Истина, "РегистрыСведений",        НСтр("ru = 'Регистры сведений'"));
	СформироватьУровеньМетаданных(ТекНомерСтроки, ТекПараметры, 17, 18, Истина, "РегистрыНакопления",      НСтр("ru = 'Регистры накопления'"));
	СформироватьУровеньМетаданных(ТекНомерСтроки, ТекПараметры, 19, 20, Истина, "РегистрыБухгалтерии",     НСтр("ru = 'Регистры бухгалтерии'"));
	СформироватьУровеньМетаданных(ТекНомерСтроки, ТекПараметры, 21, 22, Истина, "РегистрыРасчета",         НСтр("ru = 'Регистры расчета'"));
	СформироватьУровеньМетаданных(ТекНомерСтроки, ТекПараметры, 23, 24, Истина, "БизнесПроцессы",          НСтр("ru = 'Бизнес-процессы'"));
	СформироватьУровеньМетаданных(ТекНомерСтроки, ТекПараметры, 25, 26, Истина, "Задачи",                  НСтр("ru = 'Задачи'"));
	
	Возврат Результат;
КонецФункции

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

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

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

#Область ДляВызоваИзДругихПодсистем

// СтандартныеПодсистемы.ДополнительныеОтчетыИОбработки

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

// Конец СтандартныеПодсистемы.ДополнительныеОтчетыИОбработки

#КонецОбласти

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

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

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

// Рекурсивное обслуживание иерархических пометок с тремя состояниями в дереве. 
//
// Параметры:
//    ДанныеСтроки - ДанныеФормыЭлементДерева - пометка хранится в числовой колонке "Пометка".
//
Процедура ИзменениеПометки(ДанныеСтроки) Экспорт
	ДанныеСтроки.Пометка = ДанныеСтроки.Пометка % 2;
	ПроставитьПометкиВниз(ДанныеСтроки);
	ПроставитьПометкиВверх(ДанныеСтроки);
КонецПроцедуры

// Рекурсивное обслуживание иерархических пометок с тремя состояниями в дереве. 
//
// Параметры:
//    ДанныеСтроки - ДанныеФормыЭлементДерева - пометка хранится в числовой колонке "Пометка".
//
Процедура ПроставитьПометкиВниз(ДанныеСтроки) Экспорт
	Значение = ДанныеСтроки.Пометка;
	Для Каждого Потомок Из ДанныеСтроки.ПолучитьЭлементы() Цикл
		Потомок.Пометка = Значение;
		ПроставитьПометкиВниз(Потомок);
	КонецЦикла;
КонецПроцедуры

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

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

	Выборка = Запрос.Выполнить().Выбрать();
	Если Выборка.Следующий() Тогда
		ЗаполнитьЗначенияСвойств(Результат, Выборка);
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

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

	    ЗафиксироватьТранзакцию();
	Исключение
	    ОтменитьТранзакцию();
	    ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

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

// Возвращает таблицу, описывающую измерения для регистрации изменений набора данных.
//
// Параметры:
//    ИмяТаблицы   - Строка - имя таблицы, например "РегистрСведений.КурсыВалют".
//
// Возвращаемое значение:
//    ТаблицаЗначений:
//         * Имя         - Строка - имя измерения.
//         * ТипЗначения - ОписаниеТипов - типы.
//         * Заголовок   - Строка - представление для измерения.
//
Функция ИзмеренияНабораЗаписей(ИмяТаблицы) Экспорт
	
	Если ТипЗнч(ИмяТаблицы) = Тип("Строка") Тогда
		Мета = МетаданныеПоПолномуИмени(ИмяТаблицы);
	Иначе
		Мета = ИмяТаблицы;
	КонецЕсли;
	
	// Определяем ключевые поля
	Измерения = Новый ТаблицаЗначений;
	Колонки = Измерения.Колонки;
	Колонки.Добавить("Имя");
	Колонки.Добавить("ТипЗначения");
	Колонки.Добавить("Заголовок");
	
	НеУчитывать = "#НомерСообщения#Узел#";
	
	ШаблонТекстаЗапроса = 
	"ВЫБРАТЬ
	|	*
	|ИЗ
	|	&ИмяТаблицыМетаданных КАК ИмяТаблицыМетаданных
	|ГДЕ
	|	ЛОЖЬ";
	
	ИмяТаблицыМетаданных = Мета.ПолноеИмя() + ".Изменения";
	ТекстЗапроса = СтрЗаменить(ШаблонТекстаЗапроса, "&ИмяТаблицыМетаданных", ИмяТаблицыМетаданных);
	
	Запрос = Новый Запрос(ТекстЗапроса);
	ПустойРезультат = Запрос.Выполнить();
	Для Каждого КолонкаРезультата Из ПустойРезультат.Колонки Цикл
		ИмяКолонки = КолонкаРезультата.Имя;
		Если СтрНайти(НеУчитывать, "#" + ИмяКолонки + "#") = 0 Тогда
			Строка = Измерения.Добавить();
			Строка.Имя         = ИмяКолонки;
			Строка.ТипЗначения = КолонкаРезультата.ТипЗначения;
			
			МетаИзмерение = Мета.Измерения.Найти(ИмяКолонки);
			Строка.Заголовок = ?(МетаИзмерение = Неопределено, ИмяКолонки, МетаИзмерение.Представление());
		КонецЕсли;
	КонецЦикла;

	Возврат Измерения;
	
КонецФункции

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

	// Динамический пересоздает реквизиты сам.
	Если Не ЭтоДинамическийСписок Тогда
		УдаляемыеИмена = Новый Массив;
		
		// Удаляем все реквизиты, которые не перечислены в СохранятьИмена.
		Для Каждого Реквизит Из Форма.ПолучитьРеквизиты(ИмяРеквизитаТаблицы) Цикл
			ТекИмя = Реквизит.Имя;
			Если Не Сохраняемые.Свойство(ТекИмя) Тогда
				УдаляемыеИмена.Добавить(Реквизит.Путь + "." + ТекИмя);
			КонецЕсли;
		КонецЦикла;
		
		Добавляемые = Новый Массив;
		Для Каждого Колонка Из Добавлять Цикл
			ТекИмя = Колонка.Имя;
			Если Не Сохраняемые.Свойство(ТекИмя) Тогда
				Добавляемые.Добавить( Новый РеквизитФормы(ТекИмя, Колонка.ТипЗначения, ИмяРеквизитаТаблицы, Колонка.Заголовок) );
			КонецЕсли;
		КонецЦикла;
		
		Форма.ИзменитьРеквизиты(Добавляемые, УдаляемыеИмена);
	КонецЕсли;
	
	// Удаляем элементы
	Родитель = ?(ГруппаКолонок = Неопределено, ТаблицаФормы, ГруппаКолонок);
	
	Удалять = Новый Массив;
	Для Каждого Элемент Из Родитель.ПодчиненныеЭлементы Цикл
		Удалять.Добавить(Элемент);
	КонецЦикла;
	Для Каждого Элемент Из Удалять Цикл
		Если ТипЗнч(Элемент) <> Тип("ГруппаФормы") И СохраняемыеПутиДанных[Элемент.ПутьКДанным] = Неопределено Тогда
			ЭлементыФормы.Удалить(Элемент);
		КонецЕсли;
	КонецЦикла;
	
	// Создаем элементы
	Префикс = ТаблицаФормы.Имя;
	Для Каждого Колонка Из Добавлять Цикл
		ТекИмя = Колонка.Имя;
		ЭлементФормы = ЭлементыФормы.Вставить(Префикс + ТекИмя, Тип("ПолеФормы"), Родитель); // ПолеФормы
		ЭлементФормы.Вид = ВидПоляФормы.ПолеВвода;
		ЭлементФормы.ПутьКДанным = ИмяРеквизитаТаблицы + "." + ТекИмя;
		ЭлементФормы.Заголовок = Колонка.Заголовок;
	КонецЦикла;
	
КонецПроцедуры	

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

// Возвращает флаг того, что работа происходит в файловой базе.
// Возвращаемое значение:
//       Булево - флаг того, что работа происходит в файловой базе.
//
Функция ЭтоФайловаяБаза() Экспорт
	Возврат СтрНайти(СтрокаСоединенияИнформационнойБазы(), "File=") > 0;
КонецФункции

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

	// Отбор
	СкопироватьОтборКомпоновкиДанных(ТекНастройки.Отбор, ИсточникДанных.Отбор);
	СкопироватьОтборКомпоновкиДанных(ТекНастройки.Отбор, ИсточникДанных.КомпоновщикНастроек.ПолучитьНастройки().Отбор);

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

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

	ПроверитьКорректностьНастроек(КлючНастройки);
КонецПроцедуры

// Устанавливаем флаги поддержки БСП.
//
Процедура ПрочитатьПризнакиПоддержкиБСП() Экспорт
	КонфигурацияПоддерживаетБСП = БСП_ДоступнаТребуемаяВерсия();
	
	Если КонфигурацияПоддерживаетБСП Тогда
		// Используем внешний интерфейс регистрации.
		ДоступнаРегистрацияСредствамиБСП = БСП_ДоступнаТребуемаяВерсия("2.1.5.11");
		ДоступнаРаботаРИБ                = БСП_ДоступнаТребуемаяВерсия("2.1.3.25");
		ДоступнаРегистрацияАсинхронно    = БСП_ДоступнаТребуемаяВерсия("2.3.5.34");
	Иначе
		ДоступнаРегистрацияСредствамиБСП = Ложь;
		ДоступнаРаботаРИБ                = Ложь;
		ДоступнаРегистрацияАсинхронно    = Ложь;
	КонецЕсли;
КонецПроцедуры

Процедура ПрочитатьПризнакиПоддержкиБСД() Экспорт
	
	ДоступнаПакетнаяРегистрация = БСД_ДоступнаТребуемаяВерсия("1.0.3.1"); 
	
КонецПроцедуры

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

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

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

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

// Выполняет непосредственное действие с конечным объектом.
//
Процедура ВыполнитьКомандуРегистрацииОбъекта(Знач Команда, Знач Узел, Знач ОбъектРегистрации)
	
	Если ТипЗнч(Команда) = Тип("Булево") Тогда
		Если Команда Тогда
			// Регистрация
			Если НастройкаВариантНомераСообщения = 1 Тогда
				// Как отправленного
				НомерОтправленного = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Узел, "НомерОтправленного");
				Команда = 1 + НомерОтправленного;
			Иначе
				// Как нового
				ЗарегистрироватьИзменения(Узел, ОбъектРегистрации);
			КонецЕсли;
		Иначе
			// Отмена регистрации
			ПланыОбмена.УдалитьРегистрациюИзменений(Узел, ОбъектРегистрации);
		КонецЕсли;
	КонецЕсли;
	
	Если ТипЗнч(Команда) = Тип("Число") Тогда
		// Одиночная регистрация с указанным номером сообщения.
		Если Команда = 0 Тогда
			// Аналогично регистрации нового.
			ЗарегистрироватьИзменения(Узел, ОбъектРегистрации)
		Иначе
			// Изменение номера регистрации, БСП не проверяем.
			ПланыОбмена.ЗарегистрироватьИзменения(Узел, ОбъектРегистрации);
			Выборка = ПланыОбмена.ВыбратьИзменения(Узел, Команда, ОбъектРегистрации);
			Пока Выборка.Следующий() Цикл
				// Выбираем изменения для установки номера сообщения обмена данными.
			КонецЦикла;
		КонецЕсли;
		
	КонецЕсли;
	
КонецПроцедуры

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

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

// Возвращает управляемую форму, которой принадлежит элемент.
//
// Параметры:
//  ЭлементФормы - ЭлементыФормы
// Возвращаемое значение:
//  ЭлементыФормы
//
Функция ФормаЭлементаФормы(ЭлементФормы)
	Результат = ЭлементФормы;
	ТипыФормы = Новый ОписаниеТипов("ФормаКлиентскогоПриложения");
	Пока Не ТипыФормы.СодержитТип(ТипЗнч(Результат)) Цикл
		Результат = Результат.Родитель;
	КонецЦикла;
	Возврат Результат;
КонецФункции

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

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

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

// Накапливаем результаты регистраций.
//
Процедура ДобавитьРезультаты(Приемник, Источник)
	Приемник.Успешно = Приемник.Успешно + Источник.Успешно;
	Приемник.Всего   = Приемник.Всего   + Источник.Всего;
КонецПроцедуры	

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

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

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

Функция ЭтоУзелПланаОбменаБСП(Узел)
	
	МодульОбменДаннымиПовтИсп = ОбщийМодуль("ОбменДаннымиПовтИсп");
	
	Если МодульОбменДаннымиПовтИсп = Неопределено Тогда
		
		Возврат Ложь;
		
	КонецЕсли;
	
	Возврат МодульОбменДаннымиПовтИсп.ПланыОбменаБСП().Найти(МодульОбменДаннымиПовтИсп.ПолучитьИмяПланаОбмена(Узел)) <> Неопределено;
	
КонецФункции

// Возвращает флаг того, что БСП в текущей конфигурации обеспечивает функционал.
//
Функция БСП_ДоступнаТребуемаяВерсия(Знач Версия = Неопределено)
	
	ТекущаяВерсия = Неопределено;
	МодульСтандартныеПодсистемыСервер = ОбщийМодуль("СтандартныеПодсистемыСервер");
	Если МодульСтандартныеПодсистемыСервер <> Неопределено Тогда
		Попытка
			ТекущаяВерсия = МодульСтандартныеПодсистемыСервер.ВерсияБиблиотеки();
		Исключение
			ТекущаяВерсия = Неопределено;
		КонецПопытки;
	КонецЕсли;
	
	Если ТекущаяВерсия = Неопределено Тогда
		// Отсутствует или поломан метод определения версии, считаем БСП недоступной.
		Возврат Ложь
	КонецЕсли;
	ТекущаяВерсия = СтрЗаменить(ТекущаяВерсия, ".", Символы.ПС);
	
	НужнаяВерсия = СтрЗаменить(?(Версия = Неопределено, "2.2.2", Версия), ".", Символы.ПС);
	
	Для Индекс = 1 По СтрЧислоСтрок(НужнаяВерсия) Цикл
		
		ЧастьТекущейВерсии = СтрокаВЧисло(СтрПолучитьСтроку(ТекущаяВерсия, Индекс));
		ЧастьНужнойВерсии  = СтрокаВЧисло(СтрПолучитьСтроку(НужнаяВерсия,  Индекс));
		
		Если ЧастьТекущейВерсии = Неопределено Тогда
			Возврат Ложь;
			
		ИначеЕсли ЧастьТекущейВерсии > ЧастьНужнойВерсии Тогда
			Возврат Истина;
			
		ИначеЕсли ЧастьТекущейВерсии < ЧастьНужнойВерсии Тогда
			Возврат Ложь;
			
		КонецЕсли;
	КонецЦикла;
	
	Возврат Истина;
КонецФункции

// Возвращает флаг того, что БСД в текущей конфигурации обеспечивает функционал.
//
Функция БСД_ДоступнаТребуемаяВерсия(Знач Версия = Неопределено)
	
	ТекущаяВерсия = Неопределено;
	МодульСтандартныеПодсистемыПовтИсп = ОбщийМодуль("СтандартныеПодсистемыПовтИсп");
	Если МодульСтандартныеПодсистемыПовтИсп <> Неопределено Тогда
		Попытка
			ТекущаяВерсия = СтандартныеПодсистемыПовтИсп.ОписанияПодсистем().ПоИменам["БиблиотекаСинхронизацииДанных"].Версия
		Исключение
			ТекущаяВерсия = Неопределено;
		КонецПопытки;
	КонецЕсли;
	
	Если ТекущаяВерсия = Неопределено Тогда
		// Отсутствует или поломан метод определения версии, считаем БСД недоступной.
		Возврат Ложь
	КонецЕсли;
	ТекущаяВерсия = СтрЗаменить(ТекущаяВерсия, ".", Символы.ПС);
	
	НужнаяВерсия = СтрЗаменить(?(Версия = Неопределено, "2.2.2", Версия), ".", Символы.ПС);
	
	Для Индекс = 1 По СтрЧислоСтрок(НужнаяВерсия) Цикл
		
		ЧастьТекущейВерсии = СтрокаВЧисло(СтрПолучитьСтроку(ТекущаяВерсия, Индекс));
		ЧастьНужнойВерсии  = СтрокаВЧисло(СтрПолучитьСтроку(НужнаяВерсия,  Индекс));
		
		Если ЧастьТекущейВерсии = Неопределено Тогда
			Возврат Ложь;
			
		ИначеЕсли ЧастьТекущейВерсии > ЧастьНужнойВерсии Тогда
			Возврат Истина;
			
		ИначеЕсли ЧастьТекущейВерсии < ЧастьНужнойВерсии Тогда
			Возврат Ложь;
			
		КонецЕсли;
	КонецЦикла;
	
	Возврат Истина;
КонецФункции

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

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

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

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

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

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

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

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

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

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

#Область ПроцедурыИФункцииИзБазовойФункциональностиДляОбеспеченияАвтономности

Функция ПодставитьПараметрыВСтроку(Знач СтрокаПодстановки, Знач Параметр1, Знач Параметр2 = Неопределено, Знач Параметр3 = Неопределено)
	
	СтрокаПодстановки = СтрЗаменить(СтрокаПодстановки, "%1", Параметр1);
	СтрокаПодстановки = СтрЗаменить(СтрокаПодстановки, "%2", Параметр2);
	СтрокаПодстановки = СтрЗаменить(СтрокаПодстановки, "%3", Параметр3);
	
	Возврат СтрокаПодстановки;
	
КонецФункции

#КонецОбласти

#КонецОбласти

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