Загрузка данных
///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2025, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////
#Область ПрограммныйИнтерфейс
// Открыть стандартную форму ожидания завершения длительной операции или использовать собственную форму
// и подключить обработчик оповещения о завершении и прогрессе выполнения процедуры длительной операции.
//
// Применяется совместно с функцией ДлительныеОперации.ВыполнитьВФоне для повышения отзывчивости
// пользовательского интерфейса, заменяя длительный серверный вызов на запуск фонового задания.
//
// Параметры:
// ДлительнаяОперация - см. ДлительныеОперации.ВыполнитьВФоне
// ОповещениеОЗавершении - ОписаниеОповещения - оповещение, которое вызывается после завершения
// длительной операции, в том числе после закрытия окна ожидания, если открывалось.
// Параметры процедуры-обработчика оповещения:
// # Результат - см. НовыйРезультатДлительнойОперации
// - Неопределено - если задание было отменено
// # ДополнительныеПараметры - Произвольный - произвольные данные, переданные в описании оповещения.
// ПараметрыОжидания - см. ДлительныеОперацииКлиент.ПараметрыОжидания
//
Процедура ОжидатьЗавершение(Знач ДлительнаяОперация, Знач ОповещениеОЗавершении = Неопределено,
Знач ПараметрыОжидания = Неопределено) Экспорт
ПроверитьПараметрыОжидатьЗавершение(ДлительнаяОперация, ОповещениеОЗавершении, ПараметрыОжидания);
РасширенныеПараметры = ПараметрыОжидания(Неопределено);
Если ПараметрыОжидания <> Неопределено Тогда
ЗаполнитьЗначенияСвойств(РасширенныеПараметры, ПараметрыОжидания);
КонецЕсли;
Если ДлительнаяОперация.Свойство("АдресРезультата") Тогда
РасширенныеПараметры.Вставить("АдресРезультата", ДлительнаяОперация.АдресРезультата);
КонецЕсли;
Если ДлительнаяОперация.Свойство("АдресДополнительногоРезультата") Тогда
РасширенныеПараметры.Вставить("АдресДополнительногоРезультата", ДлительнаяОперация.АдресДополнительногоРезультата);
КонецЕсли;
РасширенныеПараметры.Вставить("ИдентификаторЗадания", ДлительнаяОперация.ИдентификаторЗадания);
Если ДлительнаяОперация.Статус <> "Выполняется" Тогда
РасширенныеПараметры.Вставить("НакопленныеСообщения", Новый Массив);
РасширенныеПараметры.Вставить("ОповещениеОЗавершении", ОповещениеОЗавершении);
ОбработатьНакопленныеСерверныеОповещения(РасширенныеПараметры.ИдентификаторЗадания, Ложь);
Если РасширенныеПараметры.ВыводитьОкноОжидания Тогда
ОбработатьСообщенияПользователю(ДлительнаяОперация.Сообщения,
РасширенныеПараметры.НакопленныеСообщения,
РасширенныеПараметры.ВыводитьСообщения,
РасширенныеПараметры.ФормаВладелец);
ЗавершитьДлительнуюОперацию(РасширенныеПараметры, ДлительнаяОперация);
Иначе
Операция = Новый Структура(Новый ФиксированнаяСтруктура(ДлительнаяОперация));
Операция.Вставить("Прогресс");
Операция.Вставить("ФоновоеЗаданиеЗавершено");
ОбработатьРезультатАктивнойОперации(РасширенныеПараметры, Операция);
КонецЕсли;
Возврат;
КонецЕсли;
Если РасширенныеПараметры.ВыводитьОкноОжидания Тогда
РасширенныеПараметры.Удалить("ФормаВладелец");
Контекст = Новый Структура;
Контекст.Вставить("Результат");
Контекст.Вставить("ИдентификаторЗадания", РасширенныеПараметры.ИдентификаторЗадания);
Контекст.Вставить("ОповещениеОЗавершении", ОповещениеОЗавершении);
ОповещениеОЗакрытии = Новый ОписаниеОповещения("ПриЗакрытииФормыДлительнаяОперация",
ЭтотОбъект, Контекст);
ОткрытьФорму("ОбщаяФорма.ДлительнаяОперация", РасширенныеПараметры,
?(ПараметрыОжидания <> Неопределено, ПараметрыОжидания.ФормаВладелец, Неопределено),
,,,ОповещениеОЗакрытии, РасширенныеПараметры.РежимОткрытияОкнаОжидания);
Иначе
РасширенныеПараметры.Вставить("НакопленныеСообщения", Новый Массив);
РасширенныеПараметры.Вставить("ОповещениеОЗавершении", ОповещениеОЗавершении);
РасширенныеПараметры.Вставить("ТекущийИнтервал", ?(РасширенныеПараметры.Интервал <> 0, РасширенныеПараметры.Интервал, 1));
РасширенныеПараметры.Вставить("Контроль", ТекущаяДата() + РасширенныеПараметры.ТекущийИнтервал); // АПК:143 Дата сеанса не используется для проверки интервалов
РасширенныеПараметры.Вставить("ВремяОтправкиПоследнегоПрогресса", 0);
РасширенныеПараметры.Вставить("ПолученоОповещениеОЗавершении", Ложь);
Операции = АктивныеДлительныеОперации();
Операции.Список.Вставить(РасширенныеПараметры.ИдентификаторЗадания, РасширенныеПараметры);
СерверныеОповещенияКлиент.ПодключитьОбработчикПроверкиПолученияСерверныхОповещений();
ОбработатьНакопленныеСерверныеОповещения(РасширенныеПараметры.ИдентификаторЗадания);
КонецЕсли;
КонецПроцедуры
// Возвращает пустую структуру для параметра ПараметрыОжидания процедуры ДлительныеОперацииКлиент.ОжидатьЗавершение.
//
// Параметры:
// ФормаВладелец - ФормаКлиентскогоПриложения
// - Неопределено - форма, из которой вызывается длительная операция.
//
// Возвращаемое значение:
// Структура - параметры выполнения задания:
// * ФормаВладелец - ФормаКлиентскогоПриложения
// - Неопределено - форма, из которой вызывается длительная операция.
// * Заголовок - Строка - заголовок окна, выводимый на форме ожидания. Если не задан, то не выводится.
// * ТекстСообщения - Строка - текст сообщения, выводимый на форме ожидания.
// Если не задан, то выводится "Пожалуйста, подождите...".
// * ВыводитьОкноОжидания - Булево - если Истина, то открыть окно ожидания с визуальной индикацией длительной операции.
// Если используется собственный механизм индикации, то следует указать Ложь.
// * РежимОткрытияОкнаОжидания - РежимОткрытияОкнаФормы - параметр РежимОткрытияОкна у формы ожидания.
// - Неопределено - значение по умолчанию.
// * ВыводитьПрогрессВыполнения - Булево - выводить прогресс выполнения в процентах на форме ожидания.
// Процедура-обработчик длительной операции может сообщить о ходе своего выполнения
// с помощью вызова процедуры ДлительныеОперации.СообщитьПрогресс.
// * ВыводитьСообщения - Булево - из формы ожидания выводить в ФормаВладелец сообщения,
// сформированные в процедуре-обработчике длительной операции.
// * ЗаголовокКнопкиОтмена - Строка - заголовок кнопки Отмена, если не указано, по умолчанию используется "Отмена"
// * ОповещениеОПрогрессеВыполнения - ОписаниеОповещения - оповещение, которое периодически вызывается при
// проверке готовности фонового задания, если ВыводитьОкноОжидания = Ложь.
// Параметры процедуры-обработчика оповещения:
// # Результат - см. НовоеСостояниеДлительнойОперации
// # ДополнительныеПараметры - Произвольный - произвольные данные, переданные в описании оповещения.
//
// * Интервал - Число - интервал в секундах между проверками готовности длительной операции.
// По умолчанию 0 - после каждой проверки интервал увеличивается с 1 до 15 секунд
// с коэффициентом 1.4.
// * ОповещениеПользователя - Структура:
// ** Показать - Булево - если Истина, то по завершении длительной операции вывести оповещение пользователя.
// ** Текст - Строка - текст оповещения пользователя.
// ** НавигационнаяСсылка - Строка - навигационная ссылка оповещения пользователя.
// ** Пояснение - Строка - пояснение оповещения пользователя.
// ** Картинка - Картинка - картинка, которая будет показана в окне оповещения. Если Неопределено, то
// картинка не выводится.
// ** Важное - Булево - если Истина, то оповещение после автоматического закрытия будет добавлено в
// центр оповещений.
//
// * ОтменятьПриЗакрытииФормыВладельца - Булево - по умолчанию ФормаВладелец.Открыта(), Ложь если форма не указана.
// Если Ложь, тогда длительная операция не будет отменена при закрытии формы владельца и при закрытии окна ожидания.
//
// * ПолучатьРезультат - Булево - служебный параметр. Не предназначен для использования.
//
Функция ПараметрыОжидания(ФормаВладелец) Экспорт
Результат = Новый Структура;
Результат.Вставить("ФормаВладелец", ФормаВладелец);
Результат.Вставить("ТекстСообщения", "");
Результат.Вставить("Заголовок", "");
Результат.Вставить("НомерПопытки", 1);
Результат.Вставить("ВыводитьОкноОжидания", Истина);
Результат.Вставить("РежимОткрытияОкнаОжидания", Неопределено);
Результат.Вставить("ВыводитьПрогрессВыполнения", Ложь);
Результат.Вставить("ОповещениеОПрогрессеВыполнения", Неопределено);
Результат.Вставить("ВыводитьСообщения", Ложь);
Результат.Вставить("ЗаголовокКнопкиОтмена", "");
Результат.Вставить("Интервал", 0);
Результат.Вставить("ПолучатьРезультат", Ложь);
Результат.Вставить("ОтменятьПриЗакрытииФормыВладельца",
ТипЗнч(ФормаВладелец) = Тип("ФормаКлиентскогоПриложения") И ФормаВладелец.Открыта());
ОповещениеПользователя = Новый Структура;
ОповещениеПользователя.Вставить("Показать", Ложь);
ОповещениеПользователя.Вставить("Текст", Неопределено);
ОповещениеПользователя.Вставить("НавигационнаяСсылка", Неопределено);
ОповещениеПользователя.Вставить("Пояснение", Неопределено);
ОповещениеПользователя.Вставить("Картинка", Неопределено);
ОповещениеПользователя.Вставить("Важное", Неопределено);
Результат.Вставить("ОповещениеПользователя", ОповещениеПользователя);
Возврат Результат;
КонецФункции
// Возвращает результат оповещения, указанного в параметре ОповещениеОЗавершении
// процедуры ДлительныеОперацииКлиент.ОжидатьЗавершение.
//
// Возвращаемое значение:
// Неопределено - передается в результат ОповещениеОЗавершении, если задание отменено пользователем.
// Структура:
// * Статус - Строка - "Выполнено", если задание было успешно выполнено.
// "Ошибка", если задание завершено с ошибкой.
//
// * АдресРезультата - Строка - адрес временного хранилища, в которое будет помещен
// (или уже помещен) результат работы процедуры длительной операции.
//
// * АдресДополнительногоРезультата - Строка - если установлен параметр ДополнительныйРезультат,
// содержит адрес дополнительного временного хранилища, в которое будет помещен
// (или уже помещен) дополнительный результат работы процедуры длительной операции.
//
// * ИнформацияОбОшибке - ИнформацияОбОшибке - если Статус = "Ошибка".
// - Неопределено - если Статус <> "Ошибка".
//
// * Сообщения - ФиксированныйМассив из СообщениеПользователю - сообщения,
// сформированные в процедуре-обработчике длительной операции.
// Массив будет пустым, когда в параметре ПараметрыОжидания
// процедуры процедуры ДлительныеОперацииКлиент.ОжидатьЗавершение
// свойство ВыводитьОкноОжидания = Ложь и
// заполнено свойство ОповещениеОПрогрессеВыполнения.
//
// * ИдентификаторЗадания - УникальныйИдентификатор - идентификатор фонового задания, если запускалось.
// - Неопределено - если задание не запускалось (например, выполнение не в фоне).
//
// * КраткоеПредставлениеОшибки - Строка - устарел.
// * ПодробноеПредставлениеОшибки - Строка - устарел.
//
Функция НовыйРезультатДлительнойОперации() Экспорт
Результат = Новый Структура;
Результат.Вставить("Статус", "");
Результат.Вставить("АдресРезультата", "");
Результат.Вставить("АдресДополнительногоРезультата", "");
Результат.Вставить("ИнформацияОбОшибке", Неопределено);
Результат.Вставить("Сообщения", Новый ФиксированныйМассив(Новый Массив));
Результат.Вставить("ИдентификаторЗадания", Неопределено);
Результат.Вставить("КраткоеПредставлениеОшибки", "");
Результат.Вставить("ПодробноеПредставлениеОшибки", "");
Возврат Результат;
КонецФункции
// Возвращает пустую структуру для передачи как результат оповещения, указанного
// в свойстве ОповещениеОПрогрессеВыполнения параметра ПараметрыОжидания
// процедуры ДлительныеОперацииКлиент.ОжидатьЗавершение.
//
// Возвращаемое значение:
// Структура:
// * Статус - Строка - "Выполняется", если задание еще не завершилось,
// "Выполнено", если задание было успешно выполнено,
// "Ошибка", если задание завершено с ошибкой.
//
// * Прогресс - см. ДлительныеОперации.ПрочитатьПрогресс
// * Сообщения - Неопределено - нет сообщений.
// - ФиксированныйМассив из СообщениеПользователю - порция сообщений,
// отправленных из процедуры длительной операции.
//
// * ИдентификаторЗадания - УникальныйИдентификатор - идентификатор фонового задания, если запускалось.
// - Неопределено - если задание не запускалось (например, выполнение не в фоне).
//
Функция НовоеСостояниеДлительнойОперации() Экспорт
Результат = Новый Структура;
Результат.Вставить("Статус", "");
Результат.Вставить("Прогресс", Неопределено);
Результат.Вставить("Сообщения", Неопределено);
Результат.Вставить("ИдентификаторЗадания", Неопределено);
Возврат Результат;
КонецФункции
#Область УстаревшиеПроцедурыИФункции
// Устарела. Следует использовать ОжидатьЗавершение с параметром ПараметрыОжидания.ВыводитьОкноОжидания = Истина.
// Заполняет структуру параметров значениями по умолчанию.
//
// Параметры:
// ПараметрыОбработчикаОжидания - Структура - заполняется значениями по умолчанию.
//
//
Процедура ИнициализироватьПараметрыОбработчикаОжидания(ПараметрыОбработчикаОжидания) Экспорт
ПараметрыОбработчикаОжидания = Новый Структура;
ПараметрыОбработчикаОжидания.Вставить("МинимальныйИнтервал", 1);
ПараметрыОбработчикаОжидания.Вставить("МаксимальныйИнтервал", 15);
ПараметрыОбработчикаОжидания.Вставить("ТекущийИнтервал", 1);
ПараметрыОбработчикаОжидания.Вставить("КоэффициентУвеличенияИнтервала", 1.4);
КонецПроцедуры
// Устарела. Следует использовать ОжидатьЗавершение с параметром ПараметрыОжидания.ВыводитьОкноОжидания = Истина.
// Заполняет структуру параметров новыми расчетными значениями.
//
// Параметры:
// ПараметрыОбработчикаОжидания - Структура - заполняется расчетными значениями.
//
//
Процедура ОбновитьПараметрыОбработчикаОжидания(ПараметрыОбработчикаОжидания) Экспорт
ПараметрыОбработчикаОжидания.ТекущийИнтервал = ПараметрыОбработчикаОжидания.ТекущийИнтервал * ПараметрыОбработчикаОжидания.КоэффициентУвеличенияИнтервала;
Если ПараметрыОбработчикаОжидания.ТекущийИнтервал > ПараметрыОбработчикаОжидания.МаксимальныйИнтервал Тогда
ПараметрыОбработчикаОжидания.ТекущийИнтервал = ПараметрыОбработчикаОжидания.МаксимальныйИнтервал;
КонецЕсли;
КонецПроцедуры
// Устарела. Следует использовать ОжидатьЗавершение с параметром ПараметрыОжидания.ВыводитьОкноОжидания = Истина.
// Открывает форму-индикатор длительной операции.
//
// Параметры:
// ВладелецФормы - ФормаКлиентскогоПриложения - форма, из которой производится открытие.
// ИдентификаторЗадания - УникальныйИдентификатор - идентификатор фонового задания.
//
// Возвращаемое значение:
// ФормаКлиентскогоПриложения - ссылка на открытую форму.
//
Функция ОткрытьФормуДлительнойОперации(Знач ВладелецФормы, Знач ИдентификаторЗадания) Экспорт
Возврат ОткрытьФорму("ОбщаяФорма.ДлительнаяОперация",
Новый Структура("ИдентификаторЗадания", ИдентификаторЗадания),
ВладелецФормы);
КонецФункции
// Устарела. Следует использовать ОжидатьЗавершение с параметром ПараметрыОжидания.ВыводитьОкноОжидания = Истина.
// Закрывает форму-индикатор длительной операции.
//
// Параметры:
// ФормаДлительнойОперации - ФормаКлиентскогоПриложения - ссылка на форму-индикатор длительной операции.
//
Процедура ЗакрытьФормуДлительнойОперации(ФормаДлительнойОперации) Экспорт
Если ТипЗнч(ФормаДлительнойОперации) = Тип("ФормаКлиентскогоПриложения") Тогда
Если ФормаДлительнойОперации.Открыта() Тогда
ФормаДлительнойОперации.Закрыть();
КонецЕсли;
КонецЕсли;
ФормаДлительнойОперации = Неопределено;
КонецПроцедуры
#КонецОбласти
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
////////////////////////////////////////////////////////////////////////////////
// Обработчики событий подсистем конфигурации.
// Параметры:
// Параметры - см. ОбщегоНазначенияПереопределяемый.ПередПериодическойОтправкойДанныхКлиентаНаСервер.Параметры
// ОбсужденияАктивны - Булево - для доставки сообщений используется Система взаимодействия.
// Интервал - Число - уточняемое значение - число секунд до следующей проверки.
//
Процедура ПередПериодическойОтправкойДанныхКлиентаНаСервер(Параметры, ОбсужденияАктивны, Интервал) Экспорт
Результат = ПараметрыПроверкиДлительныхОпераций(ОбсужденияАктивны, Интервал);
Если Результат = Неопределено Тогда
Возврат;
КонецЕсли;
Параметры.Вставить("СтандартныеПодсистемы.БазоваяФункциональность.ПараметрыПроверкиДлительныхОпераций", Результат)
КонецПроцедуры
// Параметры:
// Результаты - см. ОбщегоНазначенияПереопределяемый.ПриПериодическомПолученииДанныхКлиентаНаСервере.Результаты
// ОбсужденияАктивны - Булево - для доставки сообщений используется Система взаимодействия.
// Интервал - Число - уточняемое значение - число секунд до следующей проверки.
//
Процедура ПослеПериодическогоПолученияДанныхКлиентаНаСервере(Результаты, ОбсужденияАктивны, Интервал) Экспорт
РезультатыОпераций = Результаты.Получить( // см. ДлительныеОперации.РезультатПроверкиДлительныхОпераций
"СтандартныеПодсистемы.БазоваяФункциональность.РезультатПроверкиДлительныхОпераций");
Если РезультатыОпераций = Неопределено Тогда
Возврат;
КонецЕсли;
ТекущиеДлительныеОперации = АктивныеДлительныеОперации();
АктивныеДлительныеОперации = ТекущиеДлительныеОперации.Список;
КонтролируемыеОперации = ТекущиеДлительныеОперации.КонтролируемыеОперации;
Для Каждого РезультатОперации Из РезультатыОпераций Цикл
Операция = КонтролируемыеОперации[РезультатОперации.Ключ];
Результат = РезультатОперации.Значение; // Структура
Результат.Вставить("ФоновоеЗаданиеЗавершено");
Результат.Вставить("КонтрольДлительныхОперацийБезСистемыВзаимодействия");
ОбработатьРезультатОперации(АктивныеДлительныеОперации, Операция, Результат);
КонецЦикла;
ТекущиеДлительныеОперации.КонтролируемыеОперации = Новый Соответствие;
Если АктивныеДлительныеОперации.Количество() = 0 Тогда
Возврат;
КонецЕсли;
УточнитьИнтервалОбработчикаОжидания(Интервал, АктивныеДлительныеОперации, ОбсужденияАктивны);
КонецПроцедуры
// Параметры:
// Результат - Неопределено
// Контекст - Структура:
// * Результат - Структура
// - Неопределено
// * ИдентификаторЗадания - УникальныйИдентификатор
// - Неопределено
// * ОповещениеОЗавершении - ОписаниеОповещения
// - Неопределено
//
Процедура ПриЗакрытииФормыДлительнаяОперация(Результат, Контекст) Экспорт
Если Контекст.ОповещениеОЗавершении = Неопределено
Или Контекст.Результат <> Неопределено
И Контекст.Результат.Статус = "Выполняется" Тогда
Возврат;
КонецЕсли;
ОповеститьОЗавершенииДлительнойОперации(Контекст.ОповещениеОЗавершении,
Контекст.Результат, Контекст.ИдентификаторЗадания);
КонецПроцедуры
// Параметры:
// ОбсужденияАктивны - Булево - для доставки сообщений используется Система взаимодействия.
// Интервал - Число - уточняемое значение - число секунд до следующей проверки.
//
// Возвращаемое значение:
// Неопределено - проверка не требуется.
// Структура:
// * ЗаданияДляПроверки - Массив из УникальныйИдентификатор
// * ЗаданияДляОтмены - Массив из УникальныйИдентификатор
//
Функция ПараметрыПроверкиДлительныхОпераций(ОбсужденияАктивны, Интервал)
ТекущаяДата = ТекущаяДата(); // АПК:143 Дата сеанса не используется для проверки интервалов времени
КонтролируемыеОперации = Новый Соответствие;
ЗаданияДляПроверки = Новый Массив;
ЗаданияДляОтмены = Новый Массив;
ТекущиеДлительныеОперации = АктивныеДлительныеОперации();
АктивныеДлительныеОперации = ТекущиеДлительныеОперации.Список;
ТекущиеДлительныеОперации.КонтролируемыеОперации = КонтролируемыеОперации;
Если Не ЗначениеЗаполнено(АктивныеДлительныеОперации) Тогда
Возврат Неопределено;
КонецЕсли;
ОстальныеАктивные = Новый Массив;
ИнтервалКонтроляОбсуждений = ИнтервалКонтроляОбсуждений();
Для Каждого ДлительнаяОперация Из АктивныеДлительныеОперации Цикл
ДлительнаяОперация = ДлительнаяОперация.Значение;
Если ДлительнаяОперацияОтменена(ДлительнаяОперация) Тогда
КонтролируемыеОперации.Вставить(ДлительнаяОперация.ИдентификаторЗадания, ДлительнаяОперация);
ЗаданияДляОтмены.Добавить(ДлительнаяОперация.ИдентификаторЗадания);
Иначе
ДатаКонтроля = ДлительнаяОперация.Контроль
+ ?(Не ОбсужденияАктивны
Или ДлительнаяОперация.ПолученоОповещениеОЗавершении
Или ДлительнаяОперация.ТекущийИнтервал > ИнтервалКонтроляОбсуждений,
0, ИнтервалКонтроляОбсуждений - ДлительнаяОперация.ТекущийИнтервал);
Если ДатаКонтроля <= ТекущаяДата Тогда
КонтролируемыеОперации.Вставить(ДлительнаяОперация.ИдентификаторЗадания, ДлительнаяОперация);
ЗаданияДляПроверки.Добавить(ДлительнаяОперация.ИдентификаторЗадания);
ИначеЕсли ОбсужденияАктивны Тогда
ОстальныеАктивные.Добавить(ДлительнаяОперация);
КонецЕсли;
КонецЕсли;
КонецЦикла;
Если Не ЗначениеЗаполнено(ЗаданияДляПроверки)
И Не ЗначениеЗаполнено(ЗаданияДляОтмены) Тогда
УточнитьИнтервалОбработчикаОжидания(Интервал, АктивныеДлительныеОперации, ОбсужденияАктивны);
Возврат Неопределено;
КонецЕсли;
Для Каждого ДлительнаяОперация Из ОстальныеАктивные Цикл
КонтролируемыеОперации.Вставить(ДлительнаяОперация.ИдентификаторЗадания, ДлительнаяОперация);
ЗаданияДляПроверки.Добавить(ДлительнаяОперация.ИдентификаторЗадания);
КонецЦикла;
Результат = Новый Структура;
Результат.Вставить("ЗаданияДляПроверки", ЗаданияДляПроверки);
Результат.Вставить("ЗаданияДляОтмены", ЗаданияДляОтмены);
Возврат Результат;
КонецФункции
Функция ДлительнаяОперацияОтменена(ДлительнаяОперация)
Возврат ДлительнаяОперация.ОтменятьПриЗакрытииФормыВладельца
И ДлительнаяОперация.ФормаВладелец <> Неопределено
И Не ДлительнаяОперация.ФормаВладелец.Открыта();
КонецФункции
// Для процедур ПриПолученииСерверногоОповещения, ПослеПериодическогоПолученияДанныхКлиентаНаСервере и
// ОбработатьНакопленныеСерверныеОповещения.
//
Процедура ОбработатьРезультатОперации(АктивныеДлительныеОперации, Операция, Результат)
Если АктивныеДлительныеОперации.Получить(Операция.ИдентификаторЗадания) = Неопределено Тогда
Возврат;
КонецЕсли;
Если ЗначениеЗаполнено(Результат.Сообщения) Тогда
Сообщения = Новый Массив;
Для Каждого ОписаниеСообщения Из Результат.Сообщения Цикл
Если ТипЗнч(ОписаниеСообщения) = Тип("СообщениеПользователю") Тогда
Сообщение = ОписаниеСообщения;
Иначе
Сообщение = Новый СообщениеПользователю;
ЗаполнитьЗначенияСвойств(Сообщение, ОписаниеСообщения);
КонецЕсли;
Сообщения.Добавить(Сообщение);
КонецЦикла;
Результат.Сообщения = Новый ФиксированныйМассив(Сообщения);
КонецЕсли;
Попытка
Если ОбработатьРезультатАктивнойОперации(Операция, Результат) Тогда
АктивныеДлительныеОперации.Удалить(Операция.ИдентификаторЗадания);
КонецЕсли;
Исключение
// далее не отслеживаем
АктивныеДлительныеОперации.Удалить(Операция.ИдентификаторЗадания);
ВызватьИсключение;
КонецПопытки;
КонецПроцедуры
Процедура УточнитьИнтервалОбработчикаОжидания(Интервал, АктивныеДлительныеОперации, ОбсужденияАктивны)
ТекущаяДата = ТекущаяДата(); // АПК:143 Дата сеанса не используется для проверки интервалов времени
НовыйИнтервал = 120;
ИнтервалКонтроляОбсуждений = ИнтервалКонтроляОбсуждений();
Если ОбсужденияАктивны И НовыйИнтервал < ИнтервалКонтроляОбсуждений Тогда
НовыйИнтервал = ИнтервалКонтроляОбсуждений;
КонецЕсли;
Для Каждого Операция Из АктивныеДлительныеОперации Цикл
Если Не ОбсужденияАктивны Или Операция.Значение.ПолученоОповещениеОЗавершении Тогда
НовыйИнтервал = Макс(Мин(НовыйИнтервал, Операция.Значение.Контроль - ТекущаяДата), 1);
КонецЕсли;
КонецЦикла;
Если Интервал > НовыйИнтервал Тогда
Интервал = НовыйИнтервал;
КонецЕсли;
КонецПроцедуры
// Возвращаемое значение:
// Число - число секунд контроля длительной операции
// через общий серверный вызов, когда обсуждения
// активны, но сообщений не поступило, например,
// при операции дольше указанного числа секунд
// или когда произошло аварийное завершение фонового задания и
// сообщение через систему взаимодействия не было отправлено.
//
Функция ИнтервалКонтроляОбсуждений()
Возврат ?(СерверныеОповещенияКлиент.СостояниеПолучения().УведомленияКлиентаДоступны, 180, 30);
КонецФункции
// См. СтандартныеПодсистемыКлиент.ПриПолученииСерверногоОповещения
Процедура ПриПолученииСерверногоОповещения(ИмяОповещения, Результат) Экспорт
ТекущиеДлительныеОперации = АктивныеДлительныеОперации();
АктивныеДлительныеОперации = ТекущиеДлительныеОперации.Список;
Операция = АктивныеДлительныеОперации.Получить(Результат.ИдентификаторЗадания);
Если Операция = Неопределено Тогда
БудущиеОперации = ТекущиеДлительныеОперации.БудущиеОперации;
ОповещенияОперации = БудущиеОперации.Получить(Результат.ИдентификаторЗадания);
Если ОповещенияОперации = Неопределено Тогда
ОповещенияОперации = Новый Структура;
ОповещенияОперации.Вставить("Результаты", Новый Массив);
ОповещенияОперации.Вставить("ДатаПоследнегоДобавления", '00010101');
БудущиеОперации.Вставить(Результат.ИдентификаторЗадания, ОповещенияОперации);
КонецЕсли;
ОповещенияОперации.Результаты.Добавить(Результат.Результат);
ОповещенияОперации.ДатаПоследнегоДобавления = ОбщегоНазначенияКлиент.ДатаУниверсальная();
Возврат;
ИначеЕсли ДлительнаяОперацияОтменена(Операция) Тогда
Возврат;
КонецЕсли;
Если Результат.ВидОповещения = "Прогресс" Тогда
Если Операция.ВремяОтправкиПоследнегоПрогресса < Результат.ВремяОтправки Тогда
Операция.ВремяОтправкиПоследнегоПрогресса = Результат.ВремяОтправки;
Иначе
Возврат; // Устаревшее сообщение прогресса пропускается.
КонецЕсли;
КонецЕсли;
ОбработатьРезультатОперации(АктивныеДлительныеОперации, Операция, Результат.Результат);
КонецПроцедуры
// Для процедуры ОжидатьЗавершение.
Процедура ОбработатьНакопленныеСерверныеОповещения(ИдентификаторЗадания, Выполняется = Истина)
ТекущиеДлительныеОперации = АктивныеДлительныеОперации();
БудущиеОперации = ТекущиеДлительныеОперации.БудущиеОперации;
ОповещенияОперации = БудущиеОперации.Получить(ИдентификаторЗадания);
БудущиеОперации.Удалить(ИдентификаторЗадания);
Если Выполняется Тогда
АктивныеДлительныеОперации = ТекущиеДлительныеОперации.Список;
Операция = АктивныеДлительныеОперации.Получить(ИдентификаторЗадания);
Если Операция <> Неопределено И ОповещенияОперации <> Неопределено Тогда
Для Каждого Результат Из ОповещенияОперации.Результаты Цикл
ОбработатьРезультатОперации(АктивныеДлительныеОперации, Операция, Результат);
КонецЦикла;
КонецЕсли;
КонецЕсли;
ДатаУниверсальная = ОбщегоНазначенияКлиент.ДатаУниверсальная();
ПросроченныеБудущиеОперации = Новый Массив;
Для Каждого КлючИЗначение Из БудущиеОперации Цикл
Если КлючИЗначение.Значение.ДатаПоследнегоДобавления + 15 < ДатаУниверсальная Тогда
ПросроченныеБудущиеОперации.Добавить(КлючИЗначение.Ключ);
КонецЕсли;
КонецЦикла;
Для Каждого ПросроченнаяБудущаяОперация Из ПросроченныеБудущиеОперации Цикл
БудущиеОперации.Удалить(ПросроченнаяБудущаяОперация);
КонецЦикла;
КонецПроцедуры
// Параметры:
// РасширенныеПараметры - Структура:
// * ФормаВладелец - ФормаКлиентскогоПриложения
// - Неопределено
// * Заголовок - Строка
// * ТекстСообщения - Строка
// * ВыводитьОкноОжидания - Булево
// * ВыводитьПрогрессВыполнения - Булево
// * ОповещениеОПрогрессеВыполнения - ОписаниеОповещения
// - Неопределено
// * ВыводитьСообщения - Булево
// * Интервал - Число
// * ОповещениеПользователя - Структура:
// ** Показать - Булево
// ** Текст - Строка
// ** НавигационнаяСсылка - Строка
// ** Пояснение - Строка
// ** Картинка - Картинка
// ** Важное - Булево
//
// * ОтменятьПриЗакрытииФормыВладельца - Булево
// * ПолучатьРезультат
//
// * ИдентификаторЗадания - УникальныйИдентификатор
// * НакопленныеСообщения - Массив
// * ОповещениеОЗавершении - ОписаниеОповещения
// - Неопределено
// * ТекущийИнтервал - Число
// * Контроль - Дата
//
// * ВремяОтправкиПоследнегоПрогресса - Число - универсальная дата в миллисекундах.
// * ПолученоОповещениеОЗавершении - Булево
//
// ДлительнаяОперация - см. ДлительныеОперации.НовыйРезультатВыполненияОперации
//
Функция ОбработатьРезультатАктивнойОперации(РасширенныеПараметры, ДлительнаяОперация)
Если ДлительнаяОперация.Статус <> "Отменено" Тогда
Если РасширенныеПараметры.ОповещениеОПрогрессеВыполнения <> Неопределено Тогда
Состояние = НовоеСостояниеДлительнойОперации();
Состояние.Статус = ДлительнаяОперация.Статус;
Состояние.Прогресс = ДлительнаяОперация.Прогресс;
Состояние.Сообщения = ДлительнаяОперация.Сообщения;
Состояние.ИдентификаторЗадания = РасширенныеПараметры.ИдентификаторЗадания;
Попытка
ВыполнитьОбработкуОповещения(РасширенныеПараметры.ОповещениеОПрогрессеВыполнения, Состояние);
Исключение
ИнформацияОбОшибке = ИнформацияОбОшибке();
ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
НСтр("ru = 'При вызове оповещения о прогрессе длительной операции
|%1 возникла ошибка:
|%2'"),
Строка(РасширенныеПараметры.ИдентификаторЗадания),
ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
ЖурналРегистрацииКлиент.ДобавитьСообщениеДляЖурналаРегистрации(
НСтр("ru = 'Длительные операции.Ошибка вызова обработчика события'",
ОбщегоНазначенияКлиент.КодОсновногоЯзыка()),
"Ошибка",
ТекстОшибки);
КонецПопытки;
ИначеЕсли ДлительнаяОперация.Сообщения <> Неопределено Тогда
Для Каждого Сообщение Из ДлительнаяОперация.Сообщения Цикл
РасширенныеПараметры.НакопленныеСообщения.Добавить(Сообщение);
КонецЦикла;
КонецЕсли;
КонецЕсли;
Если ДлительнаяОперация.Статус <> "Выполняется" Тогда
Если ДлительнаяОперация.Статус <> "Выполнено"
Или ДлительнаяОперация.Свойство("ФоновоеЗаданиеЗавершено")
Или Не ( РасширенныеПараметры.Свойство("АдресРезультата")
И ЗначениеЗаполнено(РасширенныеПараметры.АдресРезультата)
Или РасширенныеПараметры.Свойство("АдресДополнительногоРезультата")
И ЗначениеЗаполнено(РасширенныеПараметры.АдресДополнительногоРезультата))
// Требуется проверка, так как оповещение отправляется в конце фонового задания
// до его завершения и данные результата еще недоступны по адресу временного хранилища.
Или ДлительныеОперацииВызовСервера.ФоновоеЗаданиеЗавершено(РасширенныеПараметры.ИдентификаторЗадания) Тогда
ЗавершитьДлительнуюОперацию(РасширенныеПараметры, ДлительнаяОперация);
Возврат Истина;
ИначеЕсли ДлительнаяОперация.Статус = "Выполнено" Тогда
РасширенныеПараметры.ТекущийИнтервал = 1;
РасширенныеПараметры.ПолученоОповещениеОЗавершении = Истина;
СерверныеОповещенияКлиент.ПодключитьОбработчикПроверкиПолученияСерверныхОповещений();
КонецЕсли;
КонецЕсли;
ИнтервалОжидания = РасширенныеПараметры.ТекущийИнтервал;
Если РасширенныеПараметры.Интервал = 0
И ДлительнаяОперация.Свойство("КонтрольДлительныхОперацийБезСистемыВзаимодействия") Тогда
ИнтервалОжидания = ИнтервалОжидания * 1.4;
Если ИнтервалОжидания > 15 Тогда
ИнтервалОжидания = 15;
КонецЕсли;
РасширенныеПараметры.ТекущийИнтервал = ИнтервалОжидания;
КонецЕсли;
РасширенныеПараметры.Контроль = ТекущаяДата() + ИнтервалОжидания; // АПК:143 Дата сеанса не используется для проверки интервалов
Возврат Ложь;
КонецФункции
Процедура ОбработатьСообщенияПользователю(Сообщения, НакопленныеСообщения, ВыводитьСообщения, ВладелецФормы) Экспорт
ИдентификаторНазначения = ?(ВыводитьСообщения И ВладелецФормы <> Неопределено,
ВладелецФормы.УникальныйИдентификатор, Неопределено);
Для Каждого СообщениеПользователю Из Сообщения Цикл
НакопленныеСообщения.Добавить(СообщениеПользователю);
Если ИдентификаторНазначения <> Неопределено Тогда
НовоеСообщение = Новый СообщениеПользователю;
ЗаполнитьЗначенияСвойств(НовоеСообщение, СообщениеПользователю);
НовоеСообщение.ИдентификаторНазначения = ИдентификаторНазначения;
НовоеСообщение.Сообщить();
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Процедура ЗавершитьДлительнуюОперацию(РасширенныеПараметры, ДлительнаяОперация)
Если РасширенныеПараметры.ОповещениеОЗавершении = Неопределено Тогда
Если ДлительнаяОперация.Статус = "Выполнено" Тогда
ПоказатьОповещение(РасширенныеПараметры.ОповещениеПользователя, РасширенныеПараметры.ФормаВладелец);
КонецЕсли;
Возврат;
КонецЕсли;
Если ДлительнаяОперация.Статус = "Отменено" Тогда
Результат = Неопределено;
Иначе
Результат = НовыйРезультатДлительнойОперации();
Результат.Статус = ДлительнаяОперация.Статус;
Если РасширенныеПараметры.Свойство("АдресРезультата") Тогда
Результат.АдресРезультата = РасширенныеПараметры.АдресРезультата;
КонецЕсли;
Если РасширенныеПараметры.Свойство("АдресДополнительногоРезультата") Тогда
Результат.АдресДополнительногоРезультата = РасширенныеПараметры.АдресДополнительногоРезультата;
КонецЕсли;
Результат.Вставить("ИнформацияОбОшибке", ДлительнаяОперация.ИнформацияОбОшибке);
Результат.Вставить("КраткоеПредставлениеОшибки", ДлительнаяОперация.КраткоеПредставлениеОшибки);
Результат.Вставить("ПодробноеПредставлениеОшибки", ДлительнаяОперация.ПодробноеПредставлениеОшибки);
Результат.Вставить("Сообщения", Новый ФиксированныйМассив(РасширенныеПараметры.НакопленныеСообщения));
Результат.ИдентификаторЗадания = РасширенныеПараметры.ИдентификаторЗадания;
КонецЕсли;
ОповеститьОЗавершенииДлительнойОперации(РасширенныеПараметры.ОповещениеОЗавершении,
Результат, РасширенныеПараметры.ИдентификаторЗадания);
Если ДлительнаяОперация.Статус = "Выполнено" Тогда
ПоказатьОповещение(РасширенныеПараметры.ОповещениеПользователя, РасширенныеПараметры.ФормаВладелец);
КонецЕсли;
КонецПроцедуры
Процедура ОповеститьОЗавершенииДлительнойОперации(ОповещениеОЗавершении, Результат, ИдентификаторЗадания)
Попытка
ВыполнитьОбработкуОповещения(ОповещениеОЗавершении, Результат);
Исключение
ИнформацияОбОшибке = ИнформацияОбОшибке();
ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
НСтр("ru = 'При вызове оповещения о завершении длительной операции
|%1 возникла ошибка:
|%2'"),
Строка(ИдентификаторЗадания),
ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
ЖурналРегистрацииКлиент.ДобавитьСообщениеДляЖурналаРегистрации(
НСтр("ru = 'Длительные операции.Ошибка вызова обработчика события'",
ОбщегоНазначенияКлиент.КодОсновногоЯзыка()),
"Ошибка", ТекстОшибки,, Истина);
СтандартныеПодсистемыКлиент.ВывестиИнформациюОбОшибке(ИнформацияОбОшибке)
КонецПопытки;
КонецПроцедуры
// Возвращаемое значение:
// Структура:
// * Список - Соответствие из КлючИЗначение:
// ** Ключ - УникальныйИдентификатор - идентификатор фонового задания.
// ** Значение - см. ОбработатьРезультатАктивнойОперации.ДлительнаяОперация
// * КонтролируемыеОперации - Соответствие из КлючИЗначение:
// ** Ключ - УникальныйИдентификатор - идентификатор фонового задания.
// ** Значение - см. ОбработатьРезультатАктивнойОперации.ДлительнаяОперация
// * БудущиеОперации - Соответствие из КлючИЗначение:
// ** Ключ - УникальныйИдентификатор - идентификатор фонового задания.
// ** Значение - см. НовыеОповещенияБудущейОперации
//
Функция АктивныеДлительныеОперации()
ИмяПараметра = "СтандартныеПодсистемы.АктивныеДлительныеОперации";
Если ПараметрыПриложения[ИмяПараметра] = Неопределено Тогда
Операции = Новый Структура;
Операции.Вставить("Список", Новый Соответствие);
Операции.Вставить("КонтролируемыеОперации", Новый Соответствие);
Операции.Вставить("БудущиеОперации", Новый Соответствие);
ПараметрыПриложения.Вставить(ИмяПараметра, Операции);
КонецЕсли;
Возврат ПараметрыПриложения[ИмяПараметра];
КонецФункции
Процедура ПроверитьПараметрыОжидатьЗавершение(Знач ДлительнаяОперация, Знач ОповещениеОЗавершении, Знач ПараметрыОжидания)
ОбщегоНазначенияКлиентСервер.ПроверитьПараметр("ДлительныеОперацииКлиент.ОжидатьЗавершение",
"ДлительнаяОперация", ДлительнаяОперация, Тип("Структура"));
Если ОповещениеОЗавершении <> Неопределено Тогда
ОбщегоНазначенияКлиентСервер.ПроверитьПараметр("ДлительныеОперацииКлиент.ОжидатьЗавершение",
"ОповещениеОЗавершении", ОповещениеОЗавершении, Тип("ОписаниеОповещения"));
КонецЕсли;
Если ПараметрыОжидания <> Неопределено Тогда
ТипыСвойств = Новый Структура;
Если ПараметрыОжидания.ФормаВладелец <> Неопределено Тогда
ТипыСвойств.Вставить("ФормаВладелец", Тип("ФормаКлиентскогоПриложения"));
КонецЕсли;
ТипыСвойств.Вставить("ТекстСообщения", Тип("Строка"));
ТипыСвойств.Вставить("Заголовок", Тип("Строка"));
ТипыСвойств.Вставить("ВыводитьОкноОжидания", Тип("Булево"));
ТипыСвойств.Вставить("ВыводитьПрогрессВыполнения", Тип("Булево"));
ТипыСвойств.Вставить("ВыводитьСообщения", Тип("Булево"));
ТипыСвойств.Вставить("Интервал", Тип("Число"));
ТипыСвойств.Вставить("ОповещениеПользователя", Тип("Структура"));
ТипыСвойств.Вставить("ПолучатьРезультат", Тип("Булево"));
ОбщегоНазначенияКлиентСервер.ПроверитьПараметр("ДлительныеОперацииКлиент.ОжидатьЗавершение",
"ПараметрыОжидания", ПараметрыОжидания, Тип("Структура"), ТипыСвойств);
СообщениеПроверки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
НСтр("ru = 'Параметр %1 должен быть больше или равен 1'"), "ПараметрыОжидания.Интервал");
ОбщегоНазначенияКлиентСервер.Проверить(ПараметрыОжидания.Интервал = 0 Или ПараметрыОжидания.Интервал >= 1,
СообщениеПроверки, "ДлительныеОперацииКлиент.ОжидатьЗавершение");
СообщениеПроверки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
НСтр("ru = 'Если параметр %1 установлен в %2, то параметр %3 не поддерживается'"),
"ПараметрыОжидания.ВыводитьОкноОжидания",
"Истина",
"ПараметрыОжидания.ОповещениеОПрогрессеВыполнения");
ОбщегоНазначенияКлиентСервер.Проверить(Не (ПараметрыОжидания.ОповещениеОПрогрессеВыполнения <> Неопределено И ПараметрыОжидания.ВыводитьОкноОжидания),
СообщениеПроверки, "ДлительныеОперацииКлиент.ОжидатьЗавершение");
КонецЕсли;
КонецПроцедуры
Процедура ПоказатьОповещение(ОповещениеПользователя, ВладелецФормы)
Оповещение = ОповещениеПользователя;
Если Не Оповещение.Показать Тогда
Возврат;
КонецЕсли;
НавигационнаяСсылкаОповещения = Оповещение.НавигационнаяСсылка;
ПояснениеОповещения = Оповещение.Пояснение;
Если ВладелецФормы <> Неопределено И ВладелецФормы.Окно <> Неопределено Тогда
Если НавигационнаяСсылкаОповещения = Неопределено Тогда
НавигационнаяСсылкаОповещения = ВладелецФормы.Окно.ПолучитьНавигационнуюСсылку();
КонецЕсли;
Если ПояснениеОповещения = Неопределено Тогда
ПояснениеОповещения = ВладелецФормы.Окно.Заголовок;
КонецЕсли;
КонецЕсли;
СтатусОповещения = Неопределено;
Если ТипЗнч(Оповещение.Важное) = Тип("Булево") Тогда
СтатусОповещения = ?(Оповещение.Важное,
СтатусОповещенияПользователя.Важное, СтатусОповещенияПользователя.Информация);
КонецЕсли;
Текст = ?(Оповещение.Текст <> Неопределено, Оповещение.Текст, НСтр("ru = 'Действие выполнено'"));
ВывестиОповещениеПользователю(Текст, НавигационнаяСсылкаОповещения, ПояснениеОповещения,
Оповещение.Картинка, СтатусОповещения, Неопределено, ВладелецФормы);
КонецПроцедуры
// Для подключения расширением для тестирования.
Процедура ВывестиОповещениеПользователю(Текст, ДействиеПриНажатии, Пояснение,
Картинка, СтатусОповещения, КлючУникальности, ВладелецФормы)
ПоказатьОповещениеПользователя(Текст, ДействиеПриНажатии, Пояснение,
Картинка, СтатусОповещения, КлючУникальности);
КонецПроцедуры
#КонецОбласти