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