Привет всем! В данной статье предлагаю алгоритм по-шагового разбора примера создания "Длительной операции" на базе библиотеки стандартных подсистем (БСП). Всем кому интересно, что такое БСП - можете почитать в других моих статьях.
В публикации я постараюсь придерживаться стандартов разработки, основанных на системе БСП.
В качестве примера будет разрабатываться внешняя обработка (я бы даже сказал шаблон), которая будет обращаться к созданному мной серверному общему модулю с функциями или процедурами длительных операций.
Не буду использовать расширения - не считаю нужным в этом примере. Так же посмотрим некоторые новые функции БСП для работы с "длительными операциями".
Разработка задания с отражением состояния на форме "Длительной операции"
Свою разработку я начну с написания общего серверного модуля, в котором размещу процедуру для длительной операции. Добавляем вот такой текст процедуры:
&НаСервере Процедура ПровестиДокументы222(Параметры, АдресРезультата) Экспорт //МассивВозврат = Новый Массив; ПоискДляПроведения = Новый Запрос("ВЫБРАТЬ | РеализацияТоваровУслуг.Ссылка КАК Ссылка |ИЗ | Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг |ГДЕ | РеализацияТоваровУслуг.Дата МЕЖДУ &Дата1 И &Дата2 | И РеализацияТоваровУслуг.Организация = &Организация | |УПОРЯДОЧИТЬ ПО | РеализацияТоваровУслуг.Дата"); ПоискДляПроведения.УстановитьПараметр("Дата1", Параметры.ДатаНач1); ПоискДляПроведения.УстановитьПараметр("Дата2", Параметры.ДатаОкон1); ПоискДляПроведения.УстановитьПараметр("Организация", Параметры.Орг1); НашлиДокументы = ПоискДляПроведения.Выполнить().Выгрузить(); ВсегоДокументов = НашлиДокументы.Количество(); ТекДок = 1; Для Каждого Стр11 ИЗ НашлиДокументы Цикл Док = Стр11.Ссылка.ПолучитьОбъект(); Док.Проведен = Ложь; Док.ПометкаУдаления = Ложь; Попытка Док.Записать(РежимЗаписиДокумента.Проведение, РежимПроведенияДокумента.Неоперативный); Исключение Док.Записать(РежимЗаписиДокумента.Запись); КонецПопытки; ПроцентВыполнения = (ТекДок/ВсегоДокументов)*100; ПроцентВыполнения = Окр(ПроцентВыполнения,0); //МассивВозврат.Добавить(ПроцентВыполнения); // сообщаем "процент" и "текст сообщения" Дл��тельныеОперации.СообщитьПрогресс(ПроцентВыполнения,СокрЛП(Док.Ссылка)); ТекДок = ТекДок + 1; КонецЦикла; //ПоместитьВоВременноеХранилище(МассивВозврат, АдресРезультата); КонецПроцедуры
Данная процедура очень простенькая, но для того, чтобы она работала и "отдавала" состояние пользователю нам нужно - во-первых правильно передать ей структуру параметров, чтобы она "запустилась" вообще и во-вторых - использовать функционал БСП сообщать прогресс "Процентом", "Текстом" - ДлительныеОперации.СообщитьПрогресс.
На этом разработка общего модуля так быстро завершена и я перейду к программированию внешней обработки, в которой напишем всю "обвязку" к длительной операции процедурыПровестиДокументы222.
Для начала нарисуем форму обработки:

Добавим в форму вот такие реквизиты:

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

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

Заключение и выводы
В данной статье я сделал выжимку из своего опыта, отсек лишнее и показал как можно быстро и понятно использовать стандартные методы БСП для реализации, так сказать, асинхронности в базах данных 1с (т.е. запуска обработок) без блокирования интерфейса пользователя.
Всем спасибо за прочтение данной статьи. Вы так же можете ознакомиться с рядом других моих статей, посвященным стандартам и методам программирования в 1с с помощью библиотеки стандартных подсистем.
