Процедура ПолучитьДанныеФайлаИОсвободитьФайл(ФайлСсылка, ДанныеФайла, УникальныйИдентификатор = Неопределено) Экспорт

	ДанныеФайла = ДанныеФайла(ФайлСсылка);
	ОсвободитьФайл(ДанныеФайла, УникальныйИдентификатор);

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

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

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

	ПараметрыДанныхФайла = РаботаСФайламиКлиентСервер.ПараметрыДанныхФайла();
	ПараметрыДанныхФайла.ПолучатьСсылкуНаДвоичныеДанные = Ложь;

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

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

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

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

Функция РабочийКаталогПапки(СсылкаПапки) Экспорт

	Если Не ЭтоСправочникФайлы(СсылкаПапки) Тогда
		Возврат "";
	КонецЕсли;

	УстановитьПривилегированныйРежим(Истина);

	РабочийКаталог = "";

	// Подготовить структуру отбора по измерениям.

	СтруктураОтбора = Новый Структура;
	СтруктураОтбора.Вставить("Папка", СсылкаПапки);
	СтруктураОтбора.Вставить("Пользователь", Пользователи.АвторизованныйПользователь());

	// Получить структуру с данными ресурсов записи.
	СтруктураРесурсов = РегистрыСведений.РабочиеКаталогиФайлов.Получить(СтруктураОтбора);

	// Получить путь из регистра
	РабочийКаталог = СтруктураРесурсов.Путь;

	Если Не ПустаяСтрока(РабочийКаталог) Тогда
		// Добавляем слэш в конце, если его нет.
		РабочийКаталог = ОбщегоНазначенияКлиентСервер.ДобавитьКонечныйРазделительПути(РабочийКаталог);
	КонецЕсли;

	Возврат РабочийКаталог;

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

// Возвращаемое значение:
//   Структура:
//     * Файл                         - ОпределяемыйТип.ПрисоединенныйФайл
//     * Путь                         - Строка
//     * Размер                       - Число
//     * ДатаПомещенияВРабочийКаталог - Дата
//     * НаЧтение                     - Булево
//

Функция ФайлВРабочемКаталоге()

	Возврат Новый Структура("Файл, Путь, Размер, ДатаПомещенияВРабочийКаталог, НаЧтение");

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

// Сохраняет в регистре сведений рабочий каталог папки и
// заменяет пути в регистре сведений ФайлыВРабочемКаталоге.
//
// Параметры:
//  СсылкаПапки  - СправочникСсылка.ПапкиФайлов - владелец файла.
//  РабочийКаталогПапки - Строка - рабочий каталог папки.
//  ИмяКаталогаПрежнееЗначение - Строка - прежнее значение рабочего каталога.
//

Процедура СохранитьРабочийКаталогПапкиИЗаменитьПутиВРегистре(СсылкаПапки, РабочийКаталогПапки,
	ИмяКаталогаПрежнееЗначение) Экспорт

	РаботаСФайламиСлужебный.СохранитьРабочийКаталогПапки(СсылкаПапки, РабочийКаталогПапки);

	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ФайлыВРабочемКаталоге.Файл КАК Файл,
	|	ФайлыВРабочемКаталоге.Путь КАК Путь,
	|	ФайлыВРабочемКаталоге.Размер КАК Размер,
	|	ФайлыВРабочемКаталоге.ДатаПомещенияВРабочийКаталог КАК ДатаПомещенияВРабочийКаталог,
	|	ФайлыВРабочемКаталоге.НаЧтение КАК НаЧтение
	|ИЗ
	|	РегистрСведений.ФайлыВРабочемКаталоге КАК ФайлыВРабочемКаталоге
	|ГДЕ
	|	ФайлыВРабочемКаталоге.Пользователь = &Пользователь
	|	И ФайлыВРабочемКаталоге.ВРабочемКаталогеВладельца = ИСТИНА
	|	И ФайлыВРабочемКаталоге.Путь ПОДОБНО &Путь СПЕЦСИМВОЛ ""~""";

	Запрос.УстановитьПараметр("Пользователь", Пользователи.АвторизованныйПользователь());
	Запрос.УстановитьПараметр("Путь", ОбщегоНазначения.СформироватьСтрокуДляПоискаВЗапросе(ИмяКаталогаПрежнееЗначение)
		+ "%");
	УстановитьПривилегированныйРежим(Истина);
	РезультатЗапроса = Запрос.Выполнить();

	СписокДляЗамены = Новый Массив;

	Выборка = РезультатЗапроса.Выбрать();
	Пока Выборка.Следующий() Цикл

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

		ФайлВРабочемКаталоге = ФайлВРабочемКаталоге();
		ФайлВРабочемКаталоге.Файл = Выборка.Файл;
		ФайлВРабочемКаталоге.Путь = НовыйПуть;
		ФайлВРабочемКаталоге.Размер = Выборка.Размер;
		ФайлВРабочемКаталоге.ДатаПомещенияВРабочийКаталог = Выборка.ДатаПомещенияВРабочийКаталог;
		ФайлВРабочемКаталоге.НаЧтение = Выборка.НаЧтение;

		СписокДляЗамены.Добавить(ФайлВРабочемКаталоге);

	КонецЦикла;

	Для Каждого ЭлементСписка Из СписокДляЗамены Цикл

		ВРабочемКаталогеВладельца = Истина;

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

	КонецЦикла;

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

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


Функция РабочийКаталогПользователя() Экспорт

	Возврат РаботаСФайламиСлужебныйКлиентПовтИсп.РабочийКаталогПользователя();

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

// Открывает форму папки со списком файлов.
//
// Параметры:
//   СтандартнаяОбработка - Булево - передается "как есть" из параметров обработчика "Открытие".
//   Папка - СправочникСсылка.Файлы - открываемая папка.
//

Процедура РассылкаОтчетовПросмотрПапки(СтандартнаяОбработка, Папка) Экспорт

	СтандартнаяОбработка = Ложь;
	ПараметрыФормы = Новый Структура("Папка", Папка);
	ОткрытьФорму("Справочник.Файлы.Форма.Файлы", ПараметрыФормы, , Папка);

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

Процедура ПеренестиФайлы() Экспорт

	ОткрытьФорму("Обработка.ПереносФайлов.Форма");

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

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

Процедура ДобавитьИзФайловойСистемыСРасширением(ПараметрыВыполнения) Экспорт

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

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

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

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


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

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

	ВыполнитьОбработкуОповещения(ДополнительныеПараметры.ОбработчикРезультата, Результат);

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

Функция ГотовностьКСканированию(Форма, ПодключаемыйМодуль) Экспорт
	ЕстьУстройства = ЕстьУстройства(Форма, ПодключаемыйМодуль);

	Если ЕстьУстройства Тогда
		Попытка
			ПодключенныеУстройства(Форма, ПодключаемыйМодуль);
			Возврат Истина;
		Исключение
			Возврат Ложь;
		КонецПопытки;
	КонецЕсли;
	Возврат Ложь;
КонецФункции

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

// Возвращает настройку сканера по имени.
//
// Параметры:
//   ПодключаемыйМодуль - компонента сканирования
//   ИмяУстройства - Строка - имя сканера.
//   ИмяНастройки  - Строка - имя настройки,
//       например "XRESOLUTION", или "PIXELTYPE", или "ROTATION", или "SUPPORTEDSIZES".
//
// Возвращаемое значение:
//   Число - значение настройки сканера.
//

Функция НастройкаСканера(Форма, ПодключаемыйМодуль, ИмяУстройства, ИмяНастройки) Экспорт

	Если Не ДоступноСканирование(Истина) Тогда
		Возврат -1;
	КонецЕсли;

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

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

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

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

	Возврат ЕстьУстройства;

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

Процедура НачатьСканирование(Форма, ПодключаемыйМодуль, ПараметрыСканирования) Экспорт

	ПараметрСжатие = ?(ВРег(ФорматКартинки) = "JPG", ПараметрыСканирования.КачествоJPG, ПараметрыСканирования.СжатиеTIFF);
	ТекстСобытия = "НачатьСканирование" + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("(%1, %2, %3, %4, %5, %6, %7, %8, %9)",
		ПараметрыСканирования.ПоказыватьДиалог,
		ПараметрыСканирования.ВыбранноеУстройство,
		ПараметрыСканирования.ФорматКартинки,
		ПараметрыСканирования.Разрешение,
		ПараметрыСканирования.Цветность,
		ПараметрыСканирования.Поворот,
		ПараметрыСканирования.РазмерБумаги,
		ПараметрСжатие,
		ПараметрыСканирования.ДвустороннееСканирование);
	ЗаписатьЖурналСканирования("НачатьСканирование.Старт", ТекстСобытия);

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

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


Функция ДополнительныеПараметрыОтправки() Экспорт

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

	Возврат Результат;

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

// Параметры:
//  ИмяОповещения  - см. ОтправитьСерверноеОповещение.ИмяОповещения
//  Результат      - см. ОтправитьСерверноеОповещение.Результат
//  Адресаты       - см. ОтправитьСерверноеОповещение.Адресаты
//  ОтправитьСразу - см. ОтправитьСерверноеОповещение.ОтправитьСразу
//
//  ДополнительныеПараметры - см. ДополнительныеПараметрыОтправки
//

Процедура ОтправитьСерверноеОповещениеСИдентификаторомГруппы(ИмяОповещения, Результат, Адресаты,
			ОтправитьСразу, ДополнительныеПараметрыОтправки = Неопределено) Экспорт

	Если Адресаты <> Неопределено И Не ЗначениеЗаполнено(Адресаты) Тогда
		Возврат;
	КонецЕсли;

	СодержимоеОповещения = НовоеСодержимоеОповещения();
	СодержимоеОповещения.ИмяОповещения = ИмяОповещения;
	СодержимоеОповещения.Результат     = Результат;
	СодержимоеОповещения.Адресаты      = Адресаты;

	Если ДлительныеОперации.ПропуститьОповещение(СодержимоеОповещения) Тогда
		Возврат;
	КонецЕсли;

	Если ОтправитьСразу
	   И СерверныеОповещенияСлужебныйПовтИсп.ЭтоСеансОтправкиСерверныхОповещенийКлиентам() Тогда

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

	ИдентификаторОповещения = НРег(Новый УникальныйИдентификатор);
	ДатаДобавления = ТекущаяДатаСеанса();
	ДатаДобавленияМиллисекунды = Миллисекунды();
	ДополнительныеПараметры = ?(ДополнительныеПараметрыОтправки = Неопределено,
		ДополнительныеПараметрыОтправки(), ДополнительныеПараметрыОтправки);

	ОтсрочкаДоставки = ДополнительныеПараметры.ОтсрочкаДоставки;

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

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

	НаборЗаписей = СлужебныйНаборЗаписей(РегистрыСведений.ОтправленныеСерверныеОповещения);
	НаборЗаписей.Отбор.ИдентификаторОповещения.Установить(ИдентификаторОповещения);
	НоваяЗапись = НаборЗаписей.Добавить();
	НоваяЗапись.ИдентификаторОповещения = ИдентификаторОповещения;
	НоваяЗапись.ДатаДобавления = ДатаДобавления;
	НоваяЗапись.ДатаДобавленияМиллисекунды = ДатаДобавленияМиллисекунды;
	НоваяЗапись.Адресаты = ИдентификаторыАдресатов;
	НоваяЗапись.СодержимоеОповещения = Новый ХранилищеЗначения(СодержимоеОповещения);
	НоваяЗапись.ИдентификаторГруппы = НРег(ДополнительныеПараметры.ИдентификаторГруппы);
	НоваяЗапись.ВидОповещенияВГруппе = НРег(ДополнительныеПараметры.ВидОповещенияВГруппе);

	Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		НаборЗаписей.Отбор.ОбластьДанныхВспомогательныеДанные.Установить(0);
		НоваяЗапись.ОбластьДанныхВспомогательныеДанные = 0;
	КонецЕсли;

	ЗапуститьДоставкуСОтсрочкой = Ложь;

	Если ЗначениеЗаполнено(ОтсрочкаДоставки) Тогда
		ЗапуститьДоставкуСОтсрочкой = Истина;

		Если ОтсрочкаДоставки > 5 Тогда
			НоваяЗапись.ОтсрочкаЗаписиВСистемуВзаимодействия = 5;
		Иначе
			НоваяЗапись.ОтсрочкаЗаписиВСистемуВзаимодействия = ОтсрочкаДоставки;
		КонецЕсли;

	ИначеЕсли ОтправитьСразу
	        И ТекущийПользовательЗарегистрированВСистемеВзаимодействия() Тогда

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

	ИначеЕсли ОтправитьСразу Тогда
		НоваяЗапись.ОтсрочкаЗаписиВСистемуВзаимодействия = 0.1;
	КонецЕсли;

	Если ЗначениеЗаполнено(НоваяЗапись.ОтсрочкаЗаписиВСистемуВзаимодействия)
	   И Не СистемаВзаимодействийПодключена() Тогда

		НоваяЗапись.ОтсрочкаЗаписиВСистемуВзаимодействия = 0;
		ЗапуститьДоставкуСОтсрочкой = Ложь;
	КонецЕсли;

	НаборЗаписей.Записать();

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

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

// Для процедуры ОтправитьСерверноеОповещениеСИдентификаторомГруппы.

Процедура УдалитьПоследнееНедоставленноеОповещение(ИдентификаторГруппы, ВидОповещенияВГруппе,
			ОтсрочкаДоставки, ДатаДобавления, ДатаДобавленияМиллисекунды)

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

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

	Если ЗначениеЗаполнено(Выборка.ДатаЗаписиВСистемуВзаимодействия) Тогда
		Прошло = ДатаДобавления - Выборка.ДатаЗаписиВСистемуВзаимодействия
			+ (ДатаДобавленияМиллисекунды - Выборка.ДатаЗаписиВСистемуВзаимодействияМиллисекунды) / 1000;
	Иначе
		Прошло = ДатаДобавления - Выборка.ДатаДобавления
			+ (ДатаДобавленияМиллисекунды - Выборка.ДатаДобавленияМиллисекунды) / 1000;
	КонецЕсли;
	Прошло = Окр(Прошло, 1, РежимОкругления.Окр15как20);
	ОтсрочкаДоставки = ОтсрочкаДоставки - Прошло;

	Если ОтсрочкаДоставки < 0 Тогда
		ОтсрочкаДоставки = 0;
	КонецЕсли;

	Если ЗначениеЗаполнено(Выборка.ДатаЗаписиВСистемуВзаимодействия) Тогда
		Возврат;
	КонецЕсли;

	НаборЗаписей = СлужебныйНаборЗаписей(РегистрыСведений.ОтправленныеСерверныеОповещения);
	НаборЗаписей.Отбор.ИдентификаторОповещения.Установить(Выборка.ИдентификаторОповещения);
	НаборЗаписей.Записать();

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

// Для процедуры ОтправитьСерверноеОповещениеСИдентификаторомГруппы.


Функция СписокПервичнойАвторизации(СервисПодписи) Экспорт

	Результат = Новый Массив;

	ПолныйСписок = СервисКриптографииDSSСлужебный.ДоступныеСпособыПервичнойАвторизации();
	СписокОграничений = СервисКриптографииDSSСлужебный.СпособыАвторизацииСОграничениями(СервисПодписи);

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

	Если Результат.Количество() = 0 Тогда
		Результат = ПолныйСписок;
	КонецЕсли;

	СервисКриптографииDSSПереопределяемый.СписокПервичнойАвторизации(Результат);

	Возврат Результат;

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

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

Функция СписокВторичнойАвторизации(СервисПодписи) Экспорт

	Результат = Новый Массив;

	ПолныйСписок = СервисКриптографииDSSСлужебный.ДоступныеСпособыВторичнойАвторизации();
	СписокОграничений = СервисКриптографииDSSСлужебный.СпособыАвторизацииСОграничениями(СервисПодписи);

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

	Если Результат.Количество() = 0 Тогда
		Результат = ПолныйСписок;
	КонецЕсли;

	СервисКриптографииDSSПереопределяемый.СписокВторичнойАвторизации(Результат);

	Возврат Результат;

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

// Формирует структуру с информацией о доступных указанной учетной записи УЦ, Криптопровайдерах.
// Шаблонах и Полях запроса на основе данных политики авторизованного пользователя сервиса.
// Данная информация необходима при формировании запроса на сертификат отправляемый на сервер.
//
// Параметры:
//  ИдентификаторПользователя	- СправочникСсылка.УчетныеЗаписиDSS,
//                           	- Структура
//  ПараметрыОперации			- Структура, ФиксированнаяСтруктура - позволяет указать дополнительные параметры операции.
//
// Возвращаемое значение:
//  Структура:
//    * Заполнено 			- Булево - если данные актуальны для данной учетной, т.е. была аутентификация
//      * Криптопровайдеры 		- Массив из Структура:
//      ** Идентификатор 	- Строка - идентификатор криптопровайдера зарегистрированный на сервисе
//      ** Описание 		- Строка - пользовательское представление
//    * УдостоверяющиеЦентры - Массив из Структура:
//      ** Имя 				- Строка - представление УЦ
//      ** Идентификатор 	- Строка - идентификатор который передается в сервис криптографии DSS
//      ** Сторонний		- Булево - если сторонний, то потребует отдельной операции установки сертификата
//      ** Шаблоны 			- см. ПолучитьРеестрШаблоновУдостоверяющегоЦентра
//      ** ИменаПолей 		- см. ПолучитьРеестрПолейУдостоверяющегоЦентра
//

Функция ПолучитьИнформациюУдостоверяющихЦентров(ИдентификаторПользователя, ПараметрыОперации = Неопределено) Экспорт

	НастройкиПользователя	= ПолучитьНастройкиПользователя(ИдентификаторПользователя, ПараметрыОперации);

	Результат = Новый Структура();

	Результат.Вставить("УдостоверяющиеЦентры", СервисКриптографииDSSСлужебный.СкопироватьСтруктуру(НастройкиПользователя.Политика.УЦ));
	Результат.Вставить("КриптоПровайдеры", СервисКриптографииDSSСлужебный.СкопироватьСтруктуру(НастройкиПользователя.Политика.Криптопровайдеры));
	Результат.Вставить("Заполнено", НастройкиПользователя.Политика.Заполнено);

	Возврат Результат;

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

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

Функция ПолучитьРеестрПолейУдостоверяющегоЦентра(УдостоверяющийЦентр) Экспорт

	Результат	= Новый Массив;
	ВсеПоля		= СервисКриптографииDSSASNКлиентСервер.ОпределитьТип_OID(Неопределено, "10");

	Для каждого СтрокаМассива Из УдостоверяющийЦентр.ИменаПолей Цикл
		НашлиЭлемент = ВсеПоля[СтрокаМассива["OID"]];

		Если НашлиЭлемент <> Неопределено Тогда
			НашлиЭлемент.Обязательно = СтрокаМассива["IsRequired"];
			Результат.Добавить(НашлиЭлемент);
		КонецЕсли;

	КонецЦикла;

	Возврат Результат;

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

// Формирует массив шаблонов допустимые в этом УЦ
//
// Параметры:
//  УдостоверяющийЦентр	- Структура:
//    * Шаблоны			- Массив из Структура:
//      ** Шаблон		- Строка
//      ** Наименование	- Строка
//
// Возвращаемое значение:
//  Массив из Структура:
//    * Шаблон 				- Строка - значение шаблона,
//    * Наименование 		- Строка - пользовательское представление
//

Функция ПолучитьРеестрШаблоновУдостоверяющегоЦентра(УдостоверяющийЦентр) Экспорт

	Результат	= Новый Массив;

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

	Возврат Результат;

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

// Служит для установки текущего пользователя для последующей работы с сервисом.
// Предусмотрена для случая, когда нужно указать конкретного пользователя, при использовании стандартного АПИ БСП,
// в котором данный параметры не предусмотрен.
//
// Параметры:
//  СсылкаУчетнойЗаписи - СправочникСсылка.УчетныеЗаписиDSS - идентифицирующий признак в сеансовых данных
//  ИсточникНастроек 	- Неопределено, Строка - варианты определенные в функциях
//						СервисКриптографииDSSКлиентСервер.ИсточникНастроекПараметрыСеанса(),
//						СервисКриптографииDSSКлиентСервер.ИсточникНастроекХранилище().
//

Процедура УстановитьТекущегоПользователя(СсылкаУчетнойЗаписи, ИсточникНастроек = Неопределено) Экспорт

	ТекущиеНастройки 	= СервисКриптографииDSSСлужебный.ПолучитьКонтейнерНастроек(ИсточникНастроек);
	ТекущиеНастройки.ТекущийПользователь = СсылкаУчетнойЗаписи;
	СервисКриптографииDSSСлужебный.УстановитьКонтейнерНастроек(ТекущиеНастройки, ИсточникНастроек);

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

// Служит для создания новой учетной записи сервиса облачной подписи или обновления существующего элемента
//
// Параметры:
//  НастройкиУчетнойЗаписи - Структура:
//    * Владелец - СправочникСсылка.ЭкземплярыСервераDSS
//    * Логин - Строка
//    * ПервичнаяАутентификация - ПеречислениеСсылка.СпособыАвторизацииDSS
//    * ВторичнаяАвторизация - ПеречислениеСсылка.СпособыАвторизацииDSS - необязательный
//    * Комментарий - Строка - необязательный
//
// Возвращаемое значение:
//  СправочникСсылка.УчетныеЗаписиDSS
//


Функция НовыйАтрибут(OID, ИмяПоля, ДлинаПоля, Представление = "", Назначение = "", Обязательно = Ложь)

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

	Возврат Результат;

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

Функция ПолучитьТипТега(ОпределениеASN, ДанныеФайла)

	Если ПолучитьТипКласса(ОпределениеASN, ДанныеФайла) = "C" И ДанныеФайла.Блок <> 0 Тогда
		НашлиЗначение = "CONTEXT";
	Иначе
		НашлиЗначение = ОпределениеASN.Теги[ДанныеФайла.Блок];
	КонецЕсли;

	Если НашлиЗначение <> Неопределено Тогда
		Возврат НашлиЗначение;
	Иначе
		Возврат Формат(ДанныеФайла, "ЧЦ=2");
	КонецЕсли;

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

Функция ПолучитьТипКласса(ОпределениеASN, ДанныеФайла)

	НашлиЗначение = ОпределениеASN.Классы[ДанныеФайла.Класс];
	Если НашлиЗначение <> Неопределено Тогда
		Возврат НашлиЗначение;
	Иначе
		ВызватьИсключение НСтр("ru = 'Ошибка определения класса'");
	КонецЕсли;

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

Функция ПолучитьТип(ОпределениеASN, ДанныеФайла)

	Результат = ОпределениеASN.Типы[ДанныеФайла.Тип];
	Возврат Результат;

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

Функция ПолучитьСледующийБайт(ОпределениеASN, ПотокБайтов, ТекущийСчетчик)

	Если ПотокБайтов.Размер >= ТекущийСчетчик Тогда
		ТекущийБайт = ПотокБайтов.Получить(ТекущийСчетчик - 1);
		ТекущийСчетчик = ТекущийСчетчик + 1;
	Иначе
		ВызватьИсключение НСтр("ru = 'Неожиданный конец файла'");
	КонецЕсли;

	Возврат ТекущийБайт;

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

Функция ПолучитьСледующиеБайты(ОпределениеASN, ПотокБайтов, ТекущийСчетчик, Количество = 0, ПустыеБайты = Ложь)

	Если ПустыеБайты Тогда
		ПредыдущийБайт	= Неопределено;
		ТекущийБайт 	= ПотокБайтов.Получить(ТекущийСчетчик - 1);
		Пока Истина Цикл
			Количество		= Количество + 1;
			ПредыдущийБайт	= ТекущийБайт;
			ТекущийБайт 	= ПотокБайтов.Получить(ТекущийСчетчик + Количество - 1);
			Если ТекущийБайт = 0 И ПредыдущийБайт = 0 Тогда
				Количество = Количество - 2;
				Прервать;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;

	Если ПотокБайтов.Размер >= (Количество + ТекущийСчетчик - 1) Тогда
		Если Количество = 0 Тогда
			Результат = Новый БуферДвоичныхДанных(0);
		Иначе
			Результат = ПотокБайтов.ПолучитьСрез(ТекущийСчетчик - 1, Количество);
		КонецЕсли;
		ТекущийСчетчик = ТекущийСчетчик + Количество;

	Иначе
		ВызватьИсключение НСтр("ru = 'Неожиданный конец файла'");
	КонецЕсли;

	Возврат Результат;

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

Функция ПолучитьТекущийТег(ОпределениеASN, ПотокБайтов, ТекущийСчетчик)

	ТекущийБайт = ПолучитьСледующийБайт(ОпределениеASN, ПотокБайтов, ТекущийСчетчик);
	Класс 	= ПобитовоеИ(ТекущийБайт, 192);// 0xc0 (класс блока)
	Тип 	= ПобитовоеИ(ТекущийБайт, 32);// 0x20 (примитивный или составной)
	Блок	= ПобитовоеИ(ТекущийБайт, 31);// 0x1f (тип примитивного блока)

	Если Блок = 21 Тогда // 0x1f
		Блок = 0;
		Пока Истина Цикл
			ТекущийБайт = ПолучитьСледующийБайт(ОпределениеASN, ПотокБайтов, ТекущийСчетчик);
			Блок = ПобитовоеИли(ПобитовыйСдвигВлево(Блок, 7), 127);//  0x7f
			Если ПобитовоеИ(ТекущийБайт, 128) <> 0 Тогда //  0x80
				Прервать;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;

	Возврат Новый Структура("Блок, Тип, Класс", Блок, Тип, Класс);

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

Функция ПолучитьТекущуюДлину(ОпределениеASN, ПотокБайтов, ТекущийСчетчик)

	Результат 	= 0;
	ТекущийБайт = ПолучитьСледующийБайт(ОпределениеASN, ПотокБайтов, ТекущийСчетчик);

	Если ПроверитьБит(ТекущийБайт, 7) Тогда
		Количество = ПобитовоеИ(ТекущийБайт, 127);
		Если Количество = 127 Тогда
			ВызватьИсключение НСтр("ru = 'ASN.1 syntax error'");
		КонецЕсли;

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

		Попытка
			Результат = Число(Результат);
		Исключение
			Результат = 0;
		КонецПопытки;

	Иначе
		Результат = ТекущийБайт;

	КонецЕсли;

	Возврат Результат;

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

Функция Привести_Булево(МассивБайтов)

	Результат = Ложь;

	Если МассивБайтов.Размер <> 1 Тогда
		ВызватьИсключение НСтр("ru = 'ASN.1 syntax error'");
	ИначеЕсли МассивБайтов[0] <> 0 Тогда
		Результат = Истина;
	КонецЕсли;

	Возврат Результат;

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

Функция Привести_Целое(МассивБайтов)

	Результат		= 0;
	ЗначенияБайтов 	= Ложь;
	Количество		= МассивБайтов.Размер;
	Если Количество > 1 Тогда
		ЗначенияБайтов = МассивБайтов[0] = 255 И ПобитовоеИ(МассивБайтов[1], 128);
		ЗначенияБайтов = ЗначенияБайтов ИЛИ (МассивБайтов[0] = 0 И НЕ ПобитовоеИ(МассивБайтов[1], 128));
	ИначеЕсли Количество = 0 Тогда
		ЗначенияБайтов = Истина;
	КонецЕсли;

	Если ЗначенияБайтов Тогда
		ВызватьИсключение НСтр("ru = 'ASN.1 syntax error'");
	КонецЕсли;

	ОтрицательноеЗначение = ПроверитьБит(МассивБайтов[0], 8);
	Если ОтрицательноеЗначение Тогда
		Для Счетчик = 1 По Количество Цикл
			МассивБайтов[Счетчик - 1] = 255 - МассивБайтов[Счетчик - 1];
		КонецЦикла;
		Счетчик = Счетчик - 1;
		Пока Счетчик > 0 Цикл
			МассивБайтов[Счетчик] = МассивБайтов[Счетчик] + 1;
			Если МассивБайтов[Счетчик] <= 255 Тогда
				Прервать;
			КонецЕсли;
			МассивБайтов[Счетчик] = 0;
			Счетчик = Счетчик - 1;
		КонецЦикла;
	КонецЕсли;

	Для каждого СтрокаМассива Из МассивБайтов Цикл
		Результат = Результат * 256 + СтрокаМассива;
	КонецЦикла;

	Если ОтрицательноеЗначение Тогда
		Результат = - Результат;
	КонецЕсли;

	Попытка
		Результат = Число(Результат);
	Исключение
		Результат = 0;
	КонецПопытки;

	Возврат Результат;

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

Функция Привести_Пустое(МассивБайтов)

	Результат = Неопределено;

	Если МассивБайтов.Размер <> 0 Тогда
		ВызватьИсключение НСтр("ru = 'ASN.1 syntax error'");
	КонецЕсли;

	Возврат Результат;

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

Функция Привести_Строка(МассивБайтов)

	Результат = "";

	Для каждого СтрокаМассива Из МассивБайтов Цикл
		Если СтрокаМассива > 31 Тогда // Лучше использовать НайтиНедопустимыеСимволыXML, но в веб-клиенте не работает
			СледующийСимвол = Символ(СтрокаМассива);
			Результат = Результат + СледующийСимвол;
		КонецЕсли;
	КонецЦикла;

	Возврат Результат;

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

Функция Привести_Дата(МассивБайтов, Алгоритм = "Z")

	ПредставлениеДаты = Привести_Строка(МассивБайтов);
	Результат = '00010101';

	Попытка
		Если Алгоритм = "Z" Тогда
			ГодДаты = Сред(ПредставлениеДаты, 1, 2);
			МесяцДаты = Сред(ПредставлениеДаты, 3, 2);
			ДеньДаты = Сред(ПредставлениеДаты, 5, 2);
			ЧасДаты = Сред(ПредставлениеДаты, 7, 2);
			МинутыДаты = Сред(ПредставлениеДаты, 9, 2);
			СекундыДаты = Сред(ПредставлениеДаты, 11, 2);

			Если Число(ГодДаты) > 49 Тогда
				ГодДаты = "19" + ГодДаты;
			Иначе
				ГодДаты = "20" + ГодДаты;
			КонецЕсли;

			Результат = Дата(ГодДаты + МесяцДаты + ДеньДаты + ЧасДаты + МинутыДаты + СекундыДаты);
		КонецЕсли;

	Исключение
		Результат = '00010101';

	КонецПопытки;

	Возврат Результат;

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


Функция ПроверкаПередВыполнениемОперации(КонтекстФормы, ЗначениеПароля) Экспорт

	Результат = Истина;

	Если ЭтоОперацияОблачнойПодписи(КонтекстФормы) Тогда
		ТекстОшибки = "";
		КодЯзыка = СервисКриптографииDSSСлужебныйКлиент.КодЯзыка();
		РеестрИмен = БазовыйРеестрРеквизитов(КонтекстФормы);

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

		Если ТипОперации <> "Шифрование" И ТипОперации <> "Проверка" Тогда
			ВыбранСпособ = ЗначениеЗаполнено(ДанныеПодтверждения.ВторичнаяАвторизация);
		КонецЕсли;

		Если Авторизован И ТипОперации <> "Шифрование" Тогда
			ТекущийСертификат = ДанныеПодтверждения.Сертификат;
			ТребуетПинКод = ТекущийСертификат.ТребуетсяПинКод И НЕ ЗначениеЗаполнено(ЗначениеПароля);
		КонецЕсли;

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

		Если ТребуетПинКод И Авторизован Тогда
			ТекстОшибки = ТекстОшибки + Символы.ПС + НСтр("ru = 'Не указан пин-код к закрытому ключу сертификата'", КодЯзыка);
		КонецЕсли;

		Если НЕ Авторизован
			И НЕ СертификатАвторизации
			И НЕ ЗначениеЗаполнено(КонтекстФормы[РеестрИмен.ПодтверждениеДанныеСертификата]) Тогда
			ТекстОшибки = ТекстОшибки + Символы.ПС + НСтр("ru = 'Необходимо загрузить сертификат авторизации.'", КодЯзыка);
		КонецЕсли;

		Если НЕ ПустаяСтрока(ТекстОшибки) Тогда
			Результат = Ложь;
			СервисКриптографииDSSКлиент.ВывестиОшибку(Неопределено, СокрЛП(ТекстОшибки));
		КонецЕсли;

	КонецЕсли;

	Возврат Результат;

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

// Служебная, формирует параметры запроса для отправки на сервис облачной подписи, и по необходимости
// вторичной авторизации и начало цикла подтверждения.
//
// Параметры:
//  КонтекстФормы   			- ФормаКлиентскогоПриложения - содержит реквизиты:
//    * ДанныеПодтверждения	- см. СервисКриптографииDSSПодтверждениеСервер.ДанныеВторичнойАвторизацииПоУмолчанию
//    * НастройкиПользователя	- см. СервисКриптографииDSS.НастройкиПользователяПоУмолчанию
//    * ПарольЛогина			- Строка
//    * СпособПодтверждения	- ПеречислениеСсылка.СпособыАвторизацииDSS
//    * ДанныеСертификата		- Структура
//    * ПодтверждениеТипОперации - Строка
//    * ОжидатьВыполнения		- Булево
//    * КодПодтверждения		- Строка
//  ОписаниеДанных				- Структура
//  ЗначениеПароля				- Строка
//  Индекс						- Число - элемент данных для обработки
//

Процедура ВыполнитьНачальнуюОперациюСервиса(
					КонтекстФормы,
					ОписаниеДанных = Неопределено,
					ЗначениеПароля = "",
					Индекс = 0) Экспорт

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

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

	ОповещениеСледующее = Новый ОписаниеОповещения("ВыполнитьНачальнуюОперациюСервисаПослеАвторизации", ЭтотОбъект, ПараметрыОперации);

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

	Иначе
		РезультатВыполнения = СервисКриптографииDSSКлиент.ОтветСервисаПоУмолчанию();
		ВыполнитьОбработкуОповещения(ОповещениеСледующее, РезультатВыполнения);

	КонецЕсли;

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

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

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

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

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

// Служебная процедура открывает форму для вывода ошибки возникшей в процессе подтверждения
//

Процедура ОткрытьФормуОшибкиПодтверждения(ОповещениеСледующее,
			ДанныеПодтверждения,
			РежимПовторения = Истина,
			ФормаВладелец = Неопределено)

	СервисКриптографииDSSСлужебныйВызовСервера.НачальныеНастройкиФормы("ПодтверждениеОперацииОшибка");

	ПараметрыФормы = Новый Структура("ОписаниеОшибки");
	ПараметрыФормы.Вставить("ОписаниеОшибки", ДанныеПодтверждения.Ошибка);
	ПараметрыФормы.Вставить("РежимПовторения", РежимПовторения);
	ПараметрыФормы.Вставить("ДанныеПодтверждения", ДанныеПодтверждения);

	ОповещениеОЗавершении = СервисКриптографииDSSСлужебныйКлиент.ОбъектОповещенияОбработкиФормы(ОповещениеСледующее);
	ОткрытьФорму("Обработка.УправлениеПодключениемDSS.Форма.ПодтверждениеОперацииОшибка", ПараметрыФормы, ФормаВладелец, , , , ОповещениеОЗавершении);

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

// Служебная процедура открывает форму для ввода одноразового пароля, для случаев SMS и EMAIL
//

Процедура ОткрытьФормуПодтвержденияКода(ОповещениеСледующее, ДанныеПодтверждения, НастройкиПользователя)

	ПараметрыФормы = Новый Структура();
	ПараметрыФормы.Вставить("ЗаголовокФормы", ДанныеПодтверждения.Заголовок);
	ПараметрыФормы.Вставить("ДанныеДокументов", ДанныеПодтверждения.ДанныеДокументов);
	ПараметрыФормы.Вставить("Сертификат", ДанныеПодтверждения.Сертификат);
	ПараметрыФормы.Вставить("НастройкиПользователя", НастройкиПользователя);
	ПараметрыФормы.Вставить("ДанныеПодтверждения", ДанныеПодтверждения);

	ОповещениеОЗавершении = СервисКриптографииDSSСлужебныйКлиент.ОбъектОповещенияОбработкиФормы(ОповещениеСледующее);
	ОткрытьФорму("Обработка.УправлениеПодключениемDSS.Форма.ПодтверждениеОперацииКодПодтверждения", ПараметрыФормы, , , , , ОповещениеОЗавершении);

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

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


Процедура ПоказатьВопросПользователю(ОписаниеОповещенияОЗавершении, ТекстВопроса, Кнопки, ДополнительныеПараметры = Неопределено) Экспорт

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

	КодыВозвратаДиалога = Новый Соответствие;
	КодыВозвратаДиалога.Вставить(КодВозвратаДиалога.Да, "КодВозвратаДиалога.Да");
	КодыВозвратаДиалога.Вставить(КодВозвратаДиалога.Нет, "КодВозвратаДиалога.Нет");
	КодыВозвратаДиалога.Вставить(КодВозвратаДиалога.ОК, "КодВозвратаДиалога.ОК");
	КодыВозвратаДиалога.Вставить(КодВозвратаДиалога.Отмена, "КодВозвратаДиалога.Отмена");
	КодыВозвратаДиалога.Вставить(КодВозвратаДиалога.Повторить, "КодВозвратаДиалога.Повторить");
	КодыВозвратаДиалога.Вставить(КодВозвратаДиалога.Прервать, "КодВозвратаДиалога.Прервать");
	КодыВозвратаДиалога.Вставить(КодВозвратаДиалога.Пропустить, "КодВозвратаДиалога.Пропустить");
	КодыВозвратаДиалога.Вставить(КодВозвратаДиалога.Таймаут, "КодВозвратаДиалога.Таймаут");

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

	РежимыДиалогаВопрос = Новый Соответствие;
	РежимыДиалогаВопрос.Вставить(РежимДиалогаВопрос.ДаНет, "РежимДиалогаВопрос.ДаНет");
	РежимыДиалогаВопрос.Вставить(РежимДиалогаВопрос.ДаНетОтмена, "РежимДиалогаВопрос.ДаНетОтмена");
	РежимыДиалогаВопрос.Вставить(РежимДиалогаВопрос.ОК, "РежимДиалогаВопрос.ОК");
	РежимыДиалогаВопрос.Вставить(РежимДиалогаВопрос.ОКОтмена, "РежимДиалогаВопрос.ОКОтмена");
	РежимыДиалогаВопрос.Вставить(РежимДиалогаВопрос.ПовторитьОтмена, "РежимДиалогаВопрос.ПовторитьОтмена");
	РежимыДиалогаВопрос.Вставить(РежимДиалогаВопрос.ПрерватьПовторитьПропустить, "РежимДиалогаВопрос.ПрерватьПовторитьПропустить");

	КнопкиДиалога = Кнопки;

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

	Если ТипЗнч(Кнопки) = Тип("РежимДиалогаВопрос") Тогда
		КнопкиДиалога = РежимыДиалогаВопрос[Кнопки];
	КонецЕсли;

	Если ТипЗнч(Параметры.КнопкаПоУмолчанию) = Тип("КодВозвратаДиалога") Тогда
		Параметры.КнопкаПоУмолчанию = КодыВозвратаДиалога[Параметры.КнопкаПоУмолчанию];
	КонецЕсли;

	Если ТипЗнч(Параметры.КнопкаТаймаута) = Тип("КодВозвратаДиалога") Тогда
		Параметры.КнопкаТаймаута = КодыВозвратаДиалога[Параметры.КнопкаТаймаута];
	КонецЕсли;

	Параметры.Вставить("Кнопки", КнопкиДиалога);
	Параметры.Вставить("ТекстСообщения", ТекстВопроса);

	ОткрытьФорму("ОбщаяФорма.Вопрос", Параметры, , , , , ОписаниеОповещенияОЗавершении);

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

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

Функция ПараметрыВопросаПользователю() Экспорт

	Параметры = Новый Структура;
	Параметры.Вставить("КнопкаПоУмолчанию", Неопределено);
	Параметры.Вставить("Таймаут", 0);
	Параметры.Вставить("КнопкаТаймаута", Неопределено);
	Параметры.Вставить("Заголовок", КлиентскоеПриложение.ПолучитьЗаголовок());
	Параметры.Вставить("ПредлагатьБольшеНеЗадаватьЭтотВопрос", Истина);
	Параметры.Вставить("БольшеНеЗадаватьЭтотВопрос", Ложь);
	Параметры.Вставить("БлокироватьВесьИнтерфейс", Ложь);
	Параметры.Вставить("Картинка", БиблиотекаКартинок.Вопрос32);
	Параметры.Вставить("ТекстФлажка", "");

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

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

// Вызывается при необходимости открыть форму списка активных пользователей,
// которые в данный момент времени работают с системой.
//
// Параметры:
//    ПараметрыФормы - Структура        - см. описание параметра Параметры метода ОткрытьФорму в синтакс-помощнике.
//    ВладелецФормы  - ФормаКлиентскогоПриложения - см. описание параметра Владелец метода ОткрытьФорму в синтакс-помощнике.
//

Процедура ОткрытьСписокАктивныхПользователей(ПараметрыФормы = Неопределено, ВладелецФормы = Неопределено) Экспорт

	Если ОбщегоНазначенияКлиент.ПодсистемаСуществует("СтандартныеПодсистемы.ЗавершениеРаботыПользователей") Тогда

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

	Иначе

		ПоказатьПредупреждение(,
			НСтр("ru = 'Для того чтобы открыть список активных пользователей, перейдите в меню
				       |Все функции - Стандартные - Активные пользователи.'"));

	КонецЕсли;

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

// См. СтандартныеПодсистемыСервер.ЭтоБазоваяВерсияКонфигурации

Функция ЭтоБазоваяВерсияКонфигурации() Экспорт

	Возврат ПараметрКлиента("ЭтоБазоваяВерсияКонфигурации");

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

// См. СтандартныеПодсистемыСервер.ЭтоУчебнаяПлатформа

Функция ЭтоУчебнаяПлатформа() Экспорт

	Возврат ПараметрКлиента("ЭтоУчебнаяПлатформа");

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

#Область ОбработкаСобытийПриложения

// Отключает выдачу предупреждения пользователю при завершении работы программы.
//


Процедура СкорректироватьНачальнуюСтраницуНеразделенногоПользователя()

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

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

	Если Не СеансЗапущенБезРазделителей Тогда
		Возврат;
	КонецЕсли;

	КлючОбъекта  = "БазоваяФункциональность";
	КлючНастроек = "СоставФормНачальнойСтраницыВМетаданных";

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

	// Компенсация изменения состава форм в метаданных начальной страницы.
	НовыеНастройки = Новый НастройкиНачальнойСтраницы;
	СоставФормВМетаданных = НовыеНастройки.ПолучитьСоставФорм();

	Если ТипЗнч(ПредыдущийСоставФормВМетаданных) <> Тип("Структура")
	 Или Не ПредыдущийСоставФормВМетаданных.Свойство("ЛеваяКолонка")
	 Или ТипЗнч(ПредыдущийСоставФормВМетаданных.ЛеваяКолонка) <> Тип("Массив")
	 Или Не ПредыдущийСоставФормВМетаданных.Свойство("ПраваяКолонка")
	 Или ТипЗнч(ПредыдущийСоставФормВМетаданных.ПраваяКолонка) <> Тип("Массив") Тогда

		ПредыдущийСоставФормВМетаданных = Новый СоставФормНачальнойСтраницы;

	ИначеЕсли СоставФормСовпадает(ПредыдущийСоставФормВМетаданных.ЛеваяКолонка,  СоставФормВМетаданных.ЛеваяКолонка)
	        И СоставФормСовпадает(ПредыдущийСоставФормВМетаданных.ПраваяКолонка, СоставФормВМетаданных.ПраваяКолонка) Тогда

		// Состав форм в метаданных начальной страницы не изменился.
		Возврат;
	КонецЕсли;

	КомпенсироватьИзмененияСоставаФормВМетаданныхНачальнойСтраницы(ПредыдущийСоставФормВМетаданных);

	СохраняемыйСоставФормВМетаданных = Новый Структура("ЛеваяКолонка, ПраваяКолонка");
	ЗаполнитьЗначенияСвойств(СохраняемыйСоставФормВМетаданных, СоставФормВМетаданных);

	ХранилищеОбщихНастроек.Сохранить(КлючОбъекта, КлючНастроек, СохраняемыйСоставФормВМетаданных);

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

// Для процедуры СкорректироватьНачальнуюСтраницуНеразделенногоПользователя.

Функция СоставФормСовпадает(ПредыдущиеФормыВМетаданных, ФормыВМетаданных)

	Если ПредыдущиеФормыВМетаданных.Количество() <> ФормыВМетаданных.Количество() Тогда
		Возврат Ложь;
	КонецЕсли;

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

	Возврат Истина;

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

// Для процедуры СкорректироватьНачальнуюСтраницуНеразделенногоПользователя.

Процедура КомпенсироватьИзмененияСоставаФормВМетаданныхНачальнойСтраницы(ПредыдущийСоставФормВМетаданных)

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

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

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

	НовыеНастройки = Новый НастройкиНачальнойСтраницы;
	СоставФормВМетаданных = НовыеНастройки.ПолучитьСоставФорм();

	УдалитьНовыеФормыНачальнойСтраницы(СоставФорм.ЛеваяКолонка,
		ПредыдущийСоставФормВМетаданных.ЛеваяКолонка, СоставФормВМетаданных.ЛеваяКолонка);

	УдалитьНовыеФормыНачальнойСтраницы(СоставФорм.ПраваяКолонка,
		ПредыдущийСоставФормВМетаданных.ПраваяКолонка, СоставФормВМетаданных.ПраваяКолонка);

	ТекущиеНастройки = Новый НастройкиНачальнойСтраницы;
	ТекущиеНастройки.УстановитьСоставФорм(СоставФорм);

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

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

// Для процедуры КомпенсироватьИзмененияСоставаФормВМетаданныхНачальнойСтраницы.

Процедура УдалитьНовыеФормыНачальнойСтраницы(ТекущиеФормы, ПредыдущиеФормыВМетаданных, ФормыВМетаданных)

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

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

Процедура ОбработатьОчередьСкопированныхНастроек()

	Если ТекущийРежимЗапуска() = Неопределено Тогда
		Возврат;
	КонецЕсли;

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

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

	ХранилищеОбщихНастроек.Сохранить("ОчередьНастроек", "НеПримененныеНастройки", Неопределено);

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

Процедура ВыполнитьОбработчикиУстановкиПараметровСеанса(ИменаПараметровСеанса, Обработчики, УстановленныеПараметры)

	// Массив с ключами параметров сеанса
	// задаются начальным словом в имени параметра сеанса и символом "*".
	ПараметрыСеансаКлючи = Новый Массив;

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

	Для каждого ИмяПараметра Из ИменаПараметровСеанса Цикл
		Если УстановленныеПараметры.Найти(ИмяПараметра) <> Неопределено Тогда
			Продолжить;
		КонецЕсли;

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

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

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

Процедура ИгнорироватьОтправкуИдентификаторовОбъектовМетаданных(ЭлементДанных, ОтправкаЭлемента, Знач СозданиеНачальногоОбраза = Ложь)

	Если Не СозданиеНачальногоОбраза
		И ОбъектМетаданных(ЭлементДанных) = Метаданные.Справочники.ИдентификаторыОбъектовМетаданных Тогда

		ОтправкаЭлемента = ОтправкаЭлементаДанных.Игнорировать;

	КонецЕсли;

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


Функция СвойстваВидовДоступа() Экспорт

	Кэш = УправлениеДоступомСлужебныйПовтИсп.ОписаниеСвойствВидовДоступаСеанса();

	ТекущаяДатаСеанса = ТекущаяДатаСеанса();
	Если Кэш.Проверка.Дата + 3 > ТекущаяДатаСеанса Тогда
		Возврат Кэш.СвойстваСеанса;
	КонецЕсли;

	НовоеЗначение = Кэш.ХешСуммы;

	ИмяПараметра = "СтандартныеПодсистемы.УправлениеДоступом.СвойстваВидовДоступа";
	СтароеЗначение = СтандартныеПодсистемыСервер.ПараметрРаботыРасширения(ИмяПараметра, Истина);
	СтароеЗначение = НовыеХешСуммыСвойствВидовДоступа(СтароеЗначение);

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

	Кэш.Проверка.Дата = ТекущаяДатаСеанса;

	Возврат Кэш.СвойстваСеанса;

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

// Для процедуры УправлениеДоступомСлужебныйПовтИсп.ОписаниеСвойствВидовДоступаСеанса.
// Возвращает свойства видов доступа, заполняемых при внедрении в процедуре ПриЗаполненииВидовДоступа
// общего модуля УправлениеДоступомПереопределяемый и одноименных обработчиках этого события.
//
// Смотри также УправлениеДоступомПереопределяемый.ПриЗаполненииВидовДоступа.
//
// Параметры:
//  ХешСуммы - см. НовыеХешСуммыСвойствВидовДоступа
//
// Возвращаемое значение:
//   см. СвойстваВидовДоступа
//


Процедура ПерестроитьАгрегаты()

	Кэш = КэшПроверкиРазделения();

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

////////////////////////////////////////////////////////////////////////////////
// Для работы в файловом режиме.

// Возвращает Истина если ИБ работает в файловом режиме и разделение отключено.

Функция РежимРаботыЛокальныйФайловый()
	Возврат ОбщегоНазначения.ИнформационнаяБазаФайловая() И НЕ ОбщегоНазначения.РазделениеВключено();
КонецФункции

// Определяет актуальность итогов и агрегатов. Если нет регистров, то возвращает Истина.

Функция НадоСдвинутьГраницуИтогов() Экспорт
	Параметры = ПараметрыИтоговИАгрегатов();
	Возврат Параметры.ЕстьРегистрыИтогов И ДобавитьМесяц(Параметры.ДатаРасчетаИтогов, 2) < ТекущаяДатаСеанса();
КонецФункции

// Получает значение константы "ПараметрыИтоговИАгрегатов".

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

// Перезаполняет константу "ПараметрыИтоговИАгрегатов".

Функция СформироватьПараметрыИтоговИАгрегатов()
	Параметры = Новый Структура;
	Параметры.Вставить("ЕстьРегистрыИтогов", Ложь);
	Параметры.Вставить("ДатаРасчетаИтогов",  '39991231235959'); // М1.12.3999 23:59:59, максимальная дата.

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

	Если НЕ Параметры.ЕстьРегистрыИтогов Тогда
		Параметры.Вставить("ДатаРасчетаИтогов", '00010101');
	КонецЕсли;

	ЗаписатьПараметрыИтоговИАгрегатов(Параметры);

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

// Записывает значение константы "ПараметрыИтоговИАгрегатов".

Процедура ЗаписатьПараметрыИтоговИАгрегатов(Параметры) Экспорт
	Константы.ПараметрыИтоговИАгрегатов.Установить(Новый ХранилищеЗначения(Параметры));
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Обновление информационной базы.

// [2.3.4.7] Актуализирует использование регламентных заданий ОбновлениеАгрегатов и ПерестроениеАгрегатов.

Процедура ОбновитьИспользованиеРегламентныхЗаданий() Экспорт
	// Регламентные задания "ОбновлениеАгрегатов" и "ПерестроениеАгрегатов".
	ЕстьРегистрыСАгрегатами = ЕстьРегистрыСАгрегатами();
	ОбновитьРегламентноеЗадание(Метаданные.РегламентныеЗадания.ОбновлениеАгрегатов, ЕстьРегистрыСАгрегатами);
	ОбновитьРегламентноеЗадание(Метаданные.РегламентныеЗадания.ПерестроениеАгрегатов, ЕстьРегистрыСАгрегатами);

	// Регламентное задание "УстановкаПериодаРассчитанныхИтогов".
	ОбновитьРегламентноеЗадание(Метаданные.РегламентныеЗадания.УстановкаПериодаРассчитанныхИтогов, Истина);
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Другие

// Вспомогательная для процедуры ОбновитьИспользованиеРегламентныхЗаданий.

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

// Определяет, задано ли расписание регламентного задания.
//
// Параметры:
//   Расписание - РасписаниеРегламентногоЗадания - расписание регламентного задания.
//
// Возвращаемое значение:
//   Булево - Истина, если расписание регламентного задания задано.
//

Функция РасписаниеЗаполнено(Расписание)
	Возврат Расписание <> Неопределено
		И Строка(Расписание) <> Строка(Новый РасписаниеРегламентногоЗадания);
КонецФункции

// Возвращает расписание регламентного задания по умолчанию.
//   Используется вместо свойства "ОбъектМетаданных: РегламентноеЗадание.Расписание",
//   т.к. оно всегда имеет значение Неопределено.
//

Функция РасписаниеПоУмолчанию(МетаданныеРегламентногоЗадания)
	Расписание = Новый РасписаниеРегламентногоЗадания;
	Расписание.ПериодПовтораДней = 1;
	Если МетаданныеРегламентногоЗадания = Метаданные.РегламентныеЗадания.ОбновлениеАгрегатов Тогда
		Расписание.ВремяНачала = Дата(1, 1, 1, 01, 00, 00);
		ДобавитьДетальноеРасписание(Расписание, "ВремяНачала", Дата(1, 1, 1, 01, 00, 00));
		ДобавитьДетальноеРасписание(Расписание, "ВремяНачала", Дата(1, 1, 1, 14, 00, 00));
	ИначеЕсли МетаданныеРегламентногоЗадания = Метаданные.РегламентныеЗадания.ПерестроениеАгрегатов Тогда
		Расписание.ВремяНачала = Дата(1, 1, 1, 03, 00, 00);
		УстановитьДниНедели(Расписание, "6");
	ИначеЕсли МетаданныеРегламентногоЗадания = Метаданные.РегламентныеЗадания.УстановкаПериодаРассчитанныхИтогов Тогда
		Расписание.ВремяНачала = Дата(1, 1, 1, 01, 00, 00);
		Расписание.ДеньВМесяце = 5;
	Иначе
		Возврат Неопределено;
	КонецЕсли;
	Возврат Расписание;
КонецФункции

// Вспомогательная для функции РасписаниеПоУмолчанию.

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

// Вспомогательная для функции РасписаниеПоУмолчанию.

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


Процедура ПроверитьНаличиеРеквизитовКонтактнойИнформации(Форма, МассивДобавляемыхРеквизитов)

	СписокРеквизитовФормы = Форма.ПолучитьРеквизиты();

	СоздатьПараметрыКонтактнойИнформации = Истина;
	СоздатьТаблицуКонтактнойИнформации = Истина;
	Для Каждого Реквизит Из СписокРеквизитовФормы Цикл
		Если Реквизит.Имя = "ПараметрыКонтактнойИнформации" Тогда
			СоздатьПараметрыКонтактнойИнформации = Ложь;
		ИначеЕсли Реквизит.Имя = "КонтактнаяИнформацияОписаниеДополнительныхРеквизитов" Тогда
			СоздатьТаблицуКонтактнойИнформации = Ложь;
		КонецЕсли;
	КонецЦикла;

	Строка500 = Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(500));
	ИмяОписания = "КонтактнаяИнформацияОписаниеДополнительныхРеквизитов";

	Если СоздатьТаблицуКонтактнойИнформации Тогда

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

		Для Каждого Реквизит Из РеквизитыТаблицы Цикл
			Если РеквизитыДляСоздания[Реквизит.Имя] <> Неопределено Тогда
				РеквизитыДляСоздания[Реквизит.Имя] = Ложь;
			КонецЕсли;
		КонецЦикла;

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

		Если РеквизитыДляСоздания["МеждународныйФорматАдреса"] Тогда
			МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы("МеждународныйФорматАдреса", Новый ОписаниеТипов("Булево"), ИмяОписания));
		КонецЕсли;

		Если РеквизитыДляСоздания["ИмяЭлементаДляРазмещения"] Тогда
			МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы("ИмяЭлементаДляРазмещения", Строка500, ИмяОписания));
		КонецЕсли;

		Если РеквизитыДляСоздания["ИмяРеквизитаКомментарий"] Тогда
			МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы("ИмяРеквизитаКомментарий", Строка500, ИмяОписания));
		КонецЕсли;

		Если РеквизитыДляСоздания["ХранитьИсториюИзменений"] Тогда
			МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы("ХранитьИсториюИзменений", Новый ОписаниеТипов("Булево"), ИмяОписания));
		КонецЕсли;

		Если РеквизитыДляСоздания["ДействуетС"] Тогда
			МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы("ДействуетС", Новый ОписаниеТипов("Дата"), ИмяОписания));
		КонецЕсли;

		Если РеквизитыДляСоздания["Маска"] Тогда
			МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы("Маска", ОбщегоНазначения.ОписаниеТипаСтрока(100), ИмяОписания));
		КонецЕсли;

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

	КонецЕсли;

	Если СоздатьПараметрыКонтактнойИнформации Тогда
		МассивДобавляемыхРеквизитов.Добавить(Новый РеквизитФормы("ПараметрыКонтактнойИнформации", Новый ОписаниеТипов()));
	КонецЕсли;

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

Процедура УстановитьЗначенияРеквизитовПроверки(Объект, НастройкиПроверки = Неопределено)

	Объект.ПроверятьКорректность = ?(НастройкиПроверки = Неопределено, Ложь, НастройкиПроверки.ПроверятьКорректность);

	Объект.ТолькоНациональныйАдрес = Ложь;
	Объект.ВключатьСтрануВПредставление = Ложь;
	Объект.СкрыватьНеактуальныеАдреса = Ложь;

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

Функция НаименованиеДляФормированияИдентификатора(Объект) Экспорт

	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.Мультиязычность") Тогда

		МодульМультиязычностьСервер = ОбщегоНазначения.ОбщийМодуль("МультиязычностьСервер");
		СуффиксТекущегоЯзыка = МодульМультиязычностьСервер.СуффиксТекущегоЯзыка();

		Возврат ?(ЗначениеЗаполнено(СуффиксТекущегоЯзыка),
			Объект["Наименование"+ СуффиксТекущегоЯзыка],
			Объект.Наименование);

	КонецЕсли;

	Возврат Объект.Наименование;

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


Процедура ДобавитьРеквизитВОписание(Форма, СтрокаКонтактнойИнформации, ДанныеВидаКонтактнойИнформации, ЭтоНовыйВидКИ,
	ЭтоРеквизитТабличнойЧасти = Ложь, ЗаполнятьЗначениеРеквизита = Истина, ИмяЭлементаДляРазмещения = "ГруппаКонтактнаяИнформация")

	ТаблицаОписанияДополнительныхРеквизитов = УправлениеКонтактнойИнформациейКлиентСервер.ОписаниеКонтактнойИнформацииНаФорме(Форма);
	НоваяСтрока = ТаблицаОписанияДополнительныхРеквизитов.Добавить();
	НоваяСтрока.ИмяРеквизита  = СтрокаКонтактнойИнформации.ИмяРеквизита;
	НоваяСтрока.Вид           = СтрокаКонтактнойИнформации.Вид;
	НоваяСтрока.Тип           = СтрокаКонтактнойИнформации.Тип;
	НоваяСтрока.ИмяЭлементаДляРазмещения  = ИмяЭлементаДляРазмещения;
	НоваяСтрока.ЭтоРеквизитТабличнойЧасти = ЭтоРеквизитТабличнойЧасти;

	Если НоваяСтрока.Свойство("ЭтоИсторическаяКонтактнаяИнформация") Тогда
		НоваяСтрока.ЭтоИсторическаяКонтактнаяИнформация = СтрокаКонтактнойИнформации.ЭтоИсторическаяКонтактнаяИнформация;
	КонецЕсли;

	Если НоваяСтрока.Свойство("ДействуетС") Тогда
		НоваяСтрока.ДействуетС = СтрокаКонтактнойИнформации.ДействуетС;
	КонецЕсли;

	Если НоваяСтрока.Свойство("ХранитьИсториюИзменений") Тогда
		НоваяСтрока.ХранитьИсториюИзменений = СтрокаКонтактнойИнформации.ХранитьИсториюИзменений;
	КонецЕсли;

	Если НоваяСтрока.Свойство("МеждународныйФорматАдреса") Тогда
		НоваяСтрока.МеждународныйФорматАдреса = СтрокаКонтактнойИнформации.МеждународныйФорматАдреса;
	КонецЕсли;

	НоваяСтрока.Значение      = СтрокаКонтактнойИнформации.Значение;
	НоваяСтрока.Представление = СтрокаКонтактнойИнформации.Представление;
	НоваяСтрока.Комментарий   = СтрокаКонтактнойИнформации.Комментарий;

	Если ЗаполнятьЗначениеРеквизита И Не ЭтоРеквизитТабличнойЧасти Тогда

		Если Форма.Элементы.Найти(СтрокаКонтактнойИнформации.ИмяРеквизита) <> Неопределено
			И Форма.Элементы[СтрокаКонтактнойИнформации.ИмяРеквизита].Вид = ВидПоляФормы.ПолеНадписи Тогда
			ЭтоПолеНадписи = Истина;
		Иначе
			ЭтоПолеНадписи = Ложь;
		КонецЕсли;

		Если СтрокаКонтактнойИнформации.Тип = Перечисления.ТипыКонтактнойИнформации.Адрес
			И (СтрокаКонтактнойИнформации.ВидРедактирования = "Диалог" ИЛИ ЭтоПолеНадписи)
			И ПустаяСтрока(СтрокаКонтактнойИнформации.Представление) Тогда
			Форма[СтрокаКонтактнойИнформации.ИмяРеквизита] = УправлениеКонтактнойИнформациейКлиентСервер.ТекстПустогоАдресаВВидеГиперссылки();
		Иначе
			Форма[СтрокаКонтактнойИнформации.ИмяРеквизита] = СтрокаКонтактнойИнформации.Представление;
		КонецЕсли;

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

	КонецЕсли;

	ДанныеВидаКонтактнойИнформации.Вставить("Ссылка", СтрокаКонтактнойИнформации.Вид);

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

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

Процедура УдалитьКомандыИЭлементыФормы(Форма, ИмяЭлементаДляРазмещения)

	ПараметрыКонтактнойИнформации = ПараметрыКонтактнойИнформацииФормы(Форма.ПараметрыКонтактнойИнформации, ИмяЭлементаДляРазмещения);
	ДобавленныеЭлементы = ПараметрыКонтактнойИнформации.ДобавленныеЭлементы;
	ДобавленныеЭлементы.СортироватьПоПредставлению();

	Для Каждого УдаляемыйЭлемент Из ДобавленныеЭлементы Цикл

		Если УдаляемыйЭлемент.Пометка Тогда
			Форма.Команды.Удалить(Форма.Команды[УдаляемыйЭлемент.Значение]);
		Иначе
			Форма.Элементы.Удалить(Форма.Элементы[УдаляемыйЭлемент.Значение]);
		КонецЕсли;

	КонецЦикла;

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

// Возвращает, доступно ли редактирование в диалоге для указанного типа контактной информации.
//
// Параметры:
//    Тип - ПеречислениеСсылка.ТипыКонтактнойИнформации - тип контактной информации.
//
// Возвращаемое значение:
//    Булево
//

Функция ДляТипаКонтактнойИнформацииДоступноРедактированиеВДиалоге(Тип)

	Возврат Тип = Перечисления.ТипыКонтактнойИнформации.Адрес
		Или Тип = Перечисления.ТипыКонтактнойИнформации.Телефон
		Или Тип = Перечисления.ТипыКонтактнойИнформации.Факс;

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

// Возвращает имена табличных частей документа по виду контактной информации.
//
// Параметры:
//    ТаблицаВидовКонтактнойИнформации - ТаблицаЗначений - список видов контактной информации:
//     * Вид - СправочникСсылка.ВидыКонтактнойИнформации - вид контактной информации.
//    ИмяОбъекта                       - Строка - полное имя объекта метаданного.
//
// Возвращаемое значение:
//    Соответствие - имена табличных части или пустая строка, если табличной части нет.
//

Функция ИменаТабличныхЧастейПоВидамКИ(ТаблицаВидовКонтактнойИнформации, ИмяОбъекта)

	Запрос = Новый Запрос;
	Запрос.Текст = "ВЫБРАТЬ
	|	ВидыКонтактнойИнформации.Вид КАК ВидКИ
	|ПОМЕСТИТЬ ВидыКИ
	|ИЗ
	|	&ТаблицаВидовКонтактнойИнформации КАК ВидыКонтактнойИнформации
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ВЫБОР
	|		КОГДА ВидыКонтактнойИнформации.Родитель.ИмяПредопределенногоВида <> """"
	|		ТОГДА ВидыКонтактнойИнформации.Родитель.ИмяПредопределенногоВида
	|		ИНАЧЕ ВидыКонтактнойИнформации.Родитель.ИмяПредопределенныхДанных
	|	КОНЕЦ КАК ИмяТабличнойЧасти,
	|	ВидыКИ.ВидКИ КАК ВидКонтактнойИнформации
	|ИЗ
	|	ВидыКИ КАК ВидыКИ
	|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ВидыКонтактнойИнформации КАК ВидыКонтактнойИнформации
	|		ПО ВидыКИ.ВидКИ = ВидыКонтактнойИнформации.Ссылка";

	Запрос.УстановитьПараметр("ТаблицаВидовКонтактнойИнформации", ТаблицаВидовКонтактнойИнформации);
	РезультатЗапроса = Запрос.Выполнить().Выбрать();

	Результат = Новый Соответствие;
	Пока РезультатЗапроса.Следующий() Цикл

		Если ЗначениеЗаполнено(РезультатЗапроса.ИмяТабличнойЧасти) Тогда
			ИмяТабличнойЧасти = Сред(РезультатЗапроса.ИмяТабличнойЧасти, СтрНайти(РезультатЗапроса.ИмяТабличнойЧасти, ИмяОбъекта) + СтрДлина(ИмяОбъекта));
		Иначе
			ИмяТабличнойЧасти = "";
		КонецЕсли;

		Результат.Вставить(РезультатЗапроса.ВидКонтактнойИнформации, ИмяТабличнойЧасти);
	КонецЦикла;

	Возврат Результат;

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

// Проверяет на наличие в форме заполненных строк КИ того же вида (за исключением текущей).
//

Функция ЕстьДругиеЗаполненныеСтрокКИДанногоВида(Знач Форма, Знач ПроверяемаяСтрока, Знач ВидКонтактнойИнформации)

	ВсеСтрокиЭтогоВида = УправлениеКонтактнойИнформациейКлиентСервер.ОписаниеКонтактнойИнформацииНаФорме(Форма).НайтиСтроки(
		Новый Структура("Вид", ВидКонтактнойИнформации));

	Для Каждого СтрокаВида Из ВсеСтрокиЭтогоВида Цикл

		Если СтрокаВида <> ПроверяемаяСтрока Тогда
			Представление = Форма[СтрокаВида.ИмяРеквизита];
			Если Не ПустаяСтрока(Представление) Тогда
				Возврат Истина;
			КонецЕсли;
		КонецЕсли;

	КонецЦикла;

	Возврат Ложь;
КонецФункции


Функция ПреобразованиеXSLT_ТаблицаЗначенийРазличияXML()
	Преобразователь = Новый ПреобразованиеXSL;

	// Пространство имен должно быть пустым!
	Преобразователь.ЗагрузитьТаблицуСтилейXSLИзСтроки("
		|<xsl:stylesheet version=""1.0""
		|  xmlns:xsl=""http://www.w3.org/1999/XSL/Transform""
		|  xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""
		|  xmlns:xs=""http://www.w3.org/2001/XMLSchema""
		|
		|  xmlns:str=""http://exslt.org/strings""
		|  xmlns:exsl=""http://exslt.org/common""
		|
		|  extension-element-prefixes=""str exsl""
		|>
		|<xsl:output method=""xml"" omit-xml-declaration=""yes"" indent=""yes"" encoding=""utf-8""/>
		|" + XSLT_ШаблоныСтроковыхФункций() + "
		|" + XSLT_ШаблоныФункцийXPath() + "
		|
		|  <!-- parce tree elements to xpath-value -->
		|  <xsl:template match=""node()"" mode=""action"">
		|
		|    <xsl:variable name=""text"">
		|      <xsl:call-template name=""str-trim-all"">
		|        <xsl:with-param name=""str"" select=""text()"" />
		|      </xsl:call-template>
		|    </xsl:variable>
		|
		|    <xsl:if test=""$text!=''"">
		|      <xsl:element name=""item"">
		|        <xsl:attribute name=""path"">
		|          <xsl:variable name=""tmp-path"">
		|            <xsl:call-template name=""build-path"" />
		|          </xsl:variable>
		|          <xsl:value-of select=""substring($tmp-path, 6)"" /> <!-- pass '/dn/f' or '/dn/s' -->
		|        </xsl:attribute>
		|        <xsl:attribute name=""value"">
		|          <xsl:value-of select=""text()"" />
		|        </xsl:attribute>
		|      </xsl:element>
		|    </xsl:if>
		|
		|    <xsl:apply-templates select=""@* | node()"" mode=""action""/>
		|  </xsl:template>
		|
		|  <!-- parce tree attributes to xpath-value -->
		|  <xsl:template match=""@*"" mode=""action"">
		|    <xsl:element name=""item"">
		|      <xsl:attribute name=""path"">
		|          <xsl:variable name=""tmp-path"">
		|            <xsl:call-template name=""build-path"" />
		|          </xsl:variable>
		|          <xsl:value-of select=""substring($tmp-path, 6)"" /> <!-- pass '/dn/f' or '/dn/s' -->
		|      </xsl:attribute>
		|      <xsl:attribute name=""value"">
		|        <xsl:value-of select=""."" />
		|      </xsl:attribute>
		|    </xsl:element>
		|  </xsl:template>
		|
		|  <!-- main -->
		|  <xsl:variable name=""dummy"">
		|    <xsl:element name=""first"">
		|      <xsl:apply-templates select=""/dn/f"" mode=""action"" />
		|    </xsl:element>
		|    <xsl:element name=""second"">
		|      <xsl:apply-templates select=""/dn/s"" mode=""action"" />
		|    </xsl:element>
		|  </xsl:variable>
		|  <xsl:variable name=""dummy-nodeset"" select=""exsl:node-set($dummy)"" />
		|  <xsl:variable name=""first-items"" select=""$dummy-nodeset/first/item"" />
		|  <xsl:variable name=""second-items"" select=""$dummy-nodeset/second/item"" />
		|
		|  <xsl:template match=""/"">
		|
		|    <!-- first vs second -->
		|    <xsl:variable name=""first-second"">
		|      <xsl:for-each select=""$first-items"">
		|        <xsl:call-template name=""compare"">
		|          <xsl:with-param name=""check"" select=""$second-items"" />
		|        </xsl:call-template>
		|      </xsl:for-each>
		|    </xsl:variable>
		|    <xsl:variable name=""first-second-nodeset"" select=""exsl:node-set($first-second)"" />
		|
		|    <!-- second vs first without doubles -->
		|    <xsl:variable name=""doubles"" select=""$first-second-nodeset/item"" />
		|    <xsl:variable name=""second-first"">
		|      <xsl:for-each select=""$second-items"">
		|        <xsl:call-template name=""compare"">
		|          <xsl:with-param name=""check"" select=""$first-items"" />
		|          <xsl:with-param name=""doubles"" select=""$doubles"" />
		|        </xsl:call-template>
		|      </xsl:for-each>
		|    </xsl:variable>
		|
		|    <!-- result -->
		|    <ValueTable xmlns=""http://v8.1c.ru/8.1/data/core"" xmlns:xs=""http://www.w3.org/2001/XMLSchema"" xsi:type=""ValueTable"">
		|      <column>
		|        <Name xsi:type=""xs:string"">Путь</Name>
		|        <ValueType>
		|           <Type>xs:string</Type>
		|           <StringQualifiers><Length>0</Length><AllowedLength>Variable</AllowedLength></StringQualifiers>
		|        </ValueType>
		|      </column>
		|      <column>
		|        <Name xsi:type=""xs:string"">Значение1</Name>
		|        <ValueType>
		|           <Type>xs:string</Type>
		|           <StringQualifiers><Length>0</Length><AllowedLength>Variable</AllowedLength></StringQualifiers>
		|        </ValueType>
		|      </column>
		|      <column>
		|        <Name xsi:type=""xs:string"">Значение2</Name>
		|        <ValueType>
		|           <Type>xs:string</Type>
		|           <StringQualifiers><Length>0</Length><AllowedLength>Variable</AllowedLength></StringQualifiers>
		|        </ValueType>
		|      </column>
		|
		|      <xsl:for-each select=""$first-second-nodeset/item | exsl:node-set($second-first)/item"">
		|        <xsl:element name=""row"">
		|           <xsl:element name=""Value"">
		|             <xsl:value-of select=""@path""/>
		|           </xsl:element>
		|           <xsl:element name=""Value"">
		|             <xsl:value-of select=""@value1""/>
		|           </xsl:element>
		|           <xsl:element name=""Value"">
		|             <xsl:value-of select=""@value2""/>
		|           </xsl:element>
		|        </xsl:element>
		|      </xsl:for-each>
		|
		|    </ValueTable>
		|
		|  </xsl:template>
		|  <!-- /main -->
		|
		|  <!-- compare sub -->
		|  <xsl:template name=""compare"">
		|    <xsl:param name=""check"" />
		|    <xsl:param name=""doubles"" select=""/.."" />
		|
		|    <xsl:variable name=""path""  select=""@path""/>
		|    <xsl:variable name=""value"" select=""@value""/>
		|    <xsl:variable name=""diff""  select=""$check[@path=$path]""/>
		|    <xsl:choose>
		|      <xsl:when test=""count($diff)=0"">
		|        <xsl:if test=""count($doubles[@path=$path and @value1='' and @value2=$value])=0"">
		|          <xsl:element name=""item"">
		|            <xsl:attribute name=""path"">   <xsl:value-of select=""$path""/> </xsl:attribute>
		|            <xsl:attribute name=""value1""> <xsl:value-of select=""$value""/> </xsl:attribute>
		|            <xsl:attribute name=""value2"" />
		|          </xsl:element>
		|        </xsl:if>
		|      </xsl:when>
		|      <xsl:otherwise>
		|
		|        <xsl:for-each select=""$diff[@value!=$value]"">
		|            <xsl:variable name=""diff-value"" select=""@value""/>
		|            <xsl:if test=""count($doubles[@path=$path and @value1=$diff-value and @value2=$value])=0"">
		|              <xsl:element name=""item"">
		|                <xsl:attribute name=""path"">   <xsl:value-of select=""$path""/>  </xsl:attribute>
		|                <xsl:attribute name=""value1""> <xsl:value-of select=""$value""/> </xsl:attribute>
		|                <xsl:attribute name=""value2""> <xsl:value-of select=""@value""/> </xsl:attribute>
		|              </xsl:element>
		|            </xsl:if>
		|        </xsl:for-each>
		|      </xsl:otherwise>
		|    </xsl:choose>
		|  </xsl:template>
		|
		|</xsl:stylesheet>
		|");

	Возврат Преобразователь;
КонецФункции


Функция ПодготовитьСтруктуруПараметровВывода() Экспорт

	ПараметрыВывода = Новый Структура;
	ПараметрыВывода.Вставить("ЗаголовокФормы", "");
	ПараметрыВывода.Вставить("ДоступнаПечатьПоКомплектно", Ложь); // не используется
	ПараметрыВывода.Вставить("КодЯзыка", ОбщегоНазначения.КодОсновногоЯзыка());

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

	Возврат ПараметрыВывода;

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

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

Процедура ВыполнитьКомандуПечатиВФайл(КомандаПечати, НастройкиСохранения, СписокОбъектов, Результат)

	Если Не ЗначениеЗаполнено(НастройкиСохранения.ФорматыСохранения) Тогда
		НастройкиСохранения.ФорматыСохранения.Добавить(СтандартныеПодсистемыСервер.ТипФайлаТабличногоДокументаPDF());
	КонецЕсли;

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

	КоллекцияПечатныхФорм = ДанныеПечати.КоллекцияПечатныхФорм;
	ОбъектыПечати = ДанныеПечати.ОбъектыПечати;

	ПодписиИПечатиОбластей = Неопределено;
	Если НастройкиСохранения.ПодписьИПечать Тогда
		ПодписиИПечатиОбластей = ПодписиИПечатиОбластей(ОбъектыПечати);
	КонецЕсли;

	ТаблицаФорматов = НастройкиФорматовСохраненияТабличногоДокумента();

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

		Если НастройкиСохранения.ПодписьИПечать Тогда
			ДобавитьПодписьИПечать(ПечатнаяФорма.ТабличныйДокумент, ПодписиИПечатиОбластей);
		Иначе
			УбратьПодписьИПечать(ПечатнаяФорма.ТабличныйДокумент);
		КонецЕсли;

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

			ОбъектПечати = СоответствиеОбъектаПечатнойФорме.Ключ;
			ТабличныйДокумент = СоответствиеОбъектаПечатнойФорме.Значение;

			Если ТабличныйДокумент.ВысотаТаблицы = 0 Тогда
				Продолжить;
			КонецЕсли;

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

				НастройкиФормата = ТаблицаФорматов.НайтиСтроки(Новый Структура("ТипФайлаТабличногоДокумента", ТипФайла))[0];

				РасширениеФайла = НастройкиФормата.Расширение;
				ЗаданныеИменаПечатныхФорм = ПечатнаяФорма.ИмяФайлаПечатнойФормы;
				НазваниеПечатнойФормы = ПечатнаяФорма.СинонимМакета;

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

				Файл = Результат.Добавить();
				Файл.ИмяФайла = ИмяФайла;
				Файл.ДвоичныеДанные = ТабличныйДокументВДвоичныеДанные(ТабличныйДокумент, ТипФайла);
			КонецЦикла;
		КонецЦикла;
	КонецЦикла;

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

Функция ИспользуемыйТипФайлаТабличногоДокумента(ТипФайла)

	Если ТипФайла = ТипФайлаТабличногоДокумента.PDF
		Или Строка(ТипФайла) = "PDF_A_1"
		Или Строка(ТипФайла) = "PDF_A_2"
		Или Строка(ТипФайла) = "PDF_A_3" Тогда
		Возврат СтандартныеПодсистемыСервер.ТипФайлаТабличногоДокументаPDF();
	КонецЕсли;

	ЗаменяемыеТипы = Новый Соответствие;
	ЗаменяемыеТипы.Вставить(ТипФайлаТабличногоДокумента.HTML, ТипФайлаТабличногоДокумента.HTML5);
	ЗаменяемыеТипы.Вставить(ТипФайлаТабличногоДокумента.HTML3, ТипФайлаТабличногоДокумента.HTML5);
	ЗаменяемыеТипы.Вставить(ТипФайлаТабличногоДокумента.HTML4, ТипФайлаТабличногоДокумента.HTML5);
	ЗаменяемыеТипы.Вставить(ТипФайлаТабличногоДокумента.MXL7, ТипФайлаТабличногоДокумента.MXL);
	ЗаменяемыеТипы.Вставить(ТипФайлаТабличногоДокумента.XLS95, ТипФайлаТабличногоДокумента.XLS);
	ЗаменяемыеТипы.Вставить(ТипФайлаТабличногоДокумента.XLS97, ТипФайлаТабличногоДокумента.XLS);

	Результат = ЗаменяемыеТипы[ТипФайла];
	Если Результат = Неопределено Тогда
		Результат = ТипФайла;
	КонецЕсли;

	Возврат Результат;

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

Функция МакетыПечатныхФорм(ТолькоОбработкиИОтчеты = Ложь) Экспорт

	Результат = Новый Соответствие;

	КоллекцииОбъектовМетаданных = Новый Массив; // Массив из КоллекцияОбъектовМетаданных -
	КоллекцииОбъектовМетаданных.Добавить(Метаданные.Обработки);
	КоллекцииОбъектовМетаданных.Добавить(Метаданные.Отчеты);
	Если Не ТолькоОбработкиИОтчеты Тогда
		КоллекцииОбъектовМетаданных.Добавить(Метаданные.Справочники);
		КоллекцииОбъектовМетаданных.Добавить(Метаданные.Документы);
		КоллекцииОбъектовМетаданных.Добавить(Метаданные.БизнесПроцессы);
		КоллекцииОбъектовМетаданных.Добавить(Метаданные.Задачи);
		КоллекцииОбъектовМетаданных.Добавить(Метаданные.ЖурналыДокументов);
	КонецЕсли;

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

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

	Возврат Результат;

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

// Возвращаемое значение:
//  ТабличныйДокумент, ДвоичныеДанные - макет.
//