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

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

#Область ОповещениеПользователя

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

// АПК:142-вкл

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

#Если НЕ МобильныйАвтономныйСервер Тогда

#Область ДанныеВБазе

////////////////////////////////////////////////////////////////////////////////
// Общие процедуры и функции для работы с данными в базе.

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

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

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

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

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

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

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

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

// Возвращает признак того, что для пользователя установлен язык интерфейса
// соответствующий основному языку информационной базы.
//
// Возвращаемое значение:
//  Булево
//
Функция ЭтоОсновнойЯзык() Экспорт
	
	Возврат СтрСравнить(КодОсновногоЯзыка(), ТекущийЯзык().КодЯзыка) = 0;
	
КонецФункции

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

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

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

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

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

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

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

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

	КонецЦикла;
	
	Индекс = 1;
	Запрос = Новый Запрос;
	ТекстыЗапроса = Новый Массив;
	ВременныеТаблицы = Новый Массив;
	
	Для каждого МестоИспользования Из МестоИспользованияПоМетаданным Цикл
		 
		ИмяТаблицы = МестоИспользования.Ключ; // Строка
		МестоИспользования = МестоИспользования.Значение; // ТаблицаЗначений

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

			ТекстЗапроса = СтрЗаменить(ШаблонЗапроса, "#ПолноеИмяОбъектаМетаданных", ИмяТаблицы);
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ВТТаблицаСсылок", "ВТТаблицаСсылок" + Формат(Индекс, "ЧГ=;ЧН="));

			ИмяПараметра = "Ссылки" + Формат(Индекс, "ЧГ=;ЧН=");
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&Ссылки", "&" + ИмяПараметра);
			Запрос.УстановитьПараметр(ИмяПараметра, МестоИспользования);
			
			ЧастиЗапроса = СтрРазделить(ТекстЗапроса, ";");
			ВременныеТаблицы.Добавить(ЧастиЗапроса[0]);
			ТекстЗапроса = ЧастиЗапроса[1];

		Иначе
			ШаблонЗапроса = 
				"ВЫБРАТЬ
				|	&СсылкаНаВладельца КАК Ссылка,
				|	&ПроверяемаяСсылка КАК ПроверяемаяСсылка
				|ИЗ
				|	#ПолноеИмяОбъектаМетаданных КАК Таблица
				|ГДЕ
				|	Таблица.Ссылка = &СсылкаНаВладельца
				|	И (&Условие)";

			ТекстЗапроса = СтрЗаменить(ШаблонЗапроса, "#ПолноеИмяОбъектаМетаданных", ИмяТаблицы);

			ИмяПараметра = "СсылкаНаВладельца" + Формат(Индекс, "ЧГ=;ЧН=");
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&СсылкаНаВладельца", "&" + ИмяПараметра);
			Запрос.УстановитьПараметр(ИмяПараметра, МестоИспользования[0].Данные);

			ИмяПараметра = "ПроверяемаяСсылка" + Формат(Индекс, "ЧГ=;ЧН=");
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ПроверяемаяСсылка", "&" + ИмяПараметра);
			Запрос.УстановитьПараметр(ИмяПараметра, МестоИспользования[0].Ссылка);

		КонецЕсли;

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

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

	Значение = Новый ТаблицаЗначений;
	Значение.Колонки.Добавить("Ссылка", ОписаниеТипаВсеСсылки());
	Значение.Колонки.Добавить("Данные", ОписаниеТипаВсеСсылки());
	Значение.Колонки.Добавить("Метаданные");
	МестоИспользованияСтрока = Значение.Добавить();
	ЗаполнитьЗначенияСвойств(МестоИспользованияСтрока, МестоИспользования);
	
	Результат = СлужебныеСвязиДанных(Значение, ИсключенияПоискаСсылок);
	Возврат Результат[МестоИспользованияСтрока] <> Неопределено;

КонецФункции

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

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

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

// Возвращает структуру для параметра ДополнительныеПараметры функции ОбщегоНазначения.МестаИспользования. 
// 
// Возвращаемое значение:
//   Структура:
//   * ДополнительныеИсключенияПоискаСсылок - Соответствие - позволяет расширить исключения поиска ссылок
// 			см. ОбщегоНазначенияПереопределяемый.ПриДобавленииИсключенийПоискаСсылок
//   * ОтменаИсключенийПоискаСсылок - Массив из ОбъектМетаданных - полностью отменяет исключения поиска ссылок для
//                                                                 объектов метаданных.
//
Функция ПараметрыПоискаМестИспользования() Экспорт

	ПараметрыПоиска = Новый Структура;
	ПараметрыПоиска.Вставить("ДополнительныеИсключенияПоискаСсылок", Новый Соответствие);
	ПараметрыПоиска.Вставить("ОтменаИсключенийПоискаСсылок", Новый Соответствие);

	Возврат ПараметрыПоиска;

КонецФункции

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

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

// Возвращает связи подчиненных объектов с указанием типа поля связи.
//
// Возвращаемое значение:
//   ТаблицаЗначений:
//    * Ключ - Строка
//    * ТипРеквизита - Тип
//    * ИмяРеквизита - Строка
//    * Используется - Булево
//    * Метаданные - ОбъектМетаданных
//
Функция СвязиПодчиненныхОбъектовПоТипам() Экспорт

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

КонецФункции 

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

#Область УсловныеВызовы

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции для вызова необязательных подсистем.

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

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

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

#Область ТекущееОкружение

////////////////////////////////////////////////////////////////////////////////
// Функции описания текущего окружения клиентского приложения и операционной системы.

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

// Возвращает Истина, если текущий сеанс выполняется на сервере, работающем под управлением ОС Windows.
//
// Возвращаемое значение:
//  Булево - Истина, если сервер работает под управлением ОС Windows.
//
Функция ЭтоWindowsСервер() Экспорт
	
	СистемнаяИнформация = Новый СистемнаяИнформация;
	Возврат СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Windows_x86 
		Или СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Windows_x86_64;
	
КонецФункции

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

// Возвращает Истина, если текущий сеанс выполняется на сервере, работающем под управлением ОС Linux.
//
// Возвращаемое значение:
//  Булево - Истина, если сервер работает под управлением ОС Linux.
//
Функция ЭтоLinuxСервер() Экспорт
	
	СистемнаяИнформация = Новый СистемнаяИнформация;
	Возврат СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Linux_x86
		Или СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Linux_x86_64
		Или СистемнаяИнформация.ТипПлатформы = ТипПлатформы["Linux_ARM64"]
			Или СистемнаяИнформация.ТипПлатформы = ТипПлатформы["Linux_E2K"];
	
КонецФункции

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

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

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

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

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

// Возвращает имя используемого клиента, а для веб-браузера название и версию, если есть.
// До первого серверного вызова с клиента вернет Неопределено.
//
// Возвращаемое значение:
//  Строка - варианты строк "ВебКлиент.<Название>[.<Версия>]", "ТонкийКлиент",
//           "ТолстыйКлиентУправляемоеПриложение", "ТолстыйКлиентОбычноеПриложение",
//           где <Название> - варианты строк "Chrome", "Firefox", "Safari", "IE", "Opera"
//           или "Другой", когда не определен (ни один из указанных), например "ВебКлиент.Chrome.109".
//  Неопределено - если нет клиентского приложения или информация запрашивается
//   до первого серверного вызова, например, при первом вызове события
//   платформы УстановкаПараметровСеанса в модуле сеанса.
//
Функция ИспользуемыйКлиент() Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	Возврат СтандартныеПодсистемыСервер.ПараметрыКлиентаНаСервере().Получить("ИспользуемыйКлиент");
	
КонецФункции

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

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

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

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

// Возвращает признак того, что информационная база является распределенной (РИБ).
//
// Возвращаемое значение:
//   Булево
//
Функция ЭтоРаспределеннаяИнформационнаяБаза() Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	Возврат СтандартныеПодсистемыПовтИсп.ИспользуетсяРИБ();
	
КонецФункции

// Определяет, что эта информационная база является подчиненным узлом
// распределенной информационной базы (РИБ).
//
// Возвращаемое значение: 
//  Булево - Истина, если эта информационная база является подчиненным узлом РИБ.
//
Функция ЭтоПодчиненныйУзелРИБ() Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	Возврат ПланыОбмена.ГлавныйУзел() <> Неопределено;
	
КонецФункции

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

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

// Возвращает признак работы в режиме разделения данных по областям
// (технически это признак условного разделения).
// 
// Возвращает Ложь, если конфигурация не может работать в режиме разделения данных
// (не содержит общих реквизитов, предназначенных для разделения данных).
//
// Возвращаемое значение:
//  Булево - Истина, если разделение включено,
//           Ложь, если разделение выключено или не поддерживается.
//
Функция РазделениеВключено() Экспорт
	
	Возврат СтандартныеПодсистемыПовтИсп.РазделениеВключено();
	
КонецФункции

// Возвращает признак возможности обращения к разделенным данным (которые входят в состав разделителей).
// Признак относится к сеансу, но может меняться во время работы сеанса, если разделение было включено
// в самом сеансе, поэтому проверку следует делать непосредственно перед обращением к разделенным данным.
// 
// Возвращает Истина, если конфигурация не может работать в режиме разделения данных
// (не содержит общих реквизитов, предназначенных для разделения данных).
//
// Возвращаемое значение:
//   Булево - Истина, если разделение не поддерживается, либо разделение выключено,
//                    либо разделение включено и разделители установлены.
//            Ложь, если разделение включено и разделители не установлены.
//
Функция ДоступноИспользованиеРазделенныхДанных() Экспорт
	
	Возврат СтандартныеПодсистемыПовтИсп.ДоступноИспользованиеРазделенныхДанных();
	
КонецФункции

// Возвращает адрес публикации информационной базы для формирования прямых ссылок на объекты ИБ 
// для возможности перехода к ним пользователей, имеющих доступ к базе через публикацию в сети Интернет.
// Например, если такой адрес включить в электронное письмо, то из письма одним нажатием
// можно перейти к форме объекта в самой программе.
// 
// Возвращаемое значение:
//   Строка - адрес информационной базы, как он задан в настройке "Адрес в Интернете" в панели администрирования
//            (хранится в константе АдресПубликацииИнформационнойБазыВИнтернете).
//            Например, "http://1c.com/database".
//
// Пример: 
//  ОбщегоНазначения.АдресПубликацииИнформационнойБазыВЛокальнойСети() + "/" + e1cib/app/Обработка.ВыгрузкаДанныхПроекта";
//  возвращает прямую ссылку для открытия обработки ВыгрузкаДанныхПроекта.
//
Функция АдресПубликацииИнформационнойБазыВИнтернете() Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	Результат = Константы.АдресПубликацииИнформационнойБазыВИнтернете.Получить();
	Если РазделениеВключено() И ДоступноИспользованиеРазделенныхДанных() Тогда 
		Если ПустаяСтрока(Результат)
			И ПодсистемаСуществует("ТехнологияСервиса.БазоваяФункциональность")
			И ПодсистемаСуществует("ТехнологияСервиса.ВнешнийПрограммныйИнтерфейс") Тогда

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

// Возвращает адрес публикации информационной базы для формирования прямых ссылок на объекты ИБ 
// для возможности перехода к ним пользователей, имеющих доступ к базе через публикацию локальной сети.
// Например, если такой адрес включить в электронное письмо, то из письма за одно нажатие
// можно перейти к форме объекта в самой программе.
// 
// При работе в приложении в Интернете возвращает значение функции АдресПубликацииИнформационнойБазыВИнтернете.
// 
// Возвращаемое значение:
//   Строка - адрес информационной базы, как он задан в настройке "Локальный адрес" в панели администрирования
//            (хранится в константе АдресПубликацииИнформационнойБазыВЛокальнойСети).
//            Например, "http://localserver/base".
//
// Пример: 
//  ОбщегоНазначения.АдресПубликацииИнформационнойБазыВЛокальнойСети() + "/" + e1cib/app/Обработка.ВыгрузкаДанныхПроекта";
//  возвращает прямую ссылку для открытия обработки ВыгрузкаДанныхПроекта.
//
Функция АдресПубликацииИнформационнойБазыВЛокальнойСети() Экспорт
	
	Если РазделениеВключено() И ДоступноИспользованиеРазделенныхДанных() Тогда 
		Возврат АдресПубликацииИнформационнойБазыВИнтернете();
	КонецЕсли;

	УстановитьПривилегированныйРежим(Истина);
	Возврат Константы.АдресПубликацииИнформационнойБазыВЛокальнойСети.Получить();
	
КонецФункции

// Формирует ссылку для входа в программу для указанного пользователя.
//
// Параметры:
//  Пользователь - Строка - логин пользователя для входа в программу;
//  Пароль - Строка - пароль пользователя для входа в программу;
//  ВидПубликацииИБ - Строка - через какую публикацию пользователь входит в программу:
//                           "ВИнтернете" или "ВЛокальнойСети".
//
// Возвращаемое значение:
//  Строка, Неопределено - адрес входа в программу или Неопределено, если адрес не настроен.
//
Функция АдресВходаВПрограмму(Пользователь, Пароль, ВидПубликацииИБ) Экспорт
	
	Результат = "";
	
	Если НРег(ВидПубликацииИБ) = НРег("ВИнтернете") Тогда
		Результат = АдресПубликацииИнформационнойБазыВИнтернете();
	ИначеЕсли НРег(ВидПубликацииИБ) = НРег("ВЛокальнойСети") Тогда
		Результат = АдресПубликацииИнформационнойБазыВЛокальнойСети();
	КонецЕсли;
	
	Если ПустаяСтрока(Результат) Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Если Не СтрЗаканчиваетсяНа(Результат, "/") Тогда
		Результат = Результат + "/";
	КонецЕсли;
	
	Результат = Результат + "?n=" + КодироватьСтроку(Пользователь, СпособКодированияСтроки.КодировкаURL);
	Если ЗначениеЗаполнено(Пароль) Тогда
		Результат = Результат + "&p=" + КодироватьСтроку(Пароль, СпособКодированияСтроки.КодировкаURL);
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Возвращает редакцию конфигурации.
// Редакцией принято называть две первые группы цифр полной версии конфигурации.
// Например, у версии "1.2.3.4" редакция "1.2".
//
// Возвращаемое значение:
//  Строка - номер редакции конфигурации.
//
Функция РедакцияКонфигурации() Экспорт
	
	Результат = "";
	ВерсияКонфигурации = Метаданные.Версия;
	
	Позиция = СтрНайти(ВерсияКонфигурации, ".");
	Если Позиция > 0 Тогда
		Результат = Лев(ВерсияКонфигурации, Позиция);
		ВерсияКонфигурации = Сред(ВерсияКонфигурации, Позиция + 1);
		Позиция = СтрНайти(ВерсияКонфигурации, ".");
		Если Позиция > 0 Тогда
			Результат = Результат + Лев(ВерсияКонфигурации, Позиция - 1);
		Иначе
			Результат = "";
		КонецЕсли;
	КонецЕсли;
	
	Если ПустаяСтрока(Результат) Тогда
		Результат = Метаданные.Версия;
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

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

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

КонецФункции

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

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

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

#Область Даты

#Область ФункцииДляРаботыСДатамиСУчетомЧасовогоПоясаСеанса

// Преобразует локальную дату к формату "YYYY-MM-DDThh:mm:ssTZD" согласно ISO 8601.
//
// Параметры:
//  ЛокальнаяДата - Дата - дата в часовом поясе сеанса.
// 
// Возвращаемое значение:
//   Строка - представление даты.
//
Функция ПредставлениеЛокальнойДатыСоСмещением(ЛокальнаяДата) Экспорт
	
	Смещение = СмещениеСтандартногоВремени(ЧасовойПоясСеанса());
	Возврат ОбщегоНазначенияСлужебныйКлиентСервер.ПредставлениеЛокальнойДатыСоСмещением(ЛокальнаяДата, Смещение);
	
КонецФункции

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

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

#Область ФункцииДляРаботыСНастройкойРабочейДаты

// Сохраняет настройку рабочей даты пользователя.
//
// Параметры:
//  НоваяРабочаяДата - Дата - дата, которую необходимо установить в качестве рабочей даты пользователя.
//  ИмяПользователя - Строка - имя пользователя, для которого устанавливается рабочая дата.
//		Если не задано, то устанавливается для текущего пользователя.
//			
Процедура УстановитьРабочуюДатуПользователя(НоваяРабочаяДата, ИмяПользователя = Неопределено) Экспорт

	КлючОбъекта = ВРег("РабочаяДата");
	
	ХранилищеОбщихНастроекСохранить(КлючОбъекта, "", НоваяРабочаяДата, , ИмяПользователя);

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

// Возвращает значение настройки рабочей даты для пользователя.
//
// Параметры:
//  ИмяПользователя - Строка - имя пользователя, для которого запрашивается рабочая дата.
//		Если не задано, то устанавливается для текущего пользователя.
//
// Возвращаемое значение:
//  Дата - значение настройки рабочей даты пользователя или пустая дата, если настройка не задана.
//
Функция РабочаяДатаПользователя(ИмяПользователя = Неопределено) Экспорт

	КлючОбъекта = ВРег("РабочаяДата");

	Результат = ХранилищеОбщихНастроекЗагрузить(КлючОбъекта, "", '0001-01-01', , ИмяПользователя);
	
	Если ТипЗнч(Результат) <> Тип("Дата") Тогда
		Результат = '0001-01-01';
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Возвращает значение настройки рабочей даты для пользователя или текущей даты сеанса,
// если рабочая дата пользователя не задана.
//
// Параметры:
//  ИмяПользователя - Строка - имя пользователя, для которого запрашивается рабочая дата.
//		Если не задано, то устанавливается для текущего пользователя.
//
// Возвращаемое значение:
//  Дата - значение настройки рабочей даты пользователя или текущая дата сеанса, если настройка не задана.
//
Функция ТекущаяДатаПользователя(ИмяПользователя = Неопределено) Экспорт

	Результат = РабочаяДатаПользователя(ИмяПользователя);
	
	Если НЕ ЗначениеЗаполнено(Результат) Тогда
		Результат = ТекущаяДатаСеанса();
	КонецЕсли;
	
	Возврат НачалоДня(Результат);
	
КонецФункции

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

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

#Область Данные

////////////////////////////////////////////////////////////////////////////////
// Общие процедуры и функции для работы с прикладными типами и коллекциями значений.

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

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

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

КонецФункции

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

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

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

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

// Преобразует текст указанного формата XML в таблицу значений,
// при этом колонки таблицы формируются на основе описания в XML.
//
// Схема XML:
// <?xml version="1.0" encoding="utf-8"?>
//  <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
//   <xs:element name="Items">
//    <xs:complexType>
//     <xs:sequence>
//      <xs:element maxOccurs="unbounded" name="Item">
//       <xs:complexType>
//        <xs:attribute name="Code" type="xs:integer" use="required" />
//        <xs:attribute name="Name" type="xs:string" use="required" />
//        <xs:attribute name="Socr" type="xs:string" use="required" />
//        <xs:attribute name="Index" type="xs:string" use="required" />
//       </xs:complexType>
//      </xs:element>
//     </xs:sequence>
//    <xs:attribute name="Description" type="xs:string" use="required" />
//    <xs:attribute name="Columns" type="xs:string" use="required" />
//   </xs:complexType>
//  </xs:element>
// </xs:schema>
//
// Параметры:
//  XML - Строка
//      - ЧтениеXML - текст в формате XML или ЧтениеXML.
//
// Возвращаемое значение:
//  Структура:
//   * ИмяТаблицы - Строка          - имя таблицы.
//   * Данные     - ТаблицаЗначений - преобразованная из XML таблица.
//
// Пример:
//   КлассификаторТаблица = ОбщегоНазначения.ПрочитатьXMLВТаблицу(
//     Обработки.ЗагрузкаКурсовВалют.ПолучитьМакет("ОбщероссийскийКлассификаторВалют").ПолучитьТекст()).Данные;
//
Функция ПрочитатьXMLВТаблицу(Знач XML) Экспорт
	
	Если ТипЗнч(XML) <> Тип("ЧтениеXML") Тогда
		Чтение = Новый ЧтениеXML;
		Чтение.УстановитьСтроку(XML);
	Иначе
		Чтение = XML;
	КонецЕсли;
	
	// Прочитаем первый узел и проверим его.
	Если Не Чтение.Прочитать() Тогда
		ВызватьИсключение НСтр("ru = 'Не удалось загрузить данные из XML-файла, т.к. он пустой.'");
	ИначеЕсли Чтение.Имя <> "Items" Тогда
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось загрузить данные из XML-файла: отсутствует обязательный тег %1.'"),
			"Items");
	КонецЕсли;
	
	// Получим описание таблицы и создадим ее.
	ИмяТаблицы = Чтение.ПолучитьАтрибут("Description");
	ИменаКолонок = СтрЗаменить(Чтение.ПолучитьАтрибут("Columns"), ",", Символы.ПС);
	Колонок = СтрЧислоСтрок(ИменаКолонок);
	
	ТаблицаЗначений = Новый ТаблицаЗначений;
	Для Сч = 1 По Колонок Цикл
		ТаблицаЗначений.Колонки.Добавить(СтрПолучитьСтроку(ИменаКолонок, Сч), Новый ОписаниеТипов("Строка"));
	КонецЦикла;
	
	// Заполним значения в таблице.
	Пока Чтение.Прочитать() Цикл
		
		Если Чтение.ТипУзла = ТипУзлаXML.КонецЭлемента И Чтение.Имя = "Items" Тогда
			Прервать;
		ИначеЕсли Чтение.ТипУзла <> ТипУзлаXML.НачалоЭлемента Тогда
			Продолжить;
		ИначеЕсли Чтение.Имя <> "Item" Тогда
			ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось загрузить данные из XML-файла: в теге %1 отсутствует обязательный тег %2.'"),
				"Items", "Item");
		КонецЕсли;
		
		новСтр = ТаблицаЗначений.Добавить();
		Для Сч = 1 По Колонок Цикл
			ИмяКолонки = СтрПолучитьСтроку(ИменаКолонок, Сч);
			новСтр[Сч-1] = Чтение.ПолучитьАтрибут(ИмяКолонки);
		КонецЦикла;
		
	КонецЦикла;
	
	// Заполним результат
	Результат = Новый Структура;
	Результат.Вставить("ИмяТаблицы", ИмяТаблицы);
	Результат.Вставить("Данные", ТаблицаЗначений);
	
	Возврат Результат;
	
КонецФункции

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

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

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

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

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

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

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

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

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

		Индекс = Индекс + 1;
	КонецЦикла;
	
	Запрос.Текст = СтрСоединить(ТекстыЗапросов, Символы.ПС + "ОБЪЕДИНИТЬ ВСЕ" + Символы.ПС); // @query-part;
	
	УстановитьПривилегированныйРежим(Истина);
	ФактическиеСсылки = Запрос.Выполнить().Выгрузить();
	УстановитьПривилегированныйРежим(Ложь);
	
	ПредставленияСсылок = Запрос.Выполнить().Выгрузить();
	ПредставленияСсылок.Индексы.Добавить("Ссылка");
	
	Для каждого Ссылка Из ФактическиеСсылки Цикл
		Если ЗначениеЗаполнено(Ссылка.Ссылка) Тогда
			ПредставлениеСсылки = ПредставленияСсылок.Найти(Ссылка.Ссылка, "Ссылка");
			Результат[Ссылка.Ссылка] = ?(ПредставлениеСсылки <> Неопределено, 
				ПредставлениеСсылки.Представление, Строка(Ссылка.Ссылка));
		КонецЕсли;
	КонецЦикла;
	Для каждого Ссылка Из ПроверяемыеСсылки Цикл
		Если Результат[Ссылка] = Неопределено Тогда
			Результат[Ссылка] = НСтр("ru = 'удален'");
		КонецЕсли;
	КонецЦикла;
		
	Возврат Результат;
	
КонецФункции

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

#Область ДинамическийСписок

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

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

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

#Область ВнешнееСоединение

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции для работы с внешним соединением.

// Возвращает CLSID COM-класса для работы с "1С:Предприятием 8" через COM-соединение.
//
// Параметры:
//  ИмяCOMСоединителя - Строка - имя COM-класса для работы с "1С:Предприятием 8" через COM-соединение.
//
// Возвращаемое значение:
//  Строка - строковое представление CLSID.
//
Функция ИдентификаторCOMСоединителя(Знач ИмяCOMСоединителя) Экспорт
	
	Если ИмяCOMСоединителя = "v83.COMConnector" Тогда
		Возврат "181E893D-73A4-4722-B61D-D604B3D67D47";
	КонецЕсли;
	
	ВызватьИсключение(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Неверное значение параметра %1 функции %2. Не задан CLSID для класса %3.'"), 
		"ИмяCOMСоединителя", "ОбщегоНазначения.ИдентификаторCOMСоединителя", ИмяCOMСоединителя),
		КатегорияОшибки.ОшибкаКонфигурации);
	
КонецФункции

// Устанавливает внешнее соединение с информационной базой по переданным параметрам подключения и возвращает указатель
// на это соединение.
// 
// Параметры:
//  Параметры - см. ОбщегоНазначенияКлиентСервер.СтруктураПараметровДляУстановкиВнешнегоСоединения
// 
// Возвращаемое значение:
//  Структура:
//    * Соединение - COMОбъект
//                 - Неопределено - COM-объект соединения или Неопределено в случае ошибки;
//    * КраткоеОписаниеОшибки - Строка - краткое описание ошибки;
//    * ПодробноеОписаниеОшибки - Строка - подробное описание ошибки;
//    * ОшибкаПодключенияКомпоненты - Булево - флаг ошибки подключения COM.
//
Функция УстановитьВнешнееСоединениеСБазой(Параметры) Экспорт
	
	ПодключениеНедоступно = ЭтоLinuxСервер();
	КраткоеОписаниеОшибки = НСтр("ru = 'Прямое подключение к информационной базе недоступно на сервере под управлением ОС Linux.'");
	
	Возврат ОбщегоНазначенияСлужебныйКлиентСервер.УстановитьВнешнееСоединениеСБазой(Параметры, ПодключениеНедоступно, КраткоеОписаниеОшибки);
	
КонецФункции

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

#Область Метаданные

#Область ФункцииОпределенияТиповОбъектовМетаданных

// Ссылочные типы данных

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

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

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

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

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

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

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

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

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

// Регистры

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

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

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

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

// Константы

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

// Журналы документов

// Определяет принадлежность объекта метаданных к общему типу "Журнал документов".
//
// Параметры:
//  ОбъектМетаданных - ОбъектМетаданных - объект, для которого необходимо определить принадлежность к заданному типу.
// 
// Возвращаемое значение:
//   Булево - Истина, если объект является журналом документов.
//
Функция ЭтоЖурналДокументов(ОбъектМетаданных) Экспорт
	
	Возврат Метаданные.ЖурналыДокументов.Содержит(ОбъектМетаданных);
	
КонецФункции

// Последовательности

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

// РегламентныеЗадания

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

// Общие

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

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

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

#Область ПроцедурыИФункцииДляРаботыСТипамиОбъектамиМетаданныхИИхСтроковымиПредставлениями

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

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

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

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

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

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

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

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

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

// Проверяет, что переданное значение имеет ссылочный тип данных.
//
// Параметры:
//  Значение - Произвольный - проверяемое значение.
//
// Возвращаемое значение:
//  Булево - Истина, если тип значения ссылочный.
//
Функция ЗначениеСсылочногоТипа(Значение) Экспорт
	
	Возврат ЭтоСсылка(ТипЗнч(Значение));
	
КонецФункции

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

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

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

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

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

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

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

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

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

	ИначеЕсли Тип = Тип("Число") Тогда
		Результат = "Число";

	ИначеЕсли Тип = Тип("Булево") Тогда
		Результат = "Булево";

	ИначеЕсли Тип = Тип("Дата") Тогда
		Результат = "Дата";
	
	Иначе
		
		Результат = Строка(Тип);
		
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

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

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

// Позволяет определить, есть ли среди реквизитов объекта реквизит с переданным именем.
//
// Параметры:
//  ИмяРеквизита - Строка - имя реквизита;
//  МетаданныеОбъекта - ОбъектМетаданных - объект, в котором требуется проверить наличие реквизита.
//
// Возвращаемое значение:
//  Булево - Истина, если есть.
//
Функция ЕстьРеквизитОбъекта(ИмяРеквизита, МетаданныеОбъекта) Экспорт

	Реквизиты = МетаданныеОбъекта.Реквизиты; // КоллекцияОбъектовМетаданных
	Возврат НЕ (Реквизиты.Найти(ИмяРеквизита) = Неопределено);

КонецФункции

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

// Создает объект ОписаниеТипов, содержащий тип Строка.
//
// Параметры:
//  ДлинаСтроки - Число - длина строки.
//
// Возвращаемое значение:
//  ОписаниеТипов - описание типа Строка.
//
Функция ОписаниеТипаСтрока(ДлинаСтроки) Экспорт
	
	Возврат Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(ДлинаСтроки));
	
КонецФункции

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

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

// Возвращает описание типа, включающего в себя все возможные ссылочные типы конфигурации.
//
// Возвращаемое значение:
//  ОписаниеТипов - все ссылочные типы конфигурации.
//
Функция ОписаниеТипаВсеСсылки() Экспорт
	
	Возврат СтандартныеПодсистемыПовтИсп.ОписаниеТипаВсеСсылки();
	
КонецФункции

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

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

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

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

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

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

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

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

#Область ХранилищеНастроек

////////////////////////////////////////////////////////////////////////////////
// Сохранение, чтение и удаление настроек из хранилищ.

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

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

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

// Удаляет настройку из хранилища общих настроек, как метод платформы Удалить,
// объектов СтандартноеХранилищеНастроекМенеджер или ХранилищеНастроекМенеджер.<Имя хранилища>,
// но с поддержкой длины ключа настроек более 128 символов путем хеширования части,
// которая превышает 96 символов.
// Если нет права СохранениеДанныхПользователя, удаление пропускается без ошибки.
//
// Параметры:
//   КлючОбъекта     - Строка
//                   - Неопределено - см. синтакс-помощник платформы.
//   КлючНастроек    - Строка
//                   - Неопределено - см. синтакс-помощник платформы.
//   ИмяПользователя - Строка
//                   - Неопределено - см. синтакс-помощник платформы.
//
Процедура ХранилищеОбщихНастроекУдалить(КлючОбъекта, КлючНастроек, ИмяПользователя) Экспорт
	
	ХранилищеУдалить(ХранилищеОбщихНастроек,
		КлючОбъекта,
		КлючНастроек,
		ИмяПользователя);
	
КонецПроцедуры

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

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

// Удаляет настройку из хранилища системных настроек, как метод платформы Удалить,
// объекта СтандартноеХранилищеНастроекМенеджер, но с поддержкой длины ключа настроек
// более 128 символов путем хеширования части, которая превышает 96 символов.
// Если нет права СохранениеДанныхПользователя, удаление пропускается без ошибки.
//
// Параметры:
//   КлючОбъекта     - Строка
//                   - Неопределено - см. синтакс-помощник платформы.
//   КлючНастроек    - Строка
//                   - Неопределено - см. синтакс-помощник платформы.
//   ИмяПользователя - Строка
//                   - Неопределено - см. синтакс-помощник платформы.
//
Процедура ХранилищеСистемныхНастроекУдалить(КлючОбъекта, КлючНастроек, ИмяПользователя) Экспорт
	
	ХранилищеУдалить(ХранилищеСистемныхНастроек,
		КлючОбъекта,
		КлючНастроек,
		ИмяПользователя);
	
КонецПроцедуры

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

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

// Удаляет настройку из хранилища настроек данных форм, как метод платформы Удалить,
// объектов СтандартноеХранилищеНастроекМенеджер или ХранилищеНастроекМенеджер.<Имя хранилища>,
// но с поддержкой длины ключа настроек более 128 символов путем хеширования части,
// которая превышает 96 символов.
// Если нет права СохранениеДанныхПользователя, удаление пропускается без ошибки.
//
// Параметры:
//   КлючОбъекта     - Строка
//                   - Неопределено - см. синтакс-помощник платформы.
//   КлючНастроек    - Строка
//                   - Неопределено - см. синтакс-помощник платформы.
//   ИмяПользователя - Строка
//                   - Неопределено - см. синтакс-помощник платформы.
//
Процедура ХранилищеНастроекДанныхФормУдалить(КлючОбъекта, КлючНастроек, ИмяПользователя) Экспорт
	
	ХранилищеУдалить(ХранилищеНастроекДанныхФорм,
		КлючОбъекта,
		КлючНастроек,
		ИмяПользователя);
	
КонецПроцедуры

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

#Область СериализацияXML

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

// Выполняет преобразование (десериализацию) XML-строки в значение.
// См. также ЗначениеВСтрокуXML.
//
// Параметры:
//  СтрокаXML - Строка - XML-строка, с сериализованным объектом..
//
// Возвращаемое значение:
//  Произвольный - значение, полученное из переданной XML-строки.
//
Функция ЗначениеИзСтрокиXML(СтрокаXML) Экспорт
	
	ЧтениеXML = Новый ЧтениеXML;
	ЧтениеXML.УстановитьСтроку(СтрокаXML);
	
	Возврат СериализаторXDTO.ПрочитатьXML(ЧтениеXML);
КонецФункции

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

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

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

#Область СериализацияJSON

// Преобразует значение в строку JSON при помощи метода глобального контекста ЗаписатьJSON.
// Допускаются не все типы значений, подробности см. в синтакс-помощнике.
// Даты преобразуются в формат ISO (YYYY-MM-DDThh:mm:ssZ).
// 
// Параметры:
//  Значение - Произвольный
//
// Возвращаемое значение:
//  Строка
//
Функция ЗначениеВJSON(Знач Значение) Экспорт
	
	ЗаписьJSON = Новый ЗаписьJSON;
	ЗаписьJSON.УстановитьСтроку();
	ЗаписатьJSON(ЗаписьJSON, Значение);
	
	Возврат ЗаписьJSON.Закрыть();
	
КонецФункции

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

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

#Область РаботаСИнтернетом

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

	ДоступРазрешен = ПолучитьФункциональнуюОпцию("РазрешитьДоступКИнтернетСервисам");

	Если Не ДоступРазрешен Тогда
		ВерсияМетаданных = Метаданные.Версия;
		Если Не ПустаяСтрока(ВерсияМетаданных) И ВерсияМетаданных <> ОбновлениеИнформационнойБазыСлужебный.ВерсияИБ(
			Метаданные.Имя) Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЕсли;

	Если Не ДоступРазрешен И ПоказатьОшибку Тогда
		ВызватьИсключение ЗапрещенДоступКИнтернетСервисамТекстСообщения();
	КонецЕсли;

	Возврат ДоступРазрешен;
	
КонецФункции

// Текст ошибки при отключенном доступе к сервисам интернета.
// 
// Возвращаемое значение:
//  Строка
//
Функция ЗапрещенДоступКИнтернетСервисамТекстСообщения() Экспорт
	
	Возврат НСтр("ru = 'Доступ к Интернет-сервисам в приложении запрещен администратором (""Интернет-поддержка и сервисы"" - ""Доступ к Интернет-сервисам"").'");
	
КонецФункции

// Устанавливает признак разрешения работа с Интернет-сервисами из приложения. После вызова на клиенте рекомендуется
// вызвать оповещение о записи константы.
// 
// Параметры:
//  Разрешить - Булево
//
Процедура НастроитьРаботуСИнтернетСервисами(Разрешить) Экспорт
	
	Если Не Разрешить И РазделениеВключено() Тогда
		Возврат;
	КонецЕсли;
	
	Константы.РазрешитьДоступКИнтернетСервисам.Установить(Разрешить);
	
	Если Разрешить И ПодсистемаСуществует("СтандартныеПодсистемы.РегламентныеЗадания") Тогда

		МодульБлокировкаРаботыСВнешнимиРесурсами = ОбщийМодуль("БлокировкаРаботыСВнешнимиРесурсами");
		МодульБлокировкаРаботыСВнешнимиРесурсами.ПриВключенииДоступаКИнтернетСервисам();
		
	КонецЕсли;
	
КонецПроцедуры

// Возвращает структуру параметров для функции СоздатьWSПрокси.
//
// Возвращаемое значение:
//   см. СоздатьWSПрокси.ПараметрыПодключенияWSПрокси
//
Функция ПараметрыПодключенияWSПрокси() Экспорт
	Результат = Новый Структура;
	Результат.Вставить("АдресWSDL");
	Результат.Вставить("URIПространстваИмен");
	Результат.Вставить("ИмяСервиса");
	Результат.Вставить("ИмяТочкиПодключения", "");
	Результат.Вставить("ИмяПользователя");
	Результат.Вставить("Пароль");
	Результат.Вставить("Таймаут", 0);
	Результат.Вставить("Местоположение");
	Результат.Вставить("ИспользоватьАутентификациюОС", Ложь);
	Результат.Вставить("ДелатьКонтрольныйВызов", Ложь);
	Результат.Вставить("ЗащищенноеСоединение", Неопределено);
	Результат.Вставить("ПроверятьДоставкуПакетовПриОшибке", Истина);
	Результат.Вставить("ПроверятьДоступКИнтернетСервисам", Истина);
	Возврат Результат;
КонецФункции

// Конструктор объекта WSПрокси с дополнительными возможностями по сравнению с конструктором Новый WSПрокси:
//  - создает WSОпределения;
//  - кэширует файл WSDL для ускорения работы с веб-сервисом;
//  - не требуется указание ИнтернетПрокси (используется автоматически, если настроен);
//  - позволяет делать быструю проверку доступности сервиса с помощью операции Ping.
//
// Параметры:
//  ПараметрыПодключенияWSПрокси - Структура:
//   * АдресWSDL                    - Строка - месторасположение wsdl, например, "http://webservice.net/webservice.asmx?wsdl".
//   * URIПространстваИмен          - Строка - URI пространства имен web-сервиса, например, "http://www.webservice.net/WebService/1.0.0.1".
//   * ИмяСервиса                   - Строка - имя сервиса, например, "WebService_1_0_0_1".
//   * ИмяТочкиПодключения          - Строка - необязательный параметр. Если не задано, образуется как "<ИмяСервиса>Soap".
//   * ИмяПользователя              - Строка - необязательный параметр. Имя пользователя для входа на сервер.
//   * Пароль                       - Строка - необязательный параметр. Пароль пользователя.
//   * Таймаут                      - Число  - необязательный параметр. Таймаут на операции веб-сервиса, выполняемые 
//                                             через прокси. В секундах. 
//   * Местоположение               - Строка - необязательный параметр. Фактический адрес сервиса. Используется, если
//                                             адрес реального расположения сервиса отличается от адреса, который указан
//                                             в WSDL-файле.
//   * ИспользоватьАутентификациюОС - Булево - необязательный параметр. Включает использование авторизации NTLM или 
//                                             Negotiate на сервере.
//   * ДелатьКонтрольныйВызов       - Булево - необязательный параметр. Выполнять проверку доступности сервиса (требуется 
//                                             поддержка команды Ping в веб-сервисе). По умолчанию, Ложь.
//   * ЗащищенноеСоединение         - ЗащищенноеСоединениеOpenSSL
//                                  - Неопределено - необязательные параметры защищенного соединения.
//   * ПроверятьДоставкуПакетовПриОшибке - см. ПолучениеФайловИзИнтернета.ДиагностикаСоединения.ПроверятьДоставкуПакетов.
//   * ПроверятьДоступКИнтернетСервисам - Булево - необязательный параметр, по умолчанию Истина - выполняется проверка
//                                             разрешения доступа к Интернет-сервисам из приложения.
//
// Возвращаемое значение:
//  WSПрокси
//
// Пример:
//	ПараметрыПодключения = ОбщегоНазначения.ПараметрыПодключенияWSПрокси();
//	ПараметрыПодключения.АдресWSDL = "http://webservice.net/webservice.asmx?wsdl";
//	ПараметрыПодключения.URIПространстваИмен = "http://www.webservice.net/WebService/1.0.0.1";
//	ПараметрыПодключения.ИмяСервиса = "WebService_1_0_0_1";
//	ПараметрыПодключения.Таймаут = 20;
//	Прокси = ОбщегоНазначения.СоздатьWSПрокси(ПараметрыПодключения);
//
Функция СоздатьWSПрокси(Знач ПараметрыПодключенияWSПрокси) Экспорт
	
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр("СоздатьWSПрокси", "Параметры", ПараметрыПодключенияWSПрокси, Тип("Структура"),
		Новый Структура("АдресWSDL,URIПространстваИмен,ИмяСервиса", Тип("Строка"), Тип("Строка"), Тип("Строка")));
		
	ПараметрыПодключения = ПараметрыПодключенияWSПрокси();
	ЗаполнитьЗначенияСвойств(ПараметрыПодключения, ПараметрыПодключенияWSПрокси);
	
	Если ПараметрыПодключения.ПроверятьДоступКИнтернетСервисам Тогда
		РазрешенДоступКИнтернетСервисам(Истина);
	КонецЕсли;
	
	ДелатьКонтрольныйВызов = ПараметрыПодключения.ДелатьКонтрольныйВызов;
	Таймаут = ПараметрыПодключения.Таймаут;
	
	Если ДелатьКонтрольныйВызов И Таймаут <> Неопределено И Таймаут > 20 Тогда
		ПараметрыПодключения.Таймаут = 7;
		WSПроксиPing = РегистрыСведений.КэшПрограммныхИнтерфейсов.ВнутренняяWSПрокси(ПараметрыПодключения);
		Попытка
			WSПроксиPing.Ping();
		Исключение
			АдресТочкиПодключения = WSПроксиPing.ТочкаПодключения.Местоположение;
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось проверить доступность web-сервиса
				           |%1
				           |по причине:
				           |%2'"),
				ПараметрыПодключения.АдресWSDL,
				ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
			
			Если ПодсистемаСуществует("СтандартныеПодсистемы.ПолучениеФайловИзИнтернета") Тогда
				МодульПолучениеФайловИзИнтернета = ОбщийМодуль("ПолучениеФайловИзИнтернета");
				РезультатДиагностики = МодульПолучениеФайловИзИнтернета.ДиагностикаСоединения(АдресТочкиПодключения,,
					ПараметрыПодключения.ПроверятьДоставкуПакетовПриОшибке);
				ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = '%1
					           |Результат диагностики:
					           |%2'"),
					ТекстОшибки,
					РезультатДиагностики.ОписаниеОшибки);
			КонецЕсли;
			
			ВызватьИсключение(ТекстОшибки, КатегорияОшибки.ОшибкаСети);
		КонецПопытки;
		ПараметрыПодключения.Таймаут = Таймаут;
	КонецЕсли;
	
	Возврат РегистрыСведений.КэшПрограммныхИнтерфейсов.ВнутренняяWSПрокси(ПараметрыПодключения);
	
КонецФункции

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

// Возвращает номера версий программных интерфейсов удаленной системы, доступной через веб-сервис.
// Позволяет обеспечивать полную обратную совместимость при изменениях в программных интерфейсах
// с помощью их явного версионирования. Например, если программный интерфейс выше определенной версии,
// то только в этом случае допустимо вызывать у него новую функцию.
//
// В целях экономии трафика при интенсивном взаимодействии между вызывающей и вызываемой сторонами 
// сведения о версиях кэшируются на одни сутки. Если для целей отладки требуется сбросить кэш раньше этого времени,
// следует удалить соответствующие записи из регистра сведений КэшПрограммныхИнтерфейсов.
//
// Параметры:
//  Адрес        - Строка - адрес веб-сервиса версионирования интерфейсов InterfaceVersion;
//  Пользователь - Строка - имя пользователя веб-сервиса;
//  Пароль       - Строка - пароль пользователя веб-сервиса;
//  Интерфейс    - Строка - имя запрашиваемого программного интерфейса, например, "СервисПередачиФайлов";
//  ПроверятьДоставкуПакетовПриОшибке - см. ПолучениеФайловИзИнтернета.ДиагностикаСоединения.ПроверятьДоставкуПакетов
//
// Возвращаемое значение:
//   ФиксированныйМассив - массив строк, каждая строка является представлением номера версии интерфейса. 
//                         Например, "1.0.2.1".
//
// Пример:
//	  Версии = ПолучитьВерсииИнтерфейса("http://vsrvx/sm", "ivanov",, "СервисПередачиФайлов");
//
//    Также для обратной совместимости поддерживается устаревший вариант вызова:
//	  ПараметрыПодключения = Новый Структура;
//	  ПараметрыПодключения.Вставить("URL", "http://vsrvx/sm");
//	  ПараметрыПодключения.Вставить("UserName", "ivanov");
//	  ПараметрыПодключения.Вставить("Password", "");
//	  Версии = ПолучитьВерсииИнтерфейса(ПараметрыПодключения, "СервисПередачиФайлов");
//
Функция ПолучитьВерсииИнтерфейса(Знач Адрес, Знач Пользователь, Знач Пароль = Неопределено, 
	Знач Интерфейс = Неопределено, Знач ПроверятьДоставкуПакетовПриОшибке = Истина) Экспорт
	
	Если ТипЗнч(Адрес) = Тип("Структура") Тогда // для обратной совместимости
		ПараметрыПодключения = Адрес;
		ИмяИнтерфейса = Пользователь;
	Иначе
		ПараметрыПодключения = Новый Структура;
		ПараметрыПодключения.Вставить("URL", Адрес);
		ПараметрыПодключения.Вставить("UserName", Пользователь);
		ПараметрыПодключения.Вставить("Password", Пароль);
		ИмяИнтерфейса = Интерфейс;
	КонецЕсли;
	
	ПараметрыПодключения.Вставить("ПроверятьДоставкуПакетовПриОшибке", ПроверятьДоставкуПакетовПриОшибке);
	
	Если Не ПараметрыПодключения.Свойство("URL") 
		Или Не ЗначениеЗаполнено(ПараметрыПодключения.URL) Тогда
		
		ВызватьИсключение(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Неверное значение параметра %1 функции %2. Не задан URL сервиса.'"), 
			"ПараметрыПодключения", "ОбщегоНазначения.ПолучитьВерсииИнтерфейса"), КатегорияОшибки.ОшибкаКонфигурации);
	КонецЕсли;
	
	ПараметрыПолучения = Новый Массив;
	ПараметрыПолучения.Добавить(ПараметрыПодключения);
	ПараметрыПолучения.Добавить(ИмяИнтерфейса);
	
	Возврат РегистрыСведений.КэшПрограммныхИнтерфейсов.ДанныеКэшаВерсий(
		РегистрыСведений.КэшПрограммныхИнтерфейсов.ИдентификаторЗаписиКэшаВерсий(ПараметрыПодключения.URL, ИмяИнтерфейса), 
		Перечисления.ТипыДанныхКэшаПрограммныхИнтерфейсов.ВерсииИнтерфейса, 
		ПараметрыПолучения,
		Истина);
	
КонецФункции

// Возвращает номера версий программных интерфейсов удаленной системы, подключенной через внешнее соединение.
// Позволяет обеспечивать полную обратную совместимость при изменениях в программных интерфейсах
// с помощью их явного версионирования. Например, если программный интерфейс выше определенной версии,
// то только в этом случае допустимо вызывать у него новую функцию.
//
// Параметры:
//   ВнешнееСоединение - COMОбъект - внешнее соединение, которое используется для работы с удаленной системой.
//   ИмяИнтерфейса     - Строка    - имя запрашиваемого программного интерфейса, например "СервисПередачиФайлов".
//
// Возвращаемое значение:
//   ФиксированныйМассив - массив строк, каждая строка является представлением номера версии интерфейса. 
//                         Например "1.0.2.1".
//
// Пример:
//  Версии = ОбщегоНазначения.ПолучитьВерсииИнтерфейсаЧерезВнешнееСоединение(ВнешнееСоединение, "СервисПередачиФайлов");
//
Функция ПолучитьВерсииИнтерфейсаЧерезВнешнееСоединение(ВнешнееСоединение, Знач ИмяИнтерфейса) Экспорт
	Попытка
		ВерсииИнтерфейсаXML = ВнешнееСоединение.СтандартныеПодсистемыСервер.ПоддерживаемыеВерсии(ИмяИнтерфейса);
	Исключение
		СтрокаСообщения = НСтр("ru = 'Корреспондент не поддерживает версионирование программных интерфейсов.
			|Описание ошибки: %1'");
		СтрокаСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(СтрокаСообщения, ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
		ЗаписьЖурналаРегистрации(НСтр("ru = 'Получение версий интерфейса'", КодОсновногоЯзыка()),
			УровеньЖурналаРегистрации.Ошибка, , , СтрокаСообщения);
		
		Возврат Новый ФиксированныйМассив(Новый Массив);
	КонецПопытки;
	
	Возврат Новый ФиксированныйМассив(ЗначениеИзСтрокиXML(ВерсииИнтерфейсаXML));
КонецФункции

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

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

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

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

#Область БезопасноеХранилище

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции для работы с хранилищем паролей.

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

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

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

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

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

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

#Область БуферОбмена

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции для работы с внутренним буфером обмена.

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

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

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

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

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

#Область БезопасноеВыполнениеВнешнегоКода

////////////////////////////////////////////////////////////////////////////////
// Функции для поддержки работы конфигурации с профилем безопасности, в котором
// запрещено подключение внешних модулей без установки безопасного режима.
//

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

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

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

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

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

// Возвращает описание защиты от опасных действий с отключенными предупреждениями.
//
// Возвращаемое значение:
//  ОписаниеЗащитыОтОпасныхДействий - со свойством ПредупреждатьОбОпасныхДействиях = Ложь.
//
Функция ОписаниеЗащитыБезПредупреждений() Экспорт
	
	ОписаниеЗащиты = Новый ОписаниеЗащитыОтОпасныхДействий;
	ОписаниеЗащиты.ПредупреждатьОбОпасныхДействиях = Ложь;
	
	Возврат ОписаниеЗащиты;
	
КонецФункции

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

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

#Область Запросы

// Подготавливает строку для использования в качестве шаблона поиска в запросе с оператором ПОДОБНО.
// Экранирует служебные символы, если они есть в строке.
//
// Параметры:
//  СтрокаПоиска - Строка - произвольная строка.
//
// Возвращаемое значение:
//  Строка
//  
// Пример:
//   ВЫБРАТЬ
//    Номенклатура.Ссылка КАК Ссылка
//  ИЗ
//    Справочник.Номенклатура КАК Номенклатура
//  ГДЕ
//    Номенклатура.Наименование ПОДОБНО &Шаблон СПЕЦСИМВОЛ "~"
//
//  Запрос.УстановитьПараметр("Шаблон", ОбщегоНазначения.СформироватьСтрокуДляПоискаВЗапросе(ИскомыйТекст));
//
Функция СформироватьСтрокуДляПоискаВЗапросе(Знач СтрокаПоиска) Экспорт
	
	Результат = СтрокаПоиска;
	Результат = СтрЗаменить(Результат, "~", "~~");
	Результат = СтрЗаменить(Результат, "%", "~%");
	Результат = СтрЗаменить(Результат, "_", "~_");
	Результат = СтрЗаменить(Результат, "[", "~[");
	Результат = СтрЗаменить(Результат, "]", "~]");
	Результат = СтрЗаменить(Результат, "^", "~^");	
	Возврат Результат;
	
КонецФункции

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

// Возвращает фрагмент текста запроса, объединяющего результаты запросов в один запрос.
//
// Возвращаемое значение:
//  Строка
//
Функция ТекстОбъединитьВсе() Экспорт
	
	Возврат
		"
		|
		|ОБЪЕДИНИТЬ ВСЕ
		|
		|";
	
КонецФункции

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

#Область Прочее

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

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

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

// Проверяет, умещаются ли переданные табличные документы на страницу при печати.
//
// Параметры:
//  ТабДокумент        - ТабличныйДокумент - табличный документ.
//  ВыводимыеОбласти   - Массив
//                     - ТабличныйДокумент - массив из проверяемых таблиц или табличный документ. 
//  РезультатПриОшибке - Булево - какой возвращать результат при возникновении ошибки.
//
// Возвращаемое значение:
//   Булево   - умещаются или нет переданные документы.
//
Функция ПроверитьВыводТабличногоДокумента(ТабДокумент, ВыводимыеОбласти, РезультатПриОшибке = Истина) Экспорт

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

КонецФункции 

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

// Выполняет пропорциональное распределение суммы в соответствии
// с заданными коэффициентами распределения. 
//
// Параметры:
//  РаспределяемаяСумма - Число  - сумма, которую надо распределить, если сумма равна 0 - то возвращается Неопределено;
//                                 Если передана отрицательная - расчет по модулю и после инверсия знаков результата.
//  Коэффициенты        - Массив из Число - коэффициенты распределения, должны быть положительны или отрицательными 
//                                          одновременно.
//  Точность            - Число  - точность округления при распределении. 
//
// Возвращаемое значение:
//  Массив из Число - массив размерностью равный массиву коэффициентов, содержит
//           суммы в соответствии с весом коэффициента (из массива коэффициентов).
//           В случае, если распределить невозможно (кол-во коэффициентов = 0
//           есть коэффициенты с отрицательным значением или суммарный вес коэффициентов = 0),
//           тогда будет возвращено Неопределено.
//
// Пример:
//
//	Коэффициенты = Новый Массив;
//	Коэффициенты.Добавить(1);
//	Коэффициенты.Добавить(2);
//	Результат = ОбщегоНазначенияКлиентСервер.РаспределитьСуммуПропорциональноКоэффициентам(1, Коэффициенты);
//	// Результат = [0.33, 0.67]
//
Функция РаспределитьСуммуПропорциональноКоэффициентам(
		Знач РаспределяемаяСумма, Коэффициенты, Знач Точность = 2) Экспорт
	
	Возврат ОбщегоНазначенияКлиентСервер.РаспределитьСуммуПропорциональноКоэффициентам(
		РаспределяемаяСумма, Коэффициенты, Точность);
	
КонецФункции

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

// Подключает внешнюю компоненту, выполненную по технологии Native API или COM,
// из макета конфигурации (хранится в виде ZIP-архива).
//
// Параметры:
//   Идентификатор   - Строка - идентификатор объекта внешней компоненты.
//   ПолноеИмяМакета - Строка - полное имя макета конфигурации с ZIP-архивом.
//   Изолированно    - Булево - если Истина, компонента должна подключаться изолированно, 
//                              в этом случае она загружается в отдельный процесс операционной системы.
//                              Если Ложь, то в этом случае компонента будет выполняться в том же процессе 
//                              операционной системы, который выполняет код встроенного языка. 
//                   - Неопределено - (по умолчанию Неопределено) соответствует поведению платформы:
//                              не изолированно - если компонентой поддерживается только этот режим, 
//                              изолированно - в остальных случаях.
//                              См. https://its.1c.eu/db/v83doc#bookmark:dev:TI000001866
//
// Возвращаемое значение:
//   - ОбъектВнешнейКомпоненты - экземпляр объекта внешней компоненты.
//   - Неопределено - если не удалось создать.
//
// Пример:
//
//  ПодключаемыйМодуль = ОбщегоНазначения.ПодключитьКомпонентуИзМакета(
//      "QRCodeExtension",
//      "ОбщийМакет.КомпонентаПечатиQRКода");
//
//  Если ПодключаемыйМодуль <> Неопределено Тогда 
//      // ПодключаемыйМодуль содержит созданный экземпляр подключенной компоненты.
//  КонецЕсли;
//
//  ПодключаемыйМодуль = Неопределено;
//
Функция ПодключитьКомпонентуИзМакета(Знач Идентификатор, Знач ПолноеИмяМакета, Знач Изолированно = Неопределено) Экспорт
	
	РезультатПроверкиВнешнейКомпоненты = Неопределено;
	
	Если ПодсистемаСуществует("СтандартныеПодсистемы.ВнешниеКомпоненты") Тогда
		МодульВнешниеКомпонентыСлужебный = ОбщийМодуль("ВнешниеКомпонентыСлужебный");
		РезультатПроверкиВнешнейКомпоненты = МодульВнешниеКомпонентыСлужебный.ПроверитьПодключениеКомпоненты(Идентификатор);
		РезультатПроверкиВнешнейКомпоненты.Вставить("Доступна", 
			Не ЗначениеЗаполнено(РезультатПроверкиВнешнейКомпоненты.ОписаниеОшибки));
	КонецЕсли;
	
	КомпонентаПоследнейВерсии = СтандартныеПодсистемыСервер.КомпонентаПоследнейВерсии(
		Идентификатор, ПолноеИмяМакета, РезультатПроверкиВнешнейКомпоненты);
		
	Результат = ПодключитьКомпонентуПоИдентификатору(Идентификатор,
			КомпонентаПоследнейВерсии.Местоположение, Изолированно);
	
	Возврат Результат.ПодключаемыйМодуль;
	
КонецФункции

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

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

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

// Проверяет, что указанный режим соответствует замещению набора записей.
//
// Параметры:
//  Режим - Булево, РежимЗамещения
//
// Возвращаемое значение:
//  Булево
//
Функция ЭтоЗамещениеНабораЗаписей(Режим) Экспорт
	
	Значение = СтандартныеПодсистемыПовтИсп.РежимЗамещенияНабораЗаписей();
	Возврат Значение <> Неопределено И Режим = Значение;
	
КонецФункции

// Проверяет, что указанный режим соответствует обновлению набора записей.
//
// Параметры:
//  Режим - Булево, РежимЗамещения
//
// Возвращаемое значение:
//  Булево
//
Функция ЭтоОбновлениеНабораЗаписей(Режим) Экспорт
	
	Значение = СтандартныеПодсистемыПовтИсп.РежимОбновленияНабораЗаписей();
	Возврат Значение <> Неопределено И Режим = Значение;
	
КонецФункции

// Проверяет, что указанный режим соответствует слиянию набора записей.
//
// Параметры:
//  Режим - Булево, РежимЗамещения
//
// Возвращаемое значение:
//  Булево
//
Функция ЭтоСлияниеНабораЗаписей(Режим) Экспорт
	
	Значение = СтандартныеПодсистемыПовтИсп.РежимСлиянияНабораЗаписей();
	Возврат Значение <> Неопределено И Режим = Значение;
	
КонецФункции

// Проверяет, что указанный режим соответствует удалению набора записей.
//
// Параметры:
//  Режим - Булево, РежимЗамещения
//
// Возвращаемое значение:
//  Булево
//
Функция ЭтоУдалениеНабораЗаписей(Режим) Экспорт
	
	Значение = СтандартныеПодсистемыПовтИсп.РежимУдаленияНабораЗаписей();
	Возврат Значение <> Неопределено И Режим = Значение;
	
КонецФункции

// Возвращает РежимЗамещения.Обновление или Неопределено,
// если не поддерживается в текущей версии 1С:Предприятия.
//
// Возвращаемое значение:
//  Неопределено, РежимЗамещения
//
Функция РежимОбновленияНабораЗаписей() Экспорт
	
	Возврат СтандартныеПодсистемыПовтИсп.РежимОбновленияНабораЗаписей();
	
КонецФункции

// Возвращает РежимЗамещения.Слияние или Неопределено,
// если не поддерживается в текущей версии 1С:Предприятия.
//
// Возвращаемое значение:
//  Неопределено, РежимЗамещения
//
Функция РежимСлиянияНабораЗаписей() Экспорт
	
	Возврат СтандартныеПодсистемыПовтИсп.РежимСлиянияНабораЗаписей();
	
КонецФункции

// Возвращает РежимЗамещения.Удаление или Неопределено,
// если не поддерживается в текущей версии 1С:Предприятия.
//
// Возвращаемое значение:
//  Неопределено, РежимЗамещения
//
Функция РежимУдаленияНабораЗаписей() Экспорт
	
	Возврат СтандартныеПодсистемыПовтИсп.РежимУдаленияНабораЗаписей();
	
КонецФункции

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

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

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

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

// Устарела. Следует использовать ФайловаяСистема.УдалитьВременныйКаталог
// Удаляет временный каталог вместе с его содержимым, если возможно.
// Если временный каталог не может быть удален (например, он занят каким-то процессом),
// то в журнал регистрации записывается соответствующее предупреждение, а процедура завершается.
//
// Для совместного использования с ОбщегоНазначения.СоздатьВременныйКаталог, 
// после окончания работы с временным каталогом.
//
// Параметры:
//   ПутьККаталогу - Строка - полный путь к временному каталогу.
//
Процедура УдалитьВременныйКаталог(Знач ПутьККаталогу) Экспорт
	
	ФайловаяСистема.УдалитьВременныйКаталог(ПутьККаталогу);
	
КонецПроцедуры

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

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

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

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

#КонецЕсли

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

#Если НЕ МобильныйАвтономныйСервер Тогда

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

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

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

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

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

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

// Сокращает имя файла с расширением, если оно превышает размер в 255 байт. 
// При этом сокращается часть имени файла без расширения, в конец которой добавляется уникальное значение, 
// вычисленное по алгоритму MD5. 
// 
// Параметры:
//  ИмяФайла - Строка - имя файла с расширением
// 
Процедура СократитьИмяФайла(ИмяФайла) Экспорт
	
	ОграничениеВБайтах = 127;
	Файл = Новый Файл(ИмяФайла);
	
	Если РазмерСтрокиВБайтах(Файл.Имя) <= ОграничениеВБайтах Тогда
		Возврат;
	КонецЕсли;
	
	Строка = "";
	ОстатокСтроки = "";
	РазмерСтроки = 0;
	МаксимальныйРазмерСтроки = ОграничениеВБайтах - 32;
	
	РазмерРасширения = РазмерСтрокиВБайтах(Файл.Расширение);
	СокращатьВместеСРасширением = РазмерРасширения > 32;
	
	Если СокращатьВместеСРасширением Тогда
		СокращаемоеИмя = Файл.Имя;
	Иначе
		СокращаемоеИмя = Файл.ИмяБезРасширения;
		РазмерСтроки = РазмерРасширения;
	КонецЕсли;
	
	Для НомерСимвола = 1 По СтрДлина(СокращаемоеИмя) Цикл
		Символ = Сред(СокращаемоеИмя, НомерСимвола, 1);
		РазмерСимвола = РазмерСтрокиВБайтах(Символ);
		
		Если РазмерСтроки + РазмерСимвола > МаксимальныйРазмерСтроки Тогда
			ОстатокСтроки = Сред(СокращаемоеИмя, НомерСимвола);
			Прервать;
		КонецЕсли;
		
		Строка = Строка + Символ;
		РазмерСтроки = РазмерСтроки + РазмерСимвола;
	КонецЦикла;
	
	ИмяФайла = Строка;
	
	ХешированиеДанных = Новый ХешированиеДанных(ХешФункция.MD5);
	ХешированиеДанных.Добавить(ОстатокСтроки);
	ХешСумма = СтрЗаменить(ХешированиеДанных.ХешСумма, " ", "");
	
	ИмяФайла = Файл.Путь + ИмяФайла + ХешСумма + ?(СокращатьВместеСРасширением, "", Файл.Расширение);
	
КонецПроцедуры

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

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

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

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

#Область ДанныеВБазе

#Область ЗначенияРеквизитов

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

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

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

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

#Область ЗаменитьСсылки

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

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

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

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

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

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

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

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

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

Функция ЗадачиПроцесса(БизнесПроцесс, ТипЗадач)

	ТекстЗапроса = "ВЫБРАТЬ
	|	ИмяТаблицы.Ссылка
	|ИЗ
	|	&ИмяТаблицы КАК ИмяТаблицы
	|ГДЕ
	|	ИмяТаблицы.БизнесПроцесс = &БизнесПроцесс";
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ИмяТаблицы", ТипЗадач);
	Запрос = Новый Запрос(ТекстЗапроса);
	Запрос.УстановитьПараметр("БизнесПроцесс", БизнесПроцесс);
	Возврат Запрос.Выполнить().Выбрать();

КонецФункции

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

	Для Каждого Реквизит Из Описание.РеквизитыАдресации Цикл
		Имя = Реквизит.Ключ;
		ЦелеваяСсылка = ПарыЗамен[ Объект[Имя] ];
		Если ЦелеваяСсылка <> Неопределено Тогда
			ЗарегистрироватьФактЗамены(Объект, Объект[Имя], ЦелеваяСсылка, "РеквизитыАдресации", Имя);
			Объект[Имя] = ЦелеваяСсылка;
		КонецЕсли;
	КонецЦикла;

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

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

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

Функция УстановитьПометкуУдаления(Результат, Знач УдаляемаяСсылка, Знач ВсеМестаИспользования, Знач ПараметрыВыполнения, 
	ЕстьВнешняяТранзакция)

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

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

		ЗарегистрироватьОшибкуЗамены(Результат, СтрокаТаблицы.Ссылка, 
			ОписаниеОшибкиЗамены("ДанныеИзменены", СтрокаТаблицы.Данные, ПредметСтрокой(СтрокаТаблицы.Данные),
				НСтр("ru = 'Заменены не все места использования. Возможно места использования были добавлены или изменены другим пользователем.'")));
	КонецЦикла;
	
КонецПроцедуры

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

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

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

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

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

// Параметры:
//   ОбъектМетаданных - ОбъектМетаданных
// 	
// Возвращаемое значение:
//  Массив из Структура:
//   * СписокПолей - Структура
//   * СтруктураИзмерений - Структура
//   * СписокВедущих - Структура
//   * НаборЗаписей - РегистрСведенийНаборЗаписей
//   * ПространствоБлокировки - Строка
//
Функция ОписаниеЗадач(Знач Мета)

	ОписаниеЗадач = Неопределено;
	Если НЕ Метаданные.БизнесПроцессы.Содержит(Мета) Тогда
		Возврат ОписаниеЗадач;
	КонецЕсли;
	
	ОписаниеЗадач = ОписаниеОбъекта(Мета.Задача);
	ОписаниеЗадач.Вставить("ПространствоБлокировки", Мета.Задача.ПолноеИмя());
	
	Возврат ОписаниеЗадач;

КонецФункции

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

Функция ОписанияТиповПересекаются(Знач Описание1, Знач Описание2)
	
	Для Каждого Тип Из Описание1.Типы() Цикл
		Если Описание2.СодержитТип(Тип) Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Ложь;
КонецФункции

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


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

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

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

Процедура СообщитьОтложенныеСообщения(Знач Сообщения)
	
	Для Каждого Сообщение Из Сообщения Цикл
		Сообщение.Сообщить();
	КонецЦикла;
	
КонецПроцедуры

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

Функция СобытиеЖурналаРегистрацииЗаменыСсылок()
	
	Возврат НСтр("ru = 'Поиск и удаление ссылок'", КодОсновногоЯзыка());
	
КонецФункции

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

// Возвращаемое значение:
//   Структура:
//    * ТипОшибки - Строка
//    * ОбъектОшибки - ЛюбаяСсылка
//    * ПредставлениеОбъектаОшибки - Строка
//    * ИнформацияОбОшибке - ИнформацияОбОшибке, Строка
//
Функция ОписаниеОшибкиЗамены(Знач ТипОшибки, Знач ОбъектОшибки, Знач ПредставлениеОбъектаОшибки, Знач ИнформацияОбОшибке)

	Результат = Новый Структура;
	Результат.Вставить("ТипОшибки",                  ТипОшибки);
	Результат.Вставить("ОбъектОшибки",               ОбъектОшибки);
	Результат.Вставить("ПредставлениеОбъектаОшибки", ПредставлениеОбъектаОшибки);
	Результат.Вставить("ИнформацияОбОшибке",         ИнформацияОбОшибке);
	Возврат Результат;

КонецФункции

// Возвращаемое значение:
//   Структура:
//     * ЕстьОшибки - Булево
//     * ОчередьКНепосредственномуУдалению - Массив
//     * Ошибки - см. ОбщегоНазначения.ЗаменитьСсылки
//
Функция РезультатЗаменыСсылок(Знач ОшибкиЗамены)

	Результат = Новый Структура;
	Результат.Вставить("ЕстьОшибки", Ложь);
	Результат.Вставить("ОчередьКНепосредственномуУдалению", Новый Массив);
	Результат.Вставить("Ошибки", ОшибкиЗамены);
	Возврат Результат
	
КонецФункции

Процедура ЗарегистрироватьОшибкуВТаблицу(Результат, Дубль, Оригинал, Данные, Информация, ТипОшибки, ИнформацияОбОшибке)
	Результат.ЕстьОшибки = Истина;
	
	ЗаписьЖурналаРегистрации(СобытиеЖурналаРегистрацииЗаменыСсылок(),
		УровеньЖурналаРегистрации.Ошибка,,,
		ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
	
	ПолноеПредставлениеДанных = Строка(Данные) + " (" + Информация.ПредставлениеЭлемента + ")";
	
	Ошибка = Результат.Ошибки.Добавить();
	Ошибка.Ссылка       = Дубль;
	Ошибка.ОбъектОшибки = Данные;
	Ошибка.ПредставлениеОбъектаОшибки = ПолноеПредставлениеДанных;
	
	Если ТипОшибки = "БлокировкаДляРегистра" Тогда
		НовыйШаблон = НСтр("ru = 'Не удалось начать редактирование %1: %2'");
		Ошибка.ТипОшибки = "ОшибкаБлокировки";
	ИначеЕсли ТипОшибки = "БлокировкаДляУдаленияДубля" Тогда
		НовыйШаблон = НСтр("ru = 'Не удалось начать удаление: %2'");
		Ошибка.ТипОшибки = "ОшибкаБлокировки";
	ИначеЕсли ТипОшибки = "УдалитьНаборДубля" Тогда
		НовыйШаблон = НСтр("ru = 'Не удалось очистить сведения о дубле в %1: %2'");
		Ошибка.ТипОшибки = "ОшибкаЗаписи";
	ИначеЕсли ТипОшибки = "ЗаписатьНаборОригинала" Тогда
		НовыйШаблон = НСтр("ru = 'Не удалось обновить сведения в %1: %2'");
		Ошибка.ТипОшибки = "ОшибкаЗаписи";
	Иначе
		НовыйШаблон = ТипОшибки + " (%1): %2";
		Ошибка.ТипОшибки = ТипОшибки;
	КонецЕсли;
	
	НовыйШаблон = НовыйШаблон + Символы.ПС + Символы.ПС + НСтр("ru = 'Подробности в журнале регистрации.'");
	
	КраткоеПредставление = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке);
	Ошибка.ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НовыйШаблон, ПолноеПредставлениеДанных, КраткоеПредставление);
	
КонецПроцедуры

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

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

Процедура ДобавитьВСтатистикуЗаменыСсылок(Статистика, Дубль, ЕстьОшибки)

	КлючДубля = Дубль.Метаданные().ПолноеИмя();
	ЭлементСтатистики = Статистика[КлючДубля];
	Если ЭлементСтатистики = Неопределено Тогда
	     ЭлементСтатистики = Новый Структура("КоличествоЭлементов, КоличествоОшибок",0,0);
		 Статистика.Вставить(КлючДубля, ЭлементСтатистики);
	 КонецЕсли;
	 
	 ЭлементСтатистики.КоличествоЭлементов = ЭлементСтатистики.КоличествоЭлементов + 1;
	 ЭлементСтатистики.КоличествоОшибок = ЭлементСтатистики.КоличествоОшибок + ?(ЕстьОшибки, 1,0);

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

Процедура ОтправитьСтатистикуЗаменыСсылок(Статистика)

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

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

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

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

Процедура ЗарегистрироватьОшибкиУдаления(Результат, ПрепятствующиеУдалению)

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

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

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

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

КонецФункции

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

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

#Область МестаИспользования

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

Функция ОписаниеИзмеренийНабора(Знач МетаданныеРегистра, КэшИзмеренийРегистров)
	
	ОписаниеИзмерений = КэшИзмеренийРегистров[МетаданныеРегистра];
	Если ОписаниеИзмерений <> Неопределено Тогда
		Возврат ОписаниеИзмерений;
	КонецЕсли;
	
	// Период и регистратор, если есть.
	ОписаниеИзмерений = Новый Структура;
	
	ДанныеИзмерения = Новый Структура("Ведущее, Представление, Формат, Тип", Ложь);
	
	Если Метаданные.РегистрыСведений.Содержит(МетаданныеРегистра) Тогда
		// Возможно есть период
		МетаПериод = МетаданныеРегистра.ПериодичностьРегистраСведений; 
		Периодичность = Метаданные.СвойстваОбъектов.ПериодичностьРегистраСведений;
		
		Если МетаПериод = Периодичность.ПозицияРегистратора Тогда
			ДанныеИзмерения.Тип           = Документы.ТипВсеСсылки();
			ДанныеИзмерения.Представление = НСтр("ru = 'Регистратор'");
			ДанныеИзмерения.Ведущее       = Истина;
			ОписаниеИзмерений.Вставить("Регистратор", ДанныеИзмерения);
			
		ИначеЕсли МетаПериод = Периодичность.Год Тогда
			ДанныеИзмерения.Тип           = Новый ОписаниеТипов("Дата");
			ДанныеИзмерения.Представление = НСтр("ru = 'Период'");
			ДанныеИзмерения.Формат        = НСтр("ru = 'ДФ=''yyyy ""г.""''; ДП=''Дата не задана'''");
			ОписаниеИзмерений.Вставить("Период", ДанныеИзмерения);
			
		ИначеЕсли МетаПериод = Периодичность.День Тогда
			ДанныеИзмерения.Тип           = Новый ОписаниеТипов("Дата");
			ДанныеИзмерения.Представление = НСтр("ru = 'Период'");
			ДанныеИзмерения.Формат        = НСтр("ru = 'ДЛФ=D; ДП=''Дата не задана'''");
			ОписаниеИзмерений.Вставить("Период", ДанныеИзмерения);
			
		ИначеЕсли МетаПериод = Периодичность.Квартал Тогда
			ДанныеИзмерения.Тип           = Новый ОписаниеТипов("Дата");
			ДанныеИзмерения.Представление = НСтр("ru = 'Период'");
			ДанныеИзмерения.Формат        =  НСтр("ru = 'ДФ=''к """"квартал """"yyyy """"г.""""''; ДП=''Дата не задана'''");
			ОписаниеИзмерений.Вставить("Период", ДанныеИзмерения);
			
		ИначеЕсли МетаПериод = Периодичность.Месяц Тогда
			ДанныеИзмерения.Тип           = Новый ОписаниеТипов("Дата");
			ДанныеИзмерения.Представление = НСтр("ru = 'Период'");
			ДанныеИзмерения.Формат        = НСтр("ru = 'ДФ=''ММММ yyyy """"г.""""''; ДП=''Дата не задана'''");
			ОписаниеИзмерений.Вставить("Период", ДанныеИзмерения);
			
		ИначеЕсли МетаПериод = Периодичность.Секунда Тогда
			ДанныеИзмерения.Тип           = Новый ОписаниеТипов("Дата");
			ДанныеИзмерения.Представление = НСтр("ru = 'Период'");
			ДанныеИзмерения.Формат        = НСтр("ru = 'ДЛФ=DT; ДП=''Дата не задана'''");
			ОписаниеИзмерений.Вставить("Период", ДанныеИзмерения);
			
		КонецЕсли;
		
	Иначе
		ДанныеИзмерения.Тип           = Документы.ТипВсеСсылки();
		ДанныеИзмерения.Представление = НСтр("ru = 'Регистратор'");
		ДанныеИзмерения.Ведущее       = Истина;
		ОписаниеИзмерений.Вставить("Регистратор", ДанныеИзмерения);
		
	КонецЕсли;
	
	// Все измерения
	Для Каждого МетаИзмерение Из МетаданныеРегистра.Измерения Цикл
		ДанныеИзмерения = Новый Структура("Ведущее, Представление, Формат, Тип");
		ДанныеИзмерения.Тип           = МетаИзмерение.Тип;
		ДанныеИзмерения.Представление = МетаИзмерение.Представление();
		ДанныеИзмерения.Ведущее       = МетаИзмерение.Ведущее;
		ОписаниеИзмерений.Вставить(МетаИзмерение.Имя, ДанныеИзмерения);
	КонецЦикла;
	
	КэшИзмеренийРегистров[МетаданныеРегистра] = ОписаниеИзмерений;
	Возврат ОписаниеИзмерений;
	
КонецФункции

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

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

#Область УсловныеВызовы

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

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

#Область Данные

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

КонецФункции

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

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

Функция СравнитьМассивы(Знач Массив1, Знач Массив2)
	
	Если Массив1.Количество() <> Массив2.Количество() Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Для Каждого Элемент Из Массив1 Цикл
		Если Массив2.Найти(Элемент) = Неопределено Тогда
			Возврат Ложь;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Истина;
	
КонецФункции		

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

Функция РазмерСтрокиВБайтах(Знач Строка)
	
	Возврат ПолучитьДвоичныеДанныеИзСтроки(Строка, "UTF-8").Размер();

КонецФункции

#Область СкопироватьРекурсивно

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

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

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

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

Процедура СкопироватьЗначенияТаблицыЗначений(ТаблицаЗначений, ФиксироватьДанные)
	Для Каждого СтрокаТаблицыЗначений Из ТаблицаЗначений Цикл
		Для Каждого Колонка Из ТаблицаЗначений.Колонки Цикл
			СтрокаТаблицыЗначений[Колонка.Имя] = СкопироватьРекурсивно(СтрокаТаблицыЗначений[Колонка.Имя], ФиксироватьДанные);
		КонецЦикла;
	КонецЦикла;
КонецПроцедуры

Процедура СкопироватьЗначенияСтрокиДереваЗначений(СтрокиДереваЗначений, ФиксироватьДанные);
	Для Каждого СтрокаДереваЗначений Из СтрокиДереваЗначений Цикл
		Для Каждого Колонка Из СтрокаДереваЗначений.Владелец().Колонки Цикл
			СтрокаДереваЗначений[Колонка.Имя] = СкопироватьРекурсивно(СтрокаДереваЗначений[Колонка.Имя], ФиксироватьДанные);
		КонецЦикла;
		СкопироватьЗначенияСтрокиДереваЗначений(СтрокаДереваЗначений.Строки, ФиксироватьДанные);
	КонецЦикла;
КонецПроцедуры

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

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

#Область Метаданные

Процедура ПроверитьОбъектМетаданныхСуществует(ПолноеИмя)
	
	Если ОбъектМетаданныхПоПолномуИмени(ПолноеИмя) = Неопределено Тогда 
		ВызватьИсключение(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Несуществующий тип объекта метаданных ""%1"".'"), ПолноеИмя),
			КатегорияОшибки.ОшибкаКонфигурации);
	КонецЕсли;
	
КонецПроцедуры

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

#Область ХранилищеНастроек

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

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

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

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

// Возвращает строку ключа настроек, не превышающую допустимую длину 128 символов.
// Если указанная строка превышает 128, тогда вместо символов сверх 96 символов
// добавляется их хеш-сумма по алгоритму MD5 размером 32 символа.
//
// Параметры:
//  Строка - Строка - строка произвольной длины.
//
// Возвращаемое значение:
//  Строка - не более 128 символов.
//
Функция КлючНастроек(Знач Строка)
	Возврат СократитьСтрокуКонтрольнойСуммой(Строка, 128);
КонецФункции

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

#Область БезопасноеХранилище

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

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

#Область БезопасноеВыполнениеВнешнегоКода

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

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

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

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

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

#Область ВнешниеКомпоненты

Процедура ПроверитьМестоположениеКомпоненты(Идентификатор, Местоположение)
	
	Если МакетСуществует(Местоположение) Тогда
		Возврат;
	КонецЕсли;
	
	Если ПодсистемаСуществует("СтандартныеПодсистемы.ВнешниеКомпоненты") Тогда
		МодульВнешниеКомпонентыСлужебный = ОбщийМодуль("ВнешниеКомпонентыСлужебный");
		МодульВнешниеКомпонентыСлужебный.ПроверитьМестоположениеКомпоненты(Идентификатор, Местоположение);
		Возврат;
	КонецЕсли;
	ВызватьИсключение(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Указан несуществующий макет ""%1"" при подключении внешней компоненты ""%2"".'"),
			Местоположение, Идентификатор),
		КатегорияОшибки.ОшибкаКонфигурации);
	
КонецПроцедуры

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

Функция СистемнаяИнформацияДляЖурналаРегистрации()
	
	Возврат СтандартныеПодсистемыСервер.ИнформацияДляПоддержки();
	
КонецФункции

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

#Область ТекущееОкружение

Функция НомерСборкиПоТекущейВерсииПлатформы(НомераСборокСтрокой)
	
	НомераСборок = СтрРазделить(НомераСборокСтрокой, ";", Истина);
	
	СборкиПоВерсиям = Новый Соответствие;
	Для Каждого НомерСборки Из НомераСборок Цикл
		НомерВерсии = ОбщегоНазначенияКлиентСервер.ВерсияКонфигурацииБезНомераСборки(НомерСборки);
		СборкиПоВерсиям.Вставить(СокрЛП(НомерВерсии), СокрЛП(НомерСборки));
	КонецЦикла;
	
	СистемнаяИнформация = Новый СистемнаяИнформация;
	ТекущаяВерсия = ОбщегоНазначенияКлиентСервер.ВерсияКонфигурацииБезНомераСборки(СистемнаяИнформация.ВерсияПриложения);
	
	Результат = СборкиПоВерсиям[ТекущаяВерсия];
	Если Не ЗначениеЗаполнено(Результат) Тогда
		Результат = НомераСборок[0];
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

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

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

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

Функция НедопустимыеВерсииПлатформы() Экспорт
	
	Возврат "";
	
КонецФункции

Функция ЭтоВерсияЗащищенногоПрограммногоКомплекса(Версия)
	
	СистемнаяИнформация = Новый СистемнаяИнформация();
	Возврат ЗначениеЗаполнено(СистемнаяИнформация.ВариантПриложения);
	
КонецФункции

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

// Параметры:
//   ОбщиеПараметры - см. ОбщиеПараметрыБазовойФункциональности
// 
Процедура УточнитьМинимальнуюВерсиюПлатформыИзСервиса(ОбщиеПараметры)
	
	МинимальнаяИзСервиса = "";
	РекомендуемаяИзСервиса = "";

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

	ОбщиеПараметры.МинимальнаяВерсияПлатформы = Минимальная;
	ОбщиеПараметры.РекомендуемаяВерсияПлатформы = Рекомендуемая;

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

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

#Область ПроверкаАлгоритмов

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

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

Функция НедопустимыеМетоды()
	Массив = Новый Массив;
	Массив.Добавить("Выполнить"); // @Non-NLS
	Массив.Добавить("Вычислить"); // @Non-NLS
	Массив.Добавить("Подключить"); // @Non-NLS
	Массив.Добавить("ЗафиксироватьТранзакцию"); // @Non-NLS
	Массив.Добавить("СократитьЖурналРегистрации"); // @Non-NLS
	Массив.Добавить("УстановитьИспользованиеСобытияЖурналаРегистрации"); // @Non-NLS
	Массив.Добавить("ЗапуститьПриложение"); // @Non-NLS
	Массив.Добавить("УстановитьМаксимальныйСрокДействияПаролейПользователей"); // @Non-NLS
	Массив.Добавить("УстановитьМинимальнуюДлинуПаролейПользователей"); // @Non-NLS
	Массив.Добавить("УстановитьМинимальныйСрокДействияПаролейПользователей"); // @Non-NLS
	Массив.Добавить("УстановитьОграничениеПовторенияПаролейПользователейСредиПоследних"); // @Non-NLS
	Массив.Добавить("УстановитьПроверкуСложностиПаролейПользователей"); // @Non-NLS
	Массив.Добавить("УстановитьНастройкиВосстановленияПароля"); // @Non-NLS
	
	Массив.Добавить("Execute");
	Массив.Добавить("Eval");
	Массив.Добавить("Connect");
	Массив.Добавить("CommitTransaction");
	Массив.Добавить("TruncateEventLog");
	Массив.Добавить("SetEventLogEventUse");
	Массив.Добавить("RunApp");
	Массив.Добавить("SetUserPasswordMaxEffectivePeriod");
	Массив.Добавить("SetUserPasswordMinLength");
	Массив.Добавить("SetUserPasswordMinEffectivePeriod");
	Массив.Добавить("SetUserPasswordReuseLimit");
	Массив.Добавить("SetUserPasswordStrengthCheck");
	Массив.Добавить("SetPasswordRecoverySettings");
	
	Возврат Массив;
КонецФункции

Функция ЗапрещенныеСвойстваГлобальногоКонтекста()
	
	Массив = Новый Массив;
	Массив.Добавить("ПользователиИнформационнойБазы"); // @Non-NLS
	Массив.Добавить("ДополнительныеНастройкиАутентификации"); // @Non-NLS
	Массив.Добавить("ПараметрыСеанса"); // @Non-NLS
	Массив.Добавить("InfoBaseUsers");
	Массив.Добавить("AdditionalAuthenticationSettings");
	Массив.Добавить("SessionParameters");
	
	Возврат Массив;
	
КонецФункции

Функция ЗапрещенныеМодули()
	
	Массив = Новый Массив;
	Массив.Добавить("ДлительныеОперации");
	
	Возврат Массив;
	
КонецФункции

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

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

#КонецЕсли