Загрузка данных


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

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

// АПК:142-выкл - Необязательных параметров более 3 для поддержки разных прикладных функций.
// АПК:134-выкл - Количество параметров более 7 для поддержки разных прикладных функций.

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

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

// Запустить выполнение процедуры в фоновом задании, если это возможно.
// Вызываемая процедура может быть с произвольным числом параметров, но не более 7.
// Значения передаваемых параметров процедуры, а также возвращаемое значение должны быть сериализуемыми.
// Параметры процедуры не должны быть возвращаемыми.
//
// Рекомендуется применять совместно с функцией ДлительныеОперацииКлиент.ОжидатьЗавершение,
// если невозможно, тогда для проверки завершения операции использовать функцию ЗаданиеВыполнено.
//
// Параметры:
//
//  ПараметрыВыполнения - см. ДлительныеОперации.ПараметрыВыполненияПроцедуры
//
//  ИмяПроцедуры - Строка - имя экспортной процедуры общего модуля, модуля менеджера объекта 
//                          или модуля обработки, которую необходимо выполнить в фоне.
//                          Например, "МойОбщийМодуль.МояПроцедура", "Отчет.ЗагруженныеДанные.Сформировать"
//                          или "Обработка.ЗагрузкаДанных.МодульОбъекта.Загрузить". 
//
//  Параметр1 - Произвольный - произвольные параметры вызова процедуры. Количество параметров может быть от 0 до 7.
//  Параметр2 - Произвольный
//  Параметр3 - Произвольный
//  Параметр4 - Произвольный
//  Параметр5 - Произвольный
//  Параметр6 - Произвольный
//  Параметр7 - Произвольный
//
// Возвращаемое значение:
//  Структура - параметры выполнения задания: 
//   * Статус               - Строка - "Выполняется", если задание еще не завершилось;
//                                     "Выполнено", если задание было успешно выполнено;
//                                     "Ошибка", если задание завершено с ошибкой;
//                                     "Отменено", если задание отменено пользователем или администратором.
//   * ИдентификаторЗадания - УникальныйИдентификатор - если Статус = "Выполняется", то содержит 
//                                     идентификатор запущенного фонового задания.
//                          - Неопределено - если Статус <> "Выполняется" и фоновое задание не запускалось.
//   * ИнформацияОбОшибке    - ИнформацияОбОшибке - если Статус = "Ошибка".
//                           - Неопределено - если Статус <> "Ошибка".
//   * Сообщения - ФиксированныйМассив - если Статус <> "Выполняется", то массив объектов СообщениеПользователю,
//                                      которые были сформированы в фоновом задании.
//   * КраткоеПредставлениеОшибки   - Строка - устарел.
//   * ПодробноеПредставлениеОшибки - Строка - устарел.
//
// Пример:
//  В общем виде процесс запуска и обработки результата длительной операции в модуле формы выглядит следующим образом:
//
//   1) Процедура, которая будет исполняться в фоне, располагается в модуле менеджера объекта или в серверном общем модуле:
//    Процедура ВыполнитьРасчет(Знач МойПараметр1, Знач МойПараметр2) Экспорт
//     ...
//    КонецПроцедуры
//
//   2) Запуск операции на сервере и подключение обработчика ожидания (при необходимости):
//    &НаКлиенте
//    Процедура ВыполнитьРасчет()
//     ДлительнаяОперация = НачатьВыполнениеНаСервере();
//     ОповещениеОЗавершении = Новый ОписаниеОповещения("ОбработатьРезультат", ЭтотОбъект);
//     ПараметрыОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтотОбъект);
//     ДлительныеОперацииКлиент.ОжидатьЗавершение(ДлительнаяОперация, ОповещениеОЗавершении, ПараметрыОжидания);
//    КонецПроцедуры
//
//    &НаСервере
//    Функция НачатьВыполнениеНаСервере()
//     Возврат ДлительныеОперации.ВыполнитьПроцедуру(, "Обработка.МояОбработка.ВыполнитьРасчет", 
//      МойПараметр1, МойПараметр2);
//    КонецФункции
//    
//   3) Обработка результата длительной операции:
//    &НаКлиенте
//    Процедура ОбработатьРезультат(Результат, ДополнительныеПараметры) Экспорт
//     Если Результат = Неопределено Тогда
//      Возврат;
//     КонецЕсли;
//     Если Результат.Статус = "Ошибка" Тогда
//      СтандартныеПодсистемыКлиент.ВывестиИнформациюОбОшибке(Результат.ИнформацияОбОшибке);
//      Возврат;
//     КонецЕсли;
//     ПриЗавершенииРасчета();
//    КонецПроцедуры 
//   
//@skip-check method-optional-parameter-before-required
//
Функция ВыполнитьПроцедуру(Знач ПараметрыВыполнения = Неопределено, ИмяПроцедуры, Знач Параметр1 = Неопределено,
	Знач Параметр2 = Неопределено, Знач Параметр3 = Неопределено, Знач Параметр4 = Неопределено,
	Знач Параметр5 = Неопределено, Знач Параметр6 = Неопределено, Знач Параметр7 = Неопределено) Экспорт
	
	ПроверитьИмяПроцедуры(ИмяПроцедуры,
		"ИмяПроцедуры", "ДлительныеОперации.ВыполнитьФункцию", ПараметрыВыполнения);
	
	ПараметрыВызова = СписокПараметров(Параметр1, Параметр2, Параметр3, Параметр4,
		Параметр5, Параметр6, Параметр7);
	ПараметрыВыполнения = ПодготовитьПараметрыВыполнения(ПараметрыВыполнения, "Процедура");
	Возврат ВыполнитьВФоне(ИмяПроцедуры, ПараметрыВызова, ПараметрыВыполнения);
	
КонецФункции

// АПК:141-вкл
// АПК:134-вкл
// АПК:142-вкл

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

#Область ОбработчикиСобытийПодсистемКонфигурации

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

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

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

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

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

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

// См. СтандартныеПодсистемыСервер.ПриОтправкеСерверногоОповещения
Процедура ПриОтправкеСерверногоОповещения(ИмяОповещения, ВариантыПараметров) Экспорт
	
	Если ИмяОповещения <> ИмяДополнительногоОповещения() Тогда
		Возврат;
	КонецЕсли;
	
	УдалитьНесуществующиеПотоки();
	
КонецПроцедуры

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

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

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

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

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

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

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

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

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

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

// Для функции ВыполнитьВФоне.
Функция ЭтоОшибкаЗаданиеСТакимЗначениемКлючаУжеВыполняется(Задание, Результат, ПараметрыВыполнения)
	
	Если Задание <> Неопределено
	 Или Не ЗначениеЗаполнено(ПараметрыВыполнения.КлючФоновогоЗадания) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	КраткоеПредставлениеОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(Результат.ИнформацияОбОшибке);
	// АПК:1405-выкл - требуется для быстрого определения причины ошибки
	СтрокаПоиска = НСтр("ru = 'Задание с таким значением ключа уже выполняется';
						|en = 'A job with the same key value is performed already'");
	// АПК:1405-вкл
	Если ЗначениеЗаполнено(СтрокаПоиска) И СтрНачинаетсяС(КраткоеПредставлениеОшибки, СтрокаПоиска) Тогда
		Возврат Истина;
	КонецЕсли;
	
	Отбор = Новый Структура;
	Отбор.Вставить("ИмяМетода", ИмяПроцедурыФоновогоЗаданияДлительнойОперации());
	Отбор.Вставить("Ключ",      ПараметрыВыполнения.КлючФоновогоЗадания);
	Отбор.Вставить("Состояние", СостояниеФоновогоЗадания.Активно);
	
	НайденныеЗадания = ФоновыеЗадания.ПолучитьФоновыеЗадания(Отбор);
	Возврат НайденныеЗадания.Количество() > 1;
	
КонецФункции

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

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

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

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

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

Процедура ВызватьПроцедуру(ИмяПроцедуры, ПараметрыВызова, ПараметрыВыполнения)
	
	ЧастиИмени = СтрРазделить(ИмяПроцедуры, ".");
	ЭтоПроцедураМодуляОбработки = (ЧастиИмени.Количество() = 4) И ВРег(ЧастиИмени[2]) = "МОДУЛЬОБЪЕКТА";
	Если Не ЭтоПроцедураМодуляОбработки Тогда
		ПроверитьИмяПроцедуры(ИмяПроцедуры, "ИмяПроцедуры", "ВызватьПроцедуру");
		ОбщегоНазначения.ВыполнитьМетодКонфигурации(ИмяПроцедуры, ПараметрыВызова);
		Возврат;
	КонецЕсли;
	
	ОбработкаОтчетОбъект = ОбработкаОтчетОбъект(ИмяПроцедуры,
		ЧастиИмени, ПараметрыВыполнения, "ИмяПроцедуры", "ВызватьПроцедуру");
	
	ПроверитьИмяПроцедуры(ЧастиИмени[3], "ИмяПроцедуры", "ВызватьПроцедуру",, ОбработкаОтчетОбъект);
	
	ОбщегоНазначения.ВыполнитьМетодОбъекта(ОбработкаОтчетОбъект, ЧастиИмени[3], ПараметрыВызова);
	
КонецПроцедуры

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

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

Процедура ВызватьФункцию(ИмяФункции, ПараметрыПроцедуры, ПараметрыВыполнения)
	
	ЧастиИмени = СтрРазделить(ИмяФункции, ".");
	ЭтоПроцедураМодуляОбработки = (ЧастиИмени.Количество() = 4) И ВРег(ЧастиИмени[2]) = "МОДУЛЬОБЪЕКТА";
	Если Не ЭтоПроцедураМодуляОбработки Тогда
		ПроверитьИмяПроцедуры(ИмяФункции, "ИмяФункции", "ВызватьФункцию");
		Результат = ОбщегоНазначения.ВыполнитьМетодКонфигурации(ИмяФункции, ПараметрыПроцедуры, Истина);
		УстановитьРезультатВызоваФункции(Результат, ПараметрыВыполнения);
		Возврат;
	КонецЕсли;
	
	ОбработкаОтчетОбъект = ОбработкаОтчетОбъект(ИмяФункции,
		ЧастиИмени, ПараметрыВыполнения, "ИмяФункции", "ВызватьФункцию");
	
	ПроверитьИмяПроцедуры(ЧастиИмени[3], "ИмяФункции", "ВызватьФункцию",, ОбработкаОтчетОбъект);
	
	Результат = ОбщегоНазначения.ВыполнитьМетодОбъекта(ОбработкаОтчетОбъект, ЧастиИмени[3], ПараметрыПроцедуры, Истина);
	УстановитьРезультатВызоваФункции(Результат, ПараметрыВыполнения);
	
КонецПроцедуры

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

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

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

Функция ИмяОповещения()
	Возврат "СтандартныеПодсистемы.БазоваяФункциональность.ДлительныеОперации";
КонецФункции

Функция ИмяДополнительногоОповещения()
	Возврат "СтандартныеПодсистемы.БазоваяФункциональность.ДлительныеОперации.УдалениеНесуществующихПотоков";
КонецФункции

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

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

// Для процедуры ОтправитьОповещениеКлиенту и функции ПолучитьИзОповещений.
Функция ИдентификаторВидаОповещения(ВидОповещения)
	
	Если ВидОповещения = "СообщениеПользователю" Или ВидОповещения = "Сообщения" Тогда
		Возврат Новый УникальныйИдентификатор("0afef160-bfcb-459e-a890-a4afbb73b7ba");
	
	ИначеЕсли ВидОповещения = "Прогресс" Тогда
		Возврат Новый УникальныйИдентификатор("14076bb1-a1f5-4876-975a-3b7f69383f6c");
		
	ИначеЕсли ВидОповещения = "ДлительнаяОперацияЗавершена" Тогда
		Возврат Новый УникальныйИдентификатор("28e5ab5c-196b-44be-aab5-8fe7edb5225b");
	КонецЕсли;
	
	ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Неизвестный вид оповещения длительной операции ""%1"".'"), ВидОповещения);
	ВызватьИсключение(ТекстОшибки, КатегорияОшибки.ОшибкаКонфигурации);
	
КонецФункции

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

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

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

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

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

Функция ВозможноВыполнитьВФоне(ИмяПроцедуры)
	
	ЧастиИмени = СтрРазделить(ИмяПроцедуры, ".");
	Если ЧастиИмени.Количество() = 0 Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

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

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

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

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

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

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

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

Процедура ПроверитьПараметрыВыполнения(ИмяПроцедуры, ПараметрыВыполнения)

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

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

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

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

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

Процедура ДобавитьПараметрыВыполненияДляВозвратаРезультата(Параметры, ИдентификаторФормы)
	
	Параметры.Вставить("ИдентификаторФормы", ИдентификаторФормы); 
	Параметры.Вставить("АдресРезультата", Неопределено);
	
КонецПроцедуры

// Многопоточные операции

Функция ИмяМетодаМногопоточногоПроцесса()
	Возврат "ДлительныеОперации.ВыполнитьМногопоточныйПроцесс";
КонецФункции

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

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

	ИдентификаторПроцесса = ПараметрыОперации.ИдентификаторПроцесса;
	ПрерватьВыполнениеЕслиОшибка = ПараметрыОперации.ПараметрыВыполнения.ПрерватьВыполнениеЕслиОшибка;

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

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

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

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

Функция ПоследнийИдентификаторЗаданияПотока(Поток)
	
	ПоследнийИдентификатор = Поток.ИдентификаторЗадания;
	
	Если ЗначениеЗаполнено(Поток.ИдентификаторПотока) Тогда
		Возврат ПоследнийИдентификатор;
	КонецЕсли;
	
	ИдентификаторЗадания = Поток.КлючПотока.Получить();
	
	Если ТипЗнч(ИдентификаторЗадания) = Тип("УникальныйИдентификатор") Тогда
		ПоследнийИдентификатор = ИдентификаторЗадания;
	КонецЕсли;
	
	Возврат ПоследнийИдентификатор;
	
КонецФункции

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Функция КоличествоПопыток()
	Возврат 3;
КонецФункции

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

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

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

Процедура УдалитьДанныеОПотоках(ИдентификаторПроцесса)
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	НаборЗаписей = РегистрыСведений.ДлительныеОперации.СоздатьНаборЗаписей();
	НаборЗаписей.Отбор.ИдентификаторПроцесса.Установить(ИдентификаторПроцесса);
	
	НаборЗаписей.Записать();
	
КонецПроцедуры

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

#Область ЖурналРегистрации

Процедура ЗаписатьОшибку(Знач Текст)
	
	ЖурналРегистрации.ДобавитьСообщениеДляЖурналаРегистрации(СобытиеЖурналаРегистрации(), УровеньЖурналаРегистрации.Ошибка,,, Текст);
	
КонецПроцедуры

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

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

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

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

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