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