Привет всем! В данной статье предлагаю алгоритм по-шагового разбора примера создания "Длительной операции" на базе библиотеки стандартных подсистем (БСП). Всем кому интересно, что такое БСП - можете почитать в других моих статьях.
В публикации я постараюсь придерживаться стандартов разработки, основанных на системе БСП.
В качестве примера будет разрабатываться внешняя обработка (я бы даже сказал шаблон), которая будет обращаться к созданному мной серверному общему модулю с функциями или процедурами длительных операций.
Не буду использовать расширения - не считаю нужным в этом примере. Так же посмотрим некоторые новые функции БСП для работы с "длительными операциями".
Разработка задания с отражением состояния на форме "Длительной операции"
Свою разработку я начну с написания общего серверного модуля, в котором размещу процедуру для длительной операции. Добавляем вот такой текст процедуры:
&НаСервере
Процедура ПровестиДокументы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с с помощью библиотеки стандартных подсистем.