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

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

#Область РаботаСИсключениями

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

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

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

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

// АПК:142-выкл 4 необязательных параметра для совместимости с предыдущими версиями библиотеки.

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

// АПК:142-вкл

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

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

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

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

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

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

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

// Возвращает имя значения перечисления типа платформы.
//
// Параметры:
//  ЗначениеТипаПлатформы - Неопределено - вернуть текущее имя типа платформы
//                                         (из объекта СистемнаяИнформация).
//                        - ТипПлатформы - вернуть имя указанного значения типа платформы.
// Возвращаемое значение:
//  Строка - например, Windows_x86_64, Linux_x86_64, MacOS_x86...
//
Функция ИмяТипаПлатформы(Знач ЗначениеТипаПлатформы = Неопределено) Экспорт
	
	СистемнаяИнформация = Новый СистемнаяИнформация;
	Если ТипЗнч(ЗначениеТипаПлатформы) <> Тип("ТипПлатформы") Тогда
		ЗначениеТипаПлатформы = СистемнаяИнформация.ТипПлатформы;
	КонецЕсли;
	
	ИменаТиповПлатформы = Новый Массив;
	ИменаТиповПлатформы.Добавить("Linux_x86");
	ИменаТиповПлатформы.Добавить("Linux_x86_64");
	
	ИменаТиповПлатформы.Добавить("MacOS_x86");
	ИменаТиповПлатформы.Добавить("MacOS_x86_64");
	
	ИменаТиповПлатформы.Добавить("Windows_x86");
	ИменаТиповПлатформы.Добавить("Windows_x86_64");
	
#Если Не МобильныйКлиент Тогда
	ИменаТиповПлатформы.Добавить("Linux_ARM64");
	ИменаТиповПлатформы.Добавить("Linux_E2K");
#КонецЕсли
	
	ИменаТиповПлатформы.Добавить("Android_ARM");
	ИменаТиповПлатформы.Добавить("Android_ARM_64");
	ИменаТиповПлатформы.Добавить("Android_x86");
	ИменаТиповПлатформы.Добавить("Android_x86_64");
	
	ИменаТиповПлатформы.Добавить("iOS_ARM");
	ИменаТиповПлатформы.Добавить("iOS_ARM_64");
	
	ИменаТиповПлатформы.Добавить("WinRT_ARM");
	ИменаТиповПлатформы.Добавить("WinRT_x86");
	ИменаТиповПлатформы.Добавить("WinRT_x86_64");
	
	Для Каждого ИмяТипаПлатформы Из ИменаТиповПлатформы Цикл
		Если ЗначениеТипаПлатформы = ТипПлатформы[ИмяТипаПлатформы] Тогда
			Возврат ИмяТипаПлатформы;
		КонецЕсли;
	КонецЦикла;
	
	Возврат "";
	
КонецФункции

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// Возвращает пустой уникальный идентификатор.
//
// Возвращаемое значение:
//  УникальныйИдентификатор - 00000000-0000-0000-0000-000000000000
//
Функция ПустойУникальныйИдентификатор() Экспорт
	
	Возврат Новый УникальныйИдентификатор("00000000-0000-0000-0000-000000000000");
	
КонецФункции

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

#Область ВерсионированиеКонфигураций

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

// Сравнить две строки версий.
//
// Параметры:
//  СтрокаВерсии1  - Строка - номер версии в формате РР.{П|ПП}.ЗЗ.СС.
//  СтрокаВерсии2  - Строка - второй сравниваемый номер версии.
//
// Возвращаемое значение:
//   Число   - больше 0, если СтрокаВерсии1 > СтрокаВерсии2; 0, если версии равны.
//
Функция СравнитьВерсии(Знач СтрокаВерсии1, Знач СтрокаВерсии2) Экспорт
	
	Строка1 = ?(ПустаяСтрока(СтрокаВерсии1), "0.0.0.0", СтрокаВерсии1);
	Строка2 = ?(ПустаяСтрока(СтрокаВерсии2), "0.0.0.0", СтрокаВерсии2);
	Версия1 = СтрРазделить(Строка1, ".");
	Если Версия1.Количество() <> 4 Тогда
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Неправильный формат параметра %1: %2'"), "СтрокаВерсии1", СтрокаВерсии1);
	КонецЕсли;
	Версия2 = СтрРазделить(Строка2, ".");
	Если Версия2.Количество() <> 4 Тогда
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
	    	НСтр("ru = 'Неправильный формат параметра %1: %2'"), "СтрокаВерсии2", СтрокаВерсии2);
	КонецЕсли;
	
	Результат = 0;
	Для Разряд = 0 По 3 Цикл
		Результат = Число(Версия1[Разряд]) - Число(Версия2[Разряд]);
		Если Результат <> 0 Тогда
			Возврат Результат;
		КонецЕсли;
	КонецЦикла;
	Возврат Результат;
	
КонецФункции

// Сравнить две строки версий.
//
// Параметры:
//  СтрокаВерсии1  - Строка - номер версии в формате РР.{П|ПП}.ЗЗ.
//  СтрокаВерсии2  - Строка - второй сравниваемый номер версии.
//
// Возвращаемое значение:
//   Число   - больше 0, если СтрокаВерсии1 > СтрокаВерсии2; 0, если версии равны.
//
Функция СравнитьВерсииБезНомераСборки(Знач СтрокаВерсии1, Знач СтрокаВерсии2) Экспорт
	
	Строка1 = ?(ПустаяСтрока(СтрокаВерсии1), "0.0.0", СтрокаВерсии1);
	Строка2 = ?(ПустаяСтрока(СтрокаВерсии2), "0.0.0", СтрокаВерсии2);
	Версия1 = СтрРазделить(Строка1, ".");
	Если Версия1.Количество() <> 3 Тогда
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Неправильный формат параметра %1: %2'"), "СтрокаВерсии1", СтрокаВерсии1);
	КонецЕсли;
	Версия2 = СтрРазделить(Строка2, ".");
	Если Версия2.Количество() <> 3 Тогда
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
	    	НСтр("ru = 'Неправильный формат параметра %1: %2'"), "СтрокаВерсии2", СтрокаВерсии2);
	КонецЕсли;
	
	Результат = 0;
	Для Разряд = 0 По 2 Цикл
		Результат = Число(Версия1[Разряд]) - Число(Версия2[Разряд]);
		Если Результат <> 0 Тогда
			Возврат Результат;
		КонецЕсли;
	КонецЦикла;
	Возврат Результат;
	
КонецФункции

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

#Область Формы

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

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

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

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

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

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

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

	Если НЕ ПустаяСтрока(Комментарий) Тогда
		Картинка = БиблиотекаКартинок.Комментарий;
	Иначе
		Картинка = Новый Картинка;
	КонецЕсли;
	
	Возврат Картинка;
	
КонецФункции

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

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

////////////////////////////////////////////////////////////////////////////////
// Функции для работы с отборами и параметрами динамических списков.
//

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

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

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

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

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

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

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

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

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

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

#Область РаботаСФайлами

////////////////////////////////////////////////////////////////////////////////
// Функции для работы с файлами.
//

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

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

	Если ПустаяСтрока(ИмяФайла) Тогда
		Возврат ИмяКаталога;
	КонецЕсли;
	
	РазделительПути = "";
	Если (Прав(ИмяКаталога, 1) <> "\") И (Прав(ИмяКаталога, 1) <> "/") Тогда
		РазделительПути = ?(СтрНайти(ИмяКаталога, "\") = 0, "/", "\");
	КонецЕсли;
	
	Возврат ИмяКаталога + РазделительПути + ИмяФайла;

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

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

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

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

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

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

// Возвращает строку недопустимых символов в имени файла
// согласно http://en.wikipedia.org/wiki/Filename - в разделе "Reserved characters and words".
// 
// Возвращаемое значение:
//   Строка
//
Функция ПолучитьНедопустимыеСимволыВИмениФайла() Экспорт

	НедопустимыеСимволы = """/\[]:;|=?*<>";
	НедопустимыеСимволы = НедопустимыеСимволы + Символы.Таб + Символы.ПС;
	Возврат НедопустимыеСимволы;

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

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

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

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

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

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

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

#Область РаботаСАдресамиЭлектроннойПочты

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

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

// Проверяет email-адрес на соответствие требованиям стандартов RFC 5321, RFC 5322,
// а также RFC 5335, RFC 5336 и RFC 3696.
// Кроме того, функция ограничивает использование спецсимволов.
// 
// Параметры:
//  Адрес - Строка - проверяемый email.
//  РазрешитьЛокальныеАдреса - Булево - не выдавать ошибку в случае отсутствия зоны домена в адресе.
//
// Возвращаемое значение:
//  Булево - Истина, если ошибок нет.
//
Функция АдресЭлектроннойПочтыСоответствуетТребованиям(Знач Адрес, РазрешитьЛокальныеАдреса = Ложь) Экспорт
	
	// Допустимые символы для email.
	Буквы = "abcdefghijklmnopqrstuvwxyzабвгдеёжзийклмнопрстуфхцчшщъыьэюя";
	Цифры = "0123456789";
	СпецСимволы = ".@_-:+";
	
	// проверяем символ @
	Если СтрЧислоВхождений(Адрес, "@") <> 1 Тогда
		Возврат Ложь;
	КонецЕсли;
	
	// Разрешаем двоеточие только один раз.
	Если СтрЧислоВхождений(Адрес, ":") > 1 Тогда
		Возврат Ложь;
	КонецЕсли;
	
	// проверяем две точки подряд
	Если СтрНайти(Адрес, "..") > 0 Тогда
		Возврат Ложь;
	КонецЕсли;
	
	// Приводим строку адреса к нижнему регистру.
	Адрес = НРег(Адрес);
	
	// Проверяем допустимые символы.
	Если Не СтрокаСодержитТолькоДопустимыеСимволы(Адрес, Буквы + Цифры + СпецСимволы) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	// Разбираем адрес на local-part и domain.
	Позиция = СтрНайти(Адрес,"@");
	ЛокальноеИмя = Лев(Адрес, Позиция - 1);
	Домен = Сред(Адрес, Позиция + 1);
	
	// Проверяем на заполненность и допустимость длины.
	Если ПустаяСтрока(ЛокальноеИмя)
	 	Или ПустаяСтрока(Домен)
		Или СтрДлина(ЛокальноеИмя) > 64
		Или СтрДлина(Домен) > 255 Тогда
		
		Возврат Ложь;
	КонецЕсли;
	
	// Проверяем наличие спецсимволов в начале и в конце имени и домена.
	Если ЕстьСимволыВНачалеВКонце(ЛокальноеИмя, ".") Или ЕстьСимволыВНачалеВКонце(Домен, СпецСимволы) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	// В домене должна быть минимум одна точка.
	Если Не РазрешитьЛокальныеАдреса И СтрНайти(Домен,".") = 0 Тогда
		Возврат Ложь;
	КонецЕсли;
	
	// В домене не должно быть символа подчеркивания.
	Если СтрНайти(Домен,"_") > 0 Тогда
		Возврат Ложь;
	КонецЕсли;
	
	// В домене не должно быть символа двоеточие.
	Если СтрНайти(Домен,":") > 0 Тогда
		Возврат Ложь;
	КонецЕсли;
	
	// В домене не должно быть символа "плюс".
	Если СтрНайти(Домен,"+") > 0 Тогда
		Возврат Ложь;
	КонецЕсли;
	
	// Выделяем зону (TLD) из имени домена.
	Зона = Домен;
	Позиция = СтрНайти(Зона,".");
	Пока Позиция > 0 Цикл
		Зона = Сред(Зона, Позиция + 1);
		Позиция = СтрНайти(Зона,".");
	КонецЦикла;
	
	// Проверяем зону домена (минимум 2 символа, только буквы).
	Возврат РазрешитьЛокальныеАдреса Или СтрДлина(Зона) >= 2 И СтрокаСодержитТолькоДопустимыеСимволы(Зона,Буквы);
	
КонецФункции

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

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

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

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

// Возвращает имя COM-класса для работы с 1С:Предприятием 8 через COM-соединение.
//
// Возвращаемое значение:
//  Строка
//
Функция ИмяCOMСоединителя() Экспорт
	
	СистемнаяИнфо = Новый СистемнаяИнформация;
	ПодстрокиВерсии = СтрРазделить(СистемнаяИнфо.ВерсияПриложения, ".");
	Возврат "v" + ПодстрокиВерсии[0] + ПодстрокиВерсии[1] + ".COMConnector";
	
КонецФункции

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

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

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

#Область Математика

////////////////////////////////////////////////////////////////////////////////
// Математические процедуры и функции.

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

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

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

// Заменяет недопустимые символы в XML-строке на заданные символы.
//
// Параметры:
//   Текст - Строка - строка, в которой требуется выполнить замену недопустимых символов.
//   СимволЗамены - Строка - строка, на которую требуется выполнить замену недопустимого символа в XML-строке.
// 
// Возвращаемое значение:
//    Строка
//
Функция ЗаменитьНедопустимыеСимволыXML(Знач Текст, СимволЗамены = " ") Экспорт
	
#Если Не ВебКлиент Тогда
	ПозицияНачала = 1;
	Позиция = ОбработкаСтрокиXML.НайтиНедопустимыеСимволыXML(Текст, ПозицияНачала);
	Пока Позиция > 0 Цикл
		НедопустимыйСимвол = Сред(Текст, Позиция, 1);
		Текст = СтрЗаменить(Текст, НедопустимыйСимвол, СимволЗамены);
		ПозицияНачала = Позиция + СтрДлина(СимволЗамены);
		Если ПозицияНачала > СтрДлина(Текст) Тогда
			Прервать;
		КонецЕсли;
		Позиция = ОбработкаСтрокиXML.НайтиНедопустимыеСимволыXML(Текст, ПозицияНачала);
	КонецЦикла;
	
	Возврат Текст;
#Иначе
	// Коды символов от 0 до 2^16-1, которые метод НайтиНедопустимыеСимволыXML
	// считает недопустимыми: 0-8, 11-12, 14-31, 55296-57343.
	Итог = "";
	ДлинаСтроки = СтрДлина(Текст);
	
	Для НомерСимвола = 1 По ДлинаСтроки Цикл
		Символ = Сред(Текст, НомерСимвола, 1);
		КодСимвола = КодСимвола(Символ);
		
		Если КодСимвола < 9
		 Или КодСимвола > 10    И КодСимвола < 13
		 Или КодСимвола > 13    И КодСимвола < 32
		 Или КодСимвола > 55295 И КодСимвола < 57344 Тогда
			
			Символ = СимволЗамены;
		КонецЕсли;
		Итог = Итог + Символ;
	КонецЦикла;
	
	Возврат Итог;
#КонецЕсли
	
КонецФункции

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

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

#Область ТабличныйДокумент

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

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

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

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

#Область РегламентныеЗадания

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

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

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

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

#Область Интернет

// Разбирает строку URI на составные части и возвращает в виде структуры.
// На основе RFC 3986.
//
// Параметры:
//  СтрокаURI - Строка - ссылка на ресурс в формате:
//                       <схема>://<логин>:<пароль>@<хост>:<порт>/<путь>?<параметры>#<якорь>.
//
// Возвращаемое значение:
//  Структура - составные части URI согласно формату:
//   * Схема         - Строка - схема из URI.
//   * Логин         - Строка - логин из URI.
//   * Пароль        - Строка - пароль из URI.
//   * ИмяСервера    - Строка - часть <хост>:<порт> из URI.
//   * Хост          - Строка - хост из URI.
//   * Порт          - Строка - порт из URI.
//   * ПутьНаСервере - Строка - часть <путь>?<параметры>#<якорь> из URI.
//
Функция СтруктураURI(Знач СтрокаURI) Экспорт
	
	СтрокаURI = СокрЛП(СтрокаURI);
	
	// схема
	Схема = "";
	Позиция = СтрНайти(СтрокаURI, "://");
	Если Позиция > 0 Тогда
		Схема = НРег(Лев(СтрокаURI, Позиция - 1));
		СтрокаURI = Сред(СтрокаURI, Позиция + 3);
	КонецЕсли;
	
	// Строка соединения и путь на сервере.
	СтрокаСоединения = СтрокаURI;
	ПутьНаСервере = "";
	Позиция = СтрНайти(СтрокаСоединения, "/");
	Если Позиция > 0 Тогда
		ПутьНаСервере = Сред(СтрокаСоединения, Позиция + 1);
		СтрокаСоединения = Лев(СтрокаСоединения, Позиция - 1);
	КонецЕсли;
	
	// Информация пользователя и имя сервера.
	СтрокаАвторизации = "";
	ИмяСервера = СтрокаСоединения;
	Позиция = СтрНайти(СтрокаСоединения, "@", НаправлениеПоиска.СКонца);
	Если Позиция > 0 Тогда
		СтрокаАвторизации = Лев(СтрокаСоединения, Позиция - 1);
		ИмяСервера = Сред(СтрокаСоединения, Позиция + 1);
	КонецЕсли;
	
	// логин и пароль
	Логин = СтрокаАвторизации;
	Пароль = "";
	Позиция = СтрНайти(СтрокаАвторизации, ":");
	Если Позиция > 0 Тогда
		Логин = Лев(СтрокаАвторизации, Позиция - 1);
		Пароль = Сред(СтрокаАвторизации, Позиция + 1);
	КонецЕсли;
	
	// хост и порт
	Хост = ИмяСервера;
	Порт = "";
	Позиция = СтрНайти(ИмяСервера, ":");
	Если Позиция > 0 Тогда
		Хост = Лев(ИмяСервера, Позиция - 1);
		Порт = Сред(ИмяСервера, Позиция + 1);
		Если Не СтроковыеФункцииКлиентСервер.ТолькоЦифрыВСтроке(Порт) Тогда
			Порт = "";
		КонецЕсли;
	КонецЕсли;
	
	Результат = Новый Структура;
	Результат.Вставить("Схема", Схема);
	Результат.Вставить("Логин", Логин);
	Результат.Вставить("Пароль", Пароль);
	Результат.Вставить("ИмяСервера", ИмяСервера);
	Результат.Вставить("Хост", Хост);
	Результат.Вставить("Порт", ?(ПустаяСтрока(Порт), Неопределено, Число(Порт)));
	Результат.Вставить("ПутьНаСервере", ПутьНаСервере);
	
	Возврат Результат;
	
КонецФункции

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

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

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

#Область ПараметрыУстройства

// Возвращает строковое представление типа используемого устройства.
//
// Возвращаемое значение:
//   Строка - тип используемого устройства.
//
Функция ТипУстройства() Экспорт
	
	ИнформацияЭкрана = ПараметрыЭкранаУстройства();
	
	DPI    = ИнформацияЭкрана.DPI; // АПК:1353 - не требуется перевод на русский язык.
	Высота = ИнформацияЭкрана.Высота;
	Ширина = ИнформацияЭкрана.Ширина;
	
	РазмерЭкрана = Sqrt((Высота/DPI*Высота/DPI)+(Ширина/DPI*Ширина/DPI));
	Если РазмерЭкрана > 16 Тогда
		Возврат "ПерсональныйКомпьютер";
	ИначеЕсли РазмерЭкрана >= ?(DPI > 310, 7.85, 9) Тогда
		Возврат "Планшет";
	ИначеЕсли РазмерЭкрана >= 4.9 Тогда
		Возврат "Фаблет";
	Иначе
		Возврат "Телефон";
	КонецЕсли;
	
КонецФункции

// Возвращает параметры экрана используемого устройства.
//
// Возвращаемое значение:
//   Структура:
//     * Ширина  - Число - ширина экрана в пикселях.
//     * Высота  - Число - высота экрана в пикселях.
//     * DPI     - Число - плотность пикселей экрана.
//     * Портрет - Булево - если экран в портретной ориентации, тогда Истина, иначе - Ложь.
//
Функция ПараметрыЭкранаУстройства() Экспорт
	
	ПараметрыЭкрана = Новый Структура;
	ИнформацияЭкрана = ПолучитьИнформациюЭкрановКлиента();
	
	Ширина = ИнформацияЭкрана[0].Ширина;
	Высота = ИнформацияЭкрана[0].Высота;
	
	ПараметрыЭкрана.Вставить("Ширина",  Ширина);
	ПараметрыЭкрана.Вставить("Высота",  Высота);
	ПараметрыЭкрана.Вставить("DPI",     ИнформацияЭкрана[0].DPI);
	ПараметрыЭкрана.Вставить("Портрет", Высота > Ширина);
	
	Возврат ПараметрыЭкрана;
	
КонецФункции

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

#Область ПроверкаТипаЗначения

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

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

#Область ПриведениеЗначения

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

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

#Область ПреобразованиеДатыДляHTTP

// Преобразует универсальную дату в дату формата rfc1123-date.
// См. https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html, п. 3.3.1.
// 
// Параметры:
//  Дата - Дата
// 
// Возвращаемое значение:
//  Строка
//
// Пример:
//  ДатаHTTP(Дата(2021,12,9,9,14,58)) = "Thu, 09 Dec 2021 09:14:58 GMT".
//
Функция ДатаHTTP(Знач Дата) Экспорт
	
	ДниНедели = СтрРазделить("Mon,Tue,Wed,Thu,Fri,Sat,Sun", ",");
	Месяцы = СтрРазделить("Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", ",");
	
	ШаблонДаты = "[ДеньНедели], [День] [Месяц] [Год] [Час]:[Минута]:[Секунда] GMT"; // АПК:1297 не локализуется
	
	ПараметрыДаты = Новый Структура;
	ПараметрыДаты.Вставить("ДеньНедели", ДниНедели[ДеньНедели(Дата)-1]);
	ПараметрыДаты.Вставить("День", Формат(День(Дата), "ЧЦ=2; ЧВН="));
	ПараметрыДаты.Вставить("Месяц", Месяцы[Месяц(Дата)-1]);
	ПараметрыДаты.Вставить("Год", Формат(Год(Дата), "ЧЦ=4; ЧВН=; ЧГ=0"));
	ПараметрыДаты.Вставить("Час", Формат(Час(Дата), "ЧЦ=2; ЧН=00; ЧВН="));
	ПараметрыДаты.Вставить("Минута", Формат(Минута(Дата), "ЧЦ=2; ЧН=00; ЧВН="));
	ПараметрыДаты.Вставить("Секунда", Формат(Секунда(Дата), "ЧЦ=2; ЧН=00; ЧВН="));
	
	ДатаHTTP = СтроковыеФункцииКлиентСервер.ВставитьПараметрыВСтроку(ШаблонДаты, ПараметрыДаты);
	
	Возврат ДатаHTTP;
	
КонецФункции

// Возвращает дату, преобразованную из формата rfc1123-date в тип Дата.
// См. https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html, п. 3.3.1.
// 
// Параметры:
//  ДатаСтрокойHTTP - Строка
// 
// Возвращаемое значение:
//  Дата
//
// Пример:
//  ДатаRFC1123("Thu, 09 Dec 2021 09:14:58 GMT") = Дата(2021,12,9,9,14,58).
//
Функция ДатаRFC1123(ДатаСтрокойHTTP) Экспорт

	ИменаМесяцев = "janfebmaraprmayjunjulaugsepoctnovdec";
	// rfc1123-date = wkday "," SP date1 SP time SP "GMT".
	ПозицияПервогоПробела = СтрНайти(ДатаСтрокойHTTP, " ");//с первого пробела до второго пробела идет дата.
	ПодстрокаДата = Сред(ДатаСтрокойHTTP,ПозицияПервогоПробела + 1);
	ПодстрокаВремя = Сред(ПодстрокаДата, 13);
	ПодстрокаДата = Лев(ПодстрокаДата, 11);
	ПозицияПервогоПробела = СтрНайти(ПодстрокаВремя, " ");
	ПодстрокаВремя = Лев(ПодстрокаВремя,ПозицияПервогоПробела - 1);
	// date1 = 2DIGIT SP month SP 4DIGIT.
	ПодстрокаДень = Лев(ПодстрокаДата, 2);
	ПодстрокаМесяц = Формат(Цел(СтрНайти(ИменаМесяцев,НРег(Сред(ПодстрокаДата,4,3))) / 3)+1, "ЧЦ=2; ЧН=00; ЧВН=");
	ПодстрокаГод = Сред(ПодстрокаДата, 8);
	// time = 2DIGIT ":" 2DIGIT ":" 2DIGIT.
	ПодстрокаЧас = Лев(ПодстрокаВремя, 2);
	ПодстрокаМинута = Сред(ПодстрокаВремя, 4, 2);
	ПодстрокаСекунда = Прав(ПодстрокаВремя, 2);
	
	Возврат Дата(ПодстрокаГод + ПодстрокаМесяц + ПодстрокаДень + ПодстрокаЧас + ПодстрокаМинута + ПодстрокаСекунда);
	
КонецФункции

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

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

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

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

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

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

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

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

// АПК:547-выкл устаревший программный интерфейс может вызывать устаревшие процедуры и функции.

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

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

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

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

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

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

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

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

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

// Устарела. Следует использовать ОбщегоНазначенияКлиент.УстановитьВнешнееСоединениеСБазой 
//  или ОбщегоНазначения.УстановитьВнешнееСоединениеСБазой
// Устанавливает внешнее соединение с информационной базой по переданным параметрам подключения и возвращает указатель
// на это соединение.
// 
// Параметры:
//  Параметры - Структура - параметры для установки внешнего соединения с информационной базой.
//                          Свойства см. в функции
//                          ОбщегоНазначенияКлиентСервер.СтруктураПараметровДляУстановкиВнешнегоСоединения):
//
//   * ВариантРаботыИнформационнойБазы             - Число  - вариант работы информационной базы: 0 - файловый; 1 -
//                                                            клиент-серверный;
//   * КаталогИнформационнойБазы                   - Строка - каталог информационной базы для файлового режима работы;
//   * ИмяСервера1СПредприятия                     - Строка - имя сервера1С:Предприятия;
//   * ИмяИнформационнойБазыНаСервере1СПредприятия - Строка - имя информационной базы на сервере1С:Предприятия;
//   * АутентификацияОперационнойСистемы           - Булево - признак аутентификации операционной системы при создании
//                                                            внешнего подключения к информационной базе;
//   * ИмяПользователя                             - Строка - имя пользователя информационной базы;
//   * ПарольПользователя                          - Строка - пароль пользователя информационной базы.
// 
// Возвращаемое значение:
//  Структура:
//    * Соединение                  - COMОбъект
//                                  - Неопределено - указатель на COM-объект соединения или Неопределено в
//                                    случае ошибки;
//    * КраткоеОписаниеОшибки       - Строка - краткое описание ошибки;
//    * ПодробноеОписаниеОшибки     - Строка - подробное описание ошибки;
//    * ОшибкаПодключенияКомпоненты - Булево - флаг ошибки подключения COM.
//
Функция УстановитьВнешнееСоединениеСБазой(Параметры) Экспорт
	
	Результат = Новый Структура;
	Результат.Вставить("Соединение");
	Результат.Вставить("КраткоеОписаниеОшибки", "");
	Результат.Вставить("ПодробноеОписаниеОшибки", "");
	Результат.Вставить("ОшибкаПодключенияКомпоненты", Ложь);
	
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
		ПодключениеНедоступно = ОбщегоНазначения.ЭтоLinuxСервер();
		КраткоеОписаниеОшибки = НСтр("ru = 'Прямое подключение к информационной базе недоступно на сервере под управлением ОС Linux.'");
#Иначе
		ПодключениеНедоступно = ЭтоLinuxКлиент() Или ЭтоOSXКлиент() Или ЭтоМобильныйКлиент();
		КраткоеОписаниеОшибки = НСтр("ru = 'Прямое подключение к информационной базе доступно только на клиенте под управлением ОС Windows.'");
#КонецЕсли
	
	Если ПодключениеНедоступно Тогда
		Результат.Соединение = Неопределено;
		Результат.КраткоеОписаниеОшибки = КраткоеОписаниеОшибки;
		Результат.ПодробноеОписаниеОшибки = КраткоеОписаниеОшибки;
		Возврат Результат;
	КонецЕсли;
	
#Если Не МобильныйКлиент Тогда
		Попытка
			COMConnector = Новый COMObject(ИмяCOMСоединителя()); // "V83.COMConnector"
		Исключение
			Информация = ИнформацияОбОшибке();
			СтрокаСообщенияОбОшибке = НСтр("ru = 'Не удалось подключиться к другому приложению: %1'");
			
			Результат.ОшибкаПодключенияКомпоненты = Истина;
			Результат.ПодробноеОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(СтрокаСообщенияОбОшибке, ОбработкаОшибок.ПодробноеПредставлениеОшибки(Информация));
			Результат.КраткоеОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(СтрокаСообщенияОбОшибке, ОбработкаОшибок.КраткоеПредставлениеОшибки(Информация));
			
			Возврат Результат;
		КонецПопытки;
	
		ФайловыйВариантРаботы = Параметры.ВариантРаботыИнформационнойБазы = 0;
		
		// Проверка корректности указания параметров.
		ОшибкаПроверкиЗаполнения = Ложь;
		Если ФайловыйВариантРаботы Тогда
			
			Если ПустаяСтрока(Параметры.КаталогИнформационнойБазы) Тогда
				СтрокаСообщенияОбОшибке = НСтр("ru = 'Не задано месторасположение каталога информационной базы.'");
				ОшибкаПроверкиЗаполнения = Истина;
			КонецЕсли;
			
		Иначе
			
			Если ПустаяСтрока(Параметры.ИмяСервера1СПредприятия) Или ПустаяСтрока(Параметры.ИмяИнформационнойБазыНаСервере1СПредприятия) Тогда
				СтрокаСообщенияОбОшибке = НСтр("ru = 'Не заданы обязательные параметры подключения: ""Имя сервера""; ""Имя информационной базы на сервере"".'");
				ОшибкаПроверкиЗаполнения = Истина;
			КонецЕсли;
			
		КонецЕсли;
		
		Если ОшибкаПроверкиЗаполнения Тогда
			
			Результат.ПодробноеОписаниеОшибки = СтрокаСообщенияОбОшибке;
			Результат.КраткоеОписаниеОшибки   = СтрокаСообщенияОбОшибке;
			Возврат Результат;
			
		КонецЕсли;
		
		// Формирование строки соединения.
		ШаблонСтрокиСоединения = "[СтрокаБазы][СтрокаАутентификации]";
		
		Если ФайловыйВариантРаботы Тогда
			СтрокаБазы = "File = ""&КаталогИнформационнойБазы""";
			СтрокаБазы = СтрЗаменить(СтрокаБазы, "&КаталогИнформационнойБазы", Параметры.КаталогИнформационнойБазы);
		Иначе
			СтрокаБазы = "Srvr = ""&ИмяСервера1СПредприятия""; Ref = ""&ИмяИнформационнойБазыНаСервере1СПредприятия""";
			СтрокаБазы = СтрЗаменить(СтрокаБазы, "&ИмяСервера1СПредприятия",                     Параметры.ИмяСервера1СПредприятия);
			СтрокаБазы = СтрЗаменить(СтрокаБазы, "&ИмяИнформационнойБазыНаСервере1СПредприятия", Параметры.ИмяИнформационнойБазыНаСервере1СПредприятия);
		КонецЕсли;
		
		Если Параметры.АутентификацияОперационнойСистемы Тогда
			СтрокаАутентификации = "";
		Иначе
			
			Если СтрНайти(Параметры.ИмяПользователя, """") Тогда
				Параметры.ИмяПользователя = СтрЗаменить(Параметры.ИмяПользователя, """", """""");
			КонецЕсли;
			
			Если СтрНайти(Параметры.ПарольПользователя, """") Тогда
				Параметры.ПарольПользователя = СтрЗаменить(Параметры.ПарольПользователя, """", """""");
			КонецЕсли;
			
			СтрокаАутентификации = "; Usr = ""&ИмяПользователя""; Pwd = ""&ПарольПользователя""";
			СтрокаАутентификации = СтрЗаменить(СтрокаАутентификации, "&ИмяПользователя",    Параметры.ИмяПользователя);
			СтрокаАутентификации = СтрЗаменить(СтрокаАутентификации, "&ПарольПользователя", Параметры.ПарольПользователя);
		КонецЕсли;
		
		СтрокаСоединения = СтрЗаменить(ШаблонСтрокиСоединения, "[СтрокаБазы]", СтрокаБазы);
		СтрокаСоединения = СтрЗаменить(СтрокаСоединения, "[СтрокаАутентификации]", СтрокаАутентификации);
		
		Попытка
			Результат.Соединение = COMConnector.Connect(СтрокаСоединения);
		Исключение
			Информация = ИнформацияОбОшибке();
			СтрокаСообщенияОбОшибке = НСтр("ru = 'Не удалось подключиться к приложению: %1'");
			
			Результат.ОшибкаПодключенияКомпоненты = Истина;
			Результат.ПодробноеОписаниеОшибки     = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(СтрокаСообщенияОбОшибке, ОбработкаОшибок.ПодробноеПредставлениеОшибки(Информация));
			Результат.КраткоеОписаниеОшибки       = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(СтрокаСообщенияОбОшибке, ОбработкаОшибок.КраткоеПредставлениеОшибки(Информация));
		КонецПопытки;
#КонецЕсли
	
	Возврат Результат;
	
КонецФункции

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

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

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

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

#Если Не МобильныйКлиент Тогда
	СистемнаяИнформация = Новый СистемнаяИнформация;
	ЭтоLinuxКлиент = ЭтоLinuxКлиент
		Или СистемнаяИнформация.ТипПлатформы = ТипПлатформы["Linux_ARM64"]
		Или СистемнаяИнформация.ТипПлатформы = ТипПлатформы["Linux_E2K"];
#КонецЕсли
	
	Возврат ЭтоLinuxКлиент;
	
КонецФункции

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

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

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

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

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

// Устарела. Следует использовать ОбщегоНазначенияКлиент.КодОсновногоЯзыка или ОбщегоНазначения.КодОсновногоЯзыка
// Возвращает код основного языка конфигурации, например "ru".
//
// Возвращаемое значение:
//  Строка - код языка.
//
Функция КодОсновногоЯзыка() Экспорт

#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
		Возврат ОбщегоНазначения.КодОсновногоЯзыка();
#Иначе
		Возврат ОбщегоНазначенияКлиент.КодОсновногоЯзыка();
#КонецЕсли

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

// Устарела. Следует использовать ОбщегоНазначенияКлиент.ПредставлениеЛокальнойДатыСоСмещением 
//  или ОбщегоНазначения.ПредставлениеЛокальнойДатыСоСмещением
// Преобразует локальную дату к формату "YYYY-MM-DDThh:mm:ssTZD" согласно ISO 8601.
//
// Параметры:
//  ЛокальнаяДата - Дата - дата в часовом поясе сеанса.
// 
// Возвращаемое значение:
//   Строка - представление даты.
//
Функция ПредставлениеЛокальнойДатыСоСмещением(ЛокальнаяДата) Экспорт
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
		Смещение = СмещениеСтандартногоВремени(ЧасовойПоясСеанса());
#Иначе
		Смещение = СтандартныеПодсистемыКлиент.ПараметрКлиента("СмещениеСтандартногоВремени");
#КонецЕсли
	ПредставлениеСмещения = "Z";
	Если Смещение > 0 Тогда
		ПредставлениеСмещения = "+";
	ИначеЕсли Смещение < 0 Тогда
		ПредставлениеСмещения = "-";
		Смещение = -Смещение;
	КонецЕсли;
	Если Смещение <> 0 Тогда
		ПредставлениеСмещения = ПредставлениеСмещения + Формат('00010101' + Смещение, "ДФ=HH:mm");
	КонецЕсли;
	
	Возврат Формат(ЛокальнаяДата, "ДФ=yyyy-MM-ddTHH:mm:ss; ДП=0001-01-01T00:00:00") + ПредставлениеСмещения;
КонецФункции

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

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

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

// Устарела. Следует использовать ФайловаяСистемаКлиент.ПараметрыЗапускаПрограммы 
// или ФайловаяСистема.ПараметрыЗапускаПрограммы.
// Возвращает структуру параметров для процедуры ЗапуститьПрограмму.
//
// Возвращаемое значение:
//  Структура:
//   * ТекущийКаталог              - Строка - задает текущий каталог запускаемого приложения.
//   * ДождатьсяЗавершения         - Булево - дожидаться завершения запущенного приложения перед продолжением работы.
//   * ПолучитьПотокВывода         - Булево - результат, направленный в поток stdout,
//                                            если не указан ДождатьсяЗавершения - игнорируется.
//   * ПолучитьПотокОшибок         - Булево - ошибки, направленные в поток stderr,
//                                            если не указан ДождатьсяЗавершения - игнорируется.
//   * ВыполнитьСНаивысшимиПравами - Булево - требуется запустить программу с повышением привилегий системы:
//                                            подтверждение UAC для Windows;
//                                            или интерактивный запрос c GUI sudo и перенаправление
//                                            $DISPLAY и $XAUTHORITY текущего пользователя для Linux;
//                                            Не совместим с параметром ДождатьсяЗавершения. В MacOS игнорируется.
//   * Кодировка                   - Строка - код кодировки, устанавливаемый до выполнения пакетной операции.
//                                            В Linux и macOS игнорируется.
//
Функция ПараметрыЗапускаПрограммы() Экспорт
	
	Параметры = Новый Структура;
	Параметры.Вставить("ТекущийКаталог", "");
	Параметры.Вставить("ДождатьсяЗавершения", Ложь);
	Параметры.Вставить("ПолучитьПотокВывода", Ложь);
	Параметры.Вставить("ПолучитьПотокОшибок", Ложь);
	Параметры.Вставить("ВыполнитьСНаивысшимиПравами", Ложь);
	Параметры.Вставить("Кодировка", "");
	
	Возврат Параметры;
	
КонецФункции

// Устарела. Следует использовать ФайловаяСистемаКлиент.ЗапуститьПрограмму или ФайловаяСистема.ЗапуститьПрограмму
// Запускает внешнюю программу в соответствии с параметрами запуска.
// Не доступно в веб-клиенте. 
//
// Параметры:
//  КомандаЗапуска - Строка
//                 - Массив - командная строка для запуска программы.
//      Если Массив, то: первый элемент массива - путь к исполняемому приложению, остальные - передаваемые параметры,
//      массив соответствует тому, который получит вызываемая программа в argv.
//  ПараметрыЗапускаПрограммы - см. ПараметрыЗапускаПрограммы
//
// Возвращаемое значение:
//  Структура - результат работы программы:
//      КодВозврата - Число  - код возврата программы.
//      ПотокВывода - Строка - результат работы программы, направленный в поток stdout.
//      ПотокОшибок - Строка - ошибки исполнения программы, направленные в поток stderr.
//
// Пример:
//	ОбщегоНазначенияКлиентСервер.ЗапуститьПрограмму("calc");
//	
//	ПараметрыЗапускаПрограммы = ОбщегоНазначенияКлиентСервер.ПараметрыЗапускаПрограммы();
//	ПараметрыЗапускаПрограммы.ВыполнитьСНаивысшимиПравами = Истина;
//	ОбщегоНазначенияКлиентСервер.ЗапуститьПрограмму("C:\Program Files\1cv8\common\1cestart.exe", 
//		ПараметрыЗапускаПрограммы);
//	
//	ПараметрыЗапускаПрограммы = ОбщегоНазначенияКлиентСервер.ПараметрыЗапускаПрограммы();
//	ПараметрыЗапускаПрограммы.ДождатьсяЗавершения = Истина;
//	Результат = ОбщегоНазначенияКлиентСервер.ЗапуститьПрограмму("ping 127.0.0.1 -n 5", ПараметрыЗапускаПрограммы);
//
Функция ЗапуститьПрограмму(Знач КомандаЗапуска, ПараметрыЗапускаПрограммы = Неопределено) Экспорт 
	
#Если ВебКлиент ИЛИ МобильныйКлиент Тогда
	ВызватьИсключение НСтр("ru = 'Запуск приложения недоступен в веб-клиенте.'");
#Иначе
	
	СтрокаКоманды = ОбщегоНазначенияСлужебныйКлиентСервер.БезопаснаяСтрокаКоманды(КомандаЗапуска);
	
	Если ПараметрыЗапускаПрограммы = Неопределено Тогда 
		ПараметрыЗапускаПрограммы = ПараметрыЗапускаПрограммы();
	КонецЕсли;
	
	ТекущийКаталог              = ПараметрыЗапускаПрограммы.ТекущийКаталог;
	ДождатьсяЗавершения         = ПараметрыЗапускаПрограммы.ДождатьсяЗавершения;
	ПолучитьПотокВывода         = ПараметрыЗапускаПрограммы.ПолучитьПотокВывода;
	ПолучитьПотокОшибок         = ПараметрыЗапускаПрограммы.ПолучитьПотокОшибок;
	ВыполнитьСНаивысшимиПравами = ПараметрыЗапускаПрограммы.ВыполнитьСНаивысшимиПравами;
	Кодировка                   = ПараметрыЗапускаПрограммы.Кодировка;
	
	Если ВыполнитьСНаивысшимиПравами Тогда 
#Если ВнешнееСоединение Тогда
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр(
			"ru = 'Недопустимое значение параметра %1.
			|Повышение привилегий системы не доступно из внешнего соединения.'"),
			"ПараметрыЗапускаПрограммы.ВыполнитьСНаивысшимиПравами");
#КонецЕсли
		
#Если Сервер Тогда
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Недопустимое значение параметра %1.
			|Повышение привилегий системы не доступно на сервере.'"),
			"ПараметрыЗапускаПрограммы.ВыполнитьСНаивысшимиПравами");
#КонецЕсли
		
	КонецЕсли;
	
	СистемнаяИнформация = Новый СистемнаяИнформация();
	Если (СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Windows_x86) 
		Или (СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Windows_x86_64) Тогда
	
		Если Не ПустаяСтрока(Кодировка) Тогда
			СтрокаКоманды = "chcp " + Кодировка + " | " + СтрокаКоманды;
		КонецЕсли;
	
	КонецЕсли;
	
	Если ДождатьсяЗавершения Тогда 
		
		Если ПолучитьПотокВывода Тогда 
			ФайлПотокаВывода = ПолучитьИмяВременногоФайла("stdout.tmp");
			СтрокаКоманды = СтрокаКоманды + " > """ + ФайлПотокаВывода + """";
		КонецЕсли;
		
		Если ПолучитьПотокОшибок Тогда 
			ФайлПотокаОшибок = ПолучитьИмяВременногоФайла("stderr.tmp");
			СтрокаКоманды = СтрокаКоманды + " 2>""" + ФайлПотокаОшибок + """";
		КонецЕсли;
		
	КонецЕсли;
	
	КодВозврата = Неопределено;
	
	Если (СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Windows_x86)
		Или (СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Windows_x86_64) Тогда
		
		// Из-за запуска через shell перенаправление каталога требуется сделать командой.
		Если Не ПустаяСтрока(ТекущийКаталог) Тогда 
			СтрокаКоманды = "cd /D """ + ТекущийКаталог + """ && " + СтрокаКоманды;
		КонецЕсли;
		
		// Выполняется запуск через cmd.exe (для перенаправления stdout и stderr).
		СтрокаКоманды = "cmd /S /C "" " + СтрокаКоманды + " """;
		
#Если Сервер Тогда
		
		Если ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
			// В файловой информационной базе показывать окно консоли не следует и в серверном контексте.
			Оболочка = Новый COMОбъект("Wscript.Shell");
			КодВозврата = Оболочка.Run(СтрокаКоманды, 0, ДождатьсяЗавершения);
			Оболочка = Неопределено;
		Иначе 
			ЗапуститьПриложение(СтрокаКоманды,, ДождатьсяЗавершения, КодВозврата);
		КонецЕсли;
		
#Иначе
		
		Если ВыполнитьСНаивысшимиПравами Тогда
			
			Если ДождатьсяЗавершения Тогда
				ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр(
					"ru = 'Недопустимо одновременно устанавливать параметры 
					| - %1 и
					| - %2:
					|Операционная система не позволяет отслеживать от имени пользователя процессы,
					|запущенные администратором.'"),
					"ПараметрыЗапускаПрограммы.ДождатьсяЗавершения",
					"ПараметрыЗапускаПрограммы.ВыполнитьСНаивысшимиПравами");
			КонецЕсли;
			
			Оболочка = Новый COMОбъект("Shell.Application");
			// Запуск с передачей глагола действия - повышения привилегий.
			Оболочка.ShellExecute("cmd", "/c """ + СтрокаКоманды + """",, "runas", 0);
			Оболочка = Неопределено;
			
		Иначе 
			Оболочка = Новый COMОбъект("Wscript.Shell");
			КодВозврата = Оболочка.Run(СтрокаКоманды, 0, ДождатьсяЗавершения);
			Оболочка = Неопределено;
		КонецЕсли;
#КонецЕсли
		
	ИначеЕсли (СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Linux_x86) 
		Или (СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Linux_x86_64)
		Или СистемнаяИнформация.ТипПлатформы = ТипПлатформы["Linux_ARM64"]
			Или СистемнаяИнформация.ТипПлатформы = ТипПлатформы["Linux_E2K"] Тогда
		
		Если ВыполнитьСНаивысшимиПравами Тогда
			
			ШаблонКоманды = "pkexec env DISPLAY=[DISPLAY] XAUTHORITY=[XAUTHORITY] [СтрокаКоманды]";
			
			ПараметрыШаблона = Новый Структура;
			ПараметрыШаблона.Вставить("СтрокаКоманды", СтрокаКоманды);
			
			ПараметрыЗапускаПодпрограммы = ПараметрыЗапускаПрограммы();
			ПараметрыЗапускаПодпрограммы.ДождатьсяЗавершения = Истина;
			ПараметрыЗапускаПодпрограммы.ПолучитьПотокВывода = Истина;
			
			Результат = ЗапуститьПрограмму("echo $DISPLAY", ПараметрыЗапускаПодпрограммы);
			ПараметрыШаблона.Вставить("DISPLAY", Результат.ПотокВывода);
			
			Результат = ЗапуститьПрограмму("echo $XAUTHORITY", ПараметрыЗапускаПодпрограммы);
			ПараметрыШаблона.Вставить("XAUTHORITY", Результат.ПотокВывода);
			
			СтрокаКоманды = СтроковыеФункцииКлиентСервер.ВставитьПараметрыВСтроку(ШаблонКоманды, ПараметрыШаблона);
			ДождатьсяЗавершения = Истина;
			
		КонецЕсли;
		
		ЗапуститьПриложение(СтрокаКоманды, ТекущийКаталог, ДождатьсяЗавершения, КодВозврата);
		
	Иначе
		
		// Для MacOS просто запускаем команду.
		// Параметр ПараметрыЗапускаПрограммы.ВыполнитьСНаивысшимиПравами игнорируется.
		ЗапуститьПриложение(СтрокаКоманды, ТекущийКаталог, ДождатьсяЗавершения, КодВозврата);
		
	КонецЕсли;
	
	// Переопределение возвращенного оболочной значения.
	Если КодВозврата = Неопределено Тогда 
		КодВозврата = 0;
	КонецЕсли;
	
	ПотокВывода = "";
	ПотокОшибок = "";
	
	Если ДождатьсяЗавершения Тогда 
		
		Если ПолучитьПотокВывода Тогда
			
			ФайлИнфо = Новый Файл(ФайлПотокаВывода);
			Если ФайлИнфо.Существует() Тогда 
				ЧтениеПотокаВывода = Новый ЧтениеТекста(ФайлПотокаВывода, КодировкаСтандартныхПотоков()); 
				ПотокВывода = ЧтениеПотокаВывода.Прочитать();
				ЧтениеПотокаВывода.Закрыть();
				УдалитьВременныйФайл(ФайлПотокаВывода);
			КонецЕсли;
			
			Если ПотокВывода = Неопределено Тогда 
				ПотокВывода = "";
			КонецЕсли;
			
		КонецЕсли;
		
		Если ПолучитьПотокОшибок Тогда 
			
			ФайлИнфо = Новый Файл(ФайлПотокаОшибок);
			Если ФайлИнфо.Существует() Тогда 
				ЧтениеПотокаОшибок = Новый ЧтениеТекста(ФайлПотокаОшибок, КодировкаСтандартныхПотоков());
				ПотокОшибок = ЧтениеПотокаОшибок.Прочитать();
				ЧтениеПотокаОшибок.Закрыть();
				УдалитьВременныйФайл(ФайлПотокаОшибок);
			КонецЕсли;
			
			Если ПотокОшибок = Неопределено Тогда 
				ПотокОшибок = "";
			КонецЕсли;
			
		КонецЕсли;
		
	КонецЕсли;
	
	Результат = Новый Структура;
	Результат.Вставить("КодВозврата", КодВозврата);
	Результат.Вставить("ПотокВывода", ПотокВывода);
	Результат.Вставить("ПотокОшибок", ПотокОшибок);
	
	Возврат Результат;
	
#КонецЕсли
	
КонецФункции

// Устарела. Следует использовать ПолучениеФайловИзИнтернета.ДиагностикаСоединения
// Запускает диагностику сетевого ресурса.
// Не доступно в веб-клиенте.
// В модели сервиса функциональность ограничена получением описания ошибки.
//
// Параметры:
//  URL - Строка - адрес URL ресурса, диагностику которого надо выполнить.
//
// Возвращаемое значение:
//  Структура - результат работы программы:
//      ОписаниеОшибки    - Строка - краткое описание ошибки.
//      ЖурналДиагностики - Строка - подробный журнал диагностики с техническими подробностями.
//
// Пример:
//	// Диагностика веб-сервиса адресного классификатора.
//	Результат = ОбщегоНазначенияКлиентСервер.ДиагностикаСоединения("https://api.orgaddress.1c.com/orgaddress/v1?wsdl");
//	
//	ОписаниеОшибки    = Результат.ОписаниеОшибки;
//	ЖурналДиагностики = Результат.ЖурналДиагностики;
//
Функция ДиагностикаСоединения(URL) Экспорт
	
#Если ВебКлиент Тогда
	ВызватьИсключение НСтр("ru = 'Выполнение диагностики соединения недоступно в веб-клиенте.'");
#Иначе
	
	Описание = Новый Массив;
	Описание.Добавить(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'При обращении по URL: %1'"), 
		URL));
	Описание.Добавить(ПредставлениеМестаДиагностики());
	
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		Описание.Добавить(
			НСтр("ru = 'Обратитесь к администратору.'"));
		
		ОписаниеОшибки = СтрСоединить(Описание, Символы.ПС);
		
		Результат = Новый Структура;
		Результат.Вставить("ОписаниеОшибки", ОписаниеОшибки);
		Результат.Вставить("ЖурналДиагностики", "");
		
		Возврат Результат;
	КонецЕсли;
#КонецЕсли
	
	Журнал = Новый Массив;
	Журнал.Добавить(
		НСтр("ru = 'Журнал диагностики:
		           |Выполняется проверка доступности сервера.
		           |Описание диагностируемой ошибки см. в следующем сообщении журнала.'"));
	Журнал.Добавить();
	
	СоединениеЧерезПрокси = Ложь;
	
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ПолучениеФайловИзИнтернета") Тогда
		МодульПолучениеФайловИзИнтернетаКлиентСервер = 
			ОбщегоНазначения.ОбщийМодуль("ПолучениеФайловИзИнтернетаКлиентСервер");
		СостояниеНастроекПрокси = МодульПолучениеФайловИзИнтернетаКлиентСервер.СостояниеНастроекПрокси();
		
		СоединениеЧерезПрокси = СостояниеНастроекПрокси.СоединениеЧерезПрокси;
		
		Журнал.Добавить(СостояниеНастроекПрокси.Представление);
	КонецЕсли;
#Иначе
	Если ОбщегоНазначенияКлиент.ПодсистемаСуществует("СтандартныеПодсистемы.ПолучениеФайловИзИнтернета") Тогда
		МодульПолучениеФайловИзИнтернетаКлиентСервер = 
			ОбщегоНазначенияКлиент.ОбщийМодуль("ПолучениеФайловИзИнтернетаКлиентСервер");
		СостояниеНастроекПрокси = МодульПолучениеФайловИзИнтернетаКлиентСервер.СостояниеНастроекПрокси();
		
		СоединениеЧерезПрокси = СостояниеНастроекПрокси.СоединениеЧерезПрокси;
		
		Журнал.Добавить(СостояниеНастроекПрокси.Представление);
	КонецЕсли;
#КонецЕсли
	
	Если СоединениеЧерезПрокси Тогда 
		
		Описание.Добавить(
			НСтр("ru = 'Диагностика соединения не выполнена, т.к. настроен прокси-сервер.
			           |Обратитесь к администратору.'"));
		
	Иначе 
		
		СтруктураСсылки = СтруктураURI(URL);
		АдресСервераРесурса = СтруктураСсылки.Хост;
		АдресСервераКонтрольнойПроверки = "google.com";
		
		РезультатДоступностиРесурса = ПроверитьДоступностьСервера(АдресСервераРесурса);
		
		Журнал.Добавить();
		Журнал.Добавить("1) " + РезультатДоступностиРесурса.ЖурналДиагностики);
		
		Если РезультатДоступностиРесурса.Доступен Тогда 
			
			Описание.Добавить(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Выполнено обращение к несуществующему ресурсу на сервере %1
				           |или возникли неполадки на удаленном сервере.'"),
				АдресСервераРесурса));
			
		Иначе 
			
			РезультатКонтрольнойПроверки = ПроверитьДоступностьСервера(АдресСервераКонтрольнойПроверки);
			Журнал.Добавить("2) " + РезультатКонтрольнойПроверки.ЖурналДиагностики);
			
			Если Не РезультатКонтрольнойПроверки.Доступен Тогда
				
				Описание.Добавить(
					НСтр("ru = 'Отсутствует доступ в сеть интернет по причине:
					           |- компьютер не подключен к интернету;
					           |- неполадки у интернет-провайдера;
					           |- подключение к интернету блокирует межсетевой экран, 
					           |  антивирус или другое программное обеспечение.'"));
				
			Иначе 
				
				Описание.Добавить(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Сервер %1 недоступен по причине:
					           |- неполадки у интернет-провайдера;
					           |- подключение к серверу блокирует межсетевой экран, 
					           |  антивирус или другое программное обеспечение;
					           |- сервер отключен или на техническом обслуживании.'"),
					АдресСервераРесурса));
				
				ЖурналТрассировки = ЖурналТрассировкиМаршрутаСервера(АдресСервераРесурса);
				Журнал.Добавить("3) " + ЖурналТрассировки);
				
			КонецЕсли;
			
		КонецЕсли;
		
	КонецЕсли;
	
	ОписаниеОшибки = СтрСоединить(Описание, Символы.ПС);
	
	Журнал.Вставить(0);
	Журнал.Вставить(0, ОписаниеОшибки);
	
	ЖурналДиагностики = СтрСоединить(Журнал, Символы.ПС);
	
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
	ЗаписьЖурналаРегистрации(
		НСтр("ru = 'Диагностика соединения'", КодОсновногоЯзыка()),
		УровеньЖурналаРегистрации.Ошибка,,, ЖурналДиагностики);
#Иначе
	ЖурналРегистрацииКлиент.ДобавитьСообщениеДляЖурналаРегистрации(
		НСтр("ru = 'Диагностика соединения'", КодОсновногоЯзыка()),
		"Ошибка", ЖурналДиагностики,, Истина);
#КонецЕсли
	
	Результат = Новый Структура;
	Результат.Вставить("ОписаниеОшибки", ОписаниеОшибки);
	Результат.Вставить("ЖурналДиагностики", ЖурналДиагностики);
	
	Возврат Результат;
	
#КонецЕсли
	
КонецФункции

// АПК:547-вкл

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

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

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

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

Функция ИмяСоответствуетТребованиямИменованияСвойств(Имя) Экспорт
	Буквы = "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯABCDEFGHIJKLMNOPQRSTUVWXYZ"; // @Non-NLS, АПК:163 В данных буква допустима
	Цифры = "1234567890"; // @Non-NLS
	
	Если Имя = "" Или СтрНайти(Буквы + "_", ВРег(Лев(Имя, 1))) = 0 Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Возврат СтрРазделить(ВРег(Имя), Буквы + Цифры + "_", Ложь).Количество() = 0;
КонецФункции

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

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

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

#Область СпискиЗначенийИдентичны

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

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

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

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

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

Функция ПредставлениеТипа(Тип)
	
	Если Тип = Неопределено Тогда
		
		Возврат "Неопределено";
		
	ИначеЕсли ТипЗнч(Тип) = Тип("ОписаниеТипов") Тогда
		
		ТипСтрокой = Строка(Тип);
		Возврат 
			?(СтрДлина(ТипСтрокой) > 150, 
				СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = '%1,... (всего %2 типов)'"),
					Лев(ТипСтрокой, 150),
					Тип.Типы().Количество()), 
				ТипСтрокой);
		
	Иначе
		
		ТипСтрокой = Строка(Тип);
		Возврат 
			?(СтрДлина(ТипСтрокой) > 150, 
				Лев(ТипСтрокой, 150) + "...", 
				ТипСтрокой);
		
	КонецЕсли;
	
КонецФункции

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

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

#Область РаботаСАдресамиЭлектроннойПочты

#Область АдресЭлектроннойПочтыСоответствуетТребованиям

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

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

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

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

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

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

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

#Область Математика

#Область РаспределитьСуммуПропорциональноКоэффициентам

Функция МаксимальноеЗначениеВМассиве(Массив)
	
	МаксимальноеЗначение = 0;
	
	Для Индекс = 0 По Массив.Количество() - 1 Цикл
		Значение = Массив[Индекс];
		
		Если МаксимальноеЗначение < Значение Тогда
			МаксимальноеЗначение = Значение;
		КонецЕсли;
	КонецЦикла;
	
	Возврат МаксимальноеЗначение;
	
КонецФункции

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

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

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

// АПК:223-выкл код сохранен для обратной совместимости, используется в устаревшем программном интерфейсе.

#Область ЗапуститьПрограмму

#Если Не ВебКлиент Тогда

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

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

#КонецЕсли

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

#Область ДиагностикаСоединения

#Если Не ВебКлиент Тогда

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

Функция ПроверитьДоступностьСервера(АдресСервера)
	
	ПараметрыЗапускаПрограммы = ПараметрыЗапускаПрограммы();
	ПараметрыЗапускаПрограммы.ДождатьсяЗавершения = Истина;
	ПараметрыЗапускаПрограммы.ПолучитьПотокВывода = Истина;
	ПараметрыЗапускаПрограммы.ПолучитьПотокОшибок = Истина;
	
	СистемнаяИнформация = Новый СистемнаяИнформация();
	ЭтоWindows = (СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Windows_x86) 
		Или (СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Windows_x86_64);
		
	Если ЭтоWindows Тогда
		ШаблонКоманды = "ping %1 -n 2 -w 500";
	Иначе
		ШаблонКоманды = "ping -c 2 -w 500 %1";
	КонецЕсли;
	
	СтрокаКоманды = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонКоманды, АдресСервера);
	
	Результат = ЗапуститьПрограмму(СтрокаКоманды, ПараметрыЗапускаПрограммы);
	
	// Разные операционные системы могут выводить ошибки в разные потоки:
	// - для Windows все всегда в потоке вывода;
	// - для Debian или RHEL ошибки падают в поток ошибок.
	ЖурналДоступности = Результат.ПотокВывода + Результат.ПотокОшибок;
	
	// АПК:1297-выкл нелокализуемый фрагмент оставленный для обратной совместимости
	
	Если ЭтоWindows Тогда
		ФактНедоступности = (СтрНайти(ЖурналДоступности, "Заданный узел недоступен") > 0) // не локализуется.
			Или (СтрНайти(ЖурналДоступности, "Destination host unreachable") > 0); // не локализуется.
		
		БезПотерь = (СтрНайти(ЖурналДоступности, "(0% потерь)") > 0) // не локализуется.
			Или (СтрНайти(ЖурналДоступности, "(0% loss)") > 0); // не локализуется.
	Иначе 
		ФактНедоступности = (СтрНайти(ЖурналДоступности, "Destination Host Unreachable") > 0); // не локализуется.
		БезПотерь = (СтрНайти(ЖурналДоступности, "0% packet loss") > 0) // не локализуется.
	КонецЕсли;
	
	// АПК:1297-вкл
	
	Доступен = Не ФактНедоступности И БезПотерь;
	
	Журнал = Новый Массив;
	Если Доступен Тогда
		Журнал.Добавить(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Удаленный сервер %1 доступен:'"), 
			АдресСервера));
	Иначе
		Журнал.Добавить(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Удаленный сервер %1 недоступен:'"), 
			АдресСервера));
	КонецЕсли;
	
	Журнал.Добавить("> " + СтрокаКоманды);
	Журнал.Добавить(ЖурналДоступности);
	
	Возврат Новый Структура("Доступен, ЖурналДиагностики", Доступен, СтрСоединить(Журнал, Символы.ПС));
	
КонецФункции

Функция ЖурналТрассировкиМаршрутаСервера(АдресСервера)
	
	ПараметрыЗапускаПрограммы = ПараметрыЗапускаПрограммы();
	ПараметрыЗапускаПрограммы.ДождатьсяЗавершения = Истина;
	ПараметрыЗапускаПрограммы.ПолучитьПотокВывода = Истина;
	ПараметрыЗапускаПрограммы.ПолучитьПотокОшибок = Истина;
	
	СистемнаяИнформация = Новый СистемнаяИнформация();
	ЭтоWindows = (СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Windows_x86) 
		Или (СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Windows_x86_64);
	
	Если ЭтоWindows Тогда
		ШаблонКоманды = "tracert -w 100 -h 15 %1";
	Иначе 
		// Если вдруг пакет traceroute не установлен - в потоке вывода будет ошибка.
		// Т.к. результат все равно не разбирается, на поток вывода можно не обращать внимания.
		// По нему администратор поймет что ему надо доставить.
		ШаблонКоманды = "traceroute -w 100 -m 100 %1";
	КонецЕсли;
	
	СтрокаКоманды = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонКоманды, АдресСервера);
	
	Результат = ЗапуститьПрограмму(СтрокаКоманды, ПараметрыЗапускаПрограммы);
	
	Журнал = Новый Массив;
	Журнал.Добавить(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Трассировка маршрута к удаленному серверу %1:'"), АдресСервера));
	
	Журнал.Добавить("> " + СтрокаКоманды);
	Журнал.Добавить(Результат.ПотокВывода);
	Журнал.Добавить(Результат.ПотокОшибок);
	
	Возврат СтрСоединить(Журнал, Символы.ПС);
	
КонецФункции

#КонецЕсли

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

// АПК:223-вкл

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

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