Фоновое задание с произвольной выборкой и произвольной обработкой

Данная обработка позволяет производить любые манипуляции с базой, не трогая конфигурацию (за исключением только первого раза, когда мы вносим код самого фонового задания в любой неглобальный общий модуль).

Метод не уникален, есть несколько подобных публикаций, как например //infostart.ru/public/437160/, но там многопоточность и все такое. Для обычной же работы требуются более простые вещи — дать фоновому заданию некую выборку и сказать, что с ней следует сделать. Даже проще — выборку оно сделает само, мы лишь подсунем ему текст запроса с параметрами, а требуемые действия также передадим в виде строки. Итак, текст процедур, которые нужно поместить в любой неглобальный общий модуль (или создать свой):


Процедура ГрупповаяОбработка(ПараметрыДлительногоЗадания, АдресХранилища) Экспорт

НачалоОбработки = ТекущаяДата();
РезультатОбработки = Новый Структура("ОбработаноДокументов, НеУдалосьОбработать, ЗаданиеВыполнено, ТекстСообщения", 0, 0, Ложь, "");
Запрос = Новый Запрос(ПараметрыДлительногоЗадания.ТекстЗапроса);
Для Каждого Запись Из ПараметрыДлительногоЗадания.ПараметрыЗапроса Цикл
Запрос.УстановитьПараметр(Запись.Ключ, Запись.Значение);
КонецЦикла;
Выборка = Запрос.Выполнить().Выбрать();
Действие = ПараметрыДлительногоЗадания.Действие;
КоличествоСтрок = Выборка.Количество();
НомерСтроки = 0;
НачалоСообщения = "Найдено "+КоличествоСтрок+" объектов";
Пока Выборка.Следующий() Цикл
НомерСтроки = НомерСтроки + 1;
ПредставлениеДокумента = Строка(Выборка.Документ);
Осталось = ПолучитьХодВыполнения(НачалоОбработки, НомерСтроки, КоличествоСтрок);
Проценты = ""+Окр(НомерСтроки * 100 / КоличествоСтрок, 2)+"%  ";
ТекстСообщения = НачалоСообщения + Символы.ПС + Проценты + Осталось + Символы.ПС + ПредставлениеДокумента;
СообщитьХодВыполнения(ТекстСообщения);
Попытка
Выполнить(Действие);
РезультатОбработки.ОбработаноДокументов = РезультатОбработки.ОбработаноДокументов + 1;
РезультатОбработки.ТекстСообщения = ТекстСообщения;
Исключение
РезультатОбработки.НеУдалосьОбработать = РезультатОбработки.НеУдалосьОбработать + 1;
КонецПопытки;
КонецЦикла;
РезультатОбработки.ЗаданиеВыполнено = Истина;
ПоместитьВоВременноеХранилище(РезультатОбработки, АдресХранилища);

КонецПроцедуры

Функция ПолучитьХодВыполнения(НачалоОбработки, НомерСтроки, Количество)
Затрачено = ТекущаяДата() - НачалоОбработки;
фч = "ЧЦ=2;ЧН=;ЧВН="; // формат числа
Осталось = Затрачено * Количество / НомерСтроки - Затрачено; // осталось секунд
Осталось_текст = "(осталось: "+Строка(Формат(Осталось/3600, фч) + ":"+ Формат(Осталось%3600/60, фч) + ":"+ Формат(Осталось%60, фч))+")";
Возврат Осталось_текст;
КонецФункции

Процедура СообщитьХодВыполнения(ТекстСообщения) Экспорт
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ТекстСообщения;
Сообщение.Сообщить();
КонецПроцедуры

Самая главная процедура — первая, в ней указаны все наши необходимые параметры — текст запроса, его параметры, и действия с отдельным объектом. Две следующие процедуры служат для отображения прогресса.

Запускается все это добро из внешней обработки, через процедуру

&НаСервере
Функция ОбработкаНаСервере()

ИБФайловая = ОбщегоНазначения.ИнформационнаяБазаФайловая();
ДлительныеОперации.ОтменитьВыполнениеЗадания(УИДЗаданиеПроведение);
УИДЗаданиеПроведение = Неопределено;

ПараметрыДлительногоЗадания = ПолучитьПараметрыЗадания();

Если ИБФайловая Тогда
ДополнительныйОбщийМодуль.ГрупповаяОбработка(ПараметрыДлительногоЗадания, АдресВоВременномХранилище);
РезультатВыполнения = Новый Структура("ЗаданиеВыполнено", Истина);
Иначе
РезультатВыполнения = ДлительныеОперации.ЗапуститьВыполнениеВФоне(УникальныйИдентификатор,
"ДополнительныйОбщийМодуль.ГрупповаяОбработка", ПараметрыДлительногоЗадания, НСтр("ru = 'Групповая обработка объектов'"));
АдресВоВременномХранилище = РезультатВыполнения.АдресХранилища;
УИДЗаданиеПроведение = РезультатВыполнения.ИдентификаторЗадания;
КонецЕсли;

Возврат РезультатВыполнения;

КонецФункции

Результат можно увидеть на приложенной картинке.

В качестве ДополнительныйОбщийМодуль в этой процедуре может выступать любой, куда вы поместите фоновое задание.

Какие преимущества? Текст запроса задаем на клиенте, параметры тоже, действия тоже. Сегодня перепроводим счета-фактуры и перенумеруем ПКО, завтра обрабатываем справочник "Контрагенты", послезавтра обрабатываем поступления, у которых БУ и НУ по 90 счету совпадает — в фоновом задании ничего не требуется переделывать, только во внешней обработке.

Недостатки — не подходит пользователям: чтобы править запрос, нужен конфигуратор, то есть обработка предназначена для программистов. Можно, конечно, вынести текст запроса на форму и задавать там, но и здесь потребуются знания, которые есть только у программиста.

Да, понимаю, что по сути это дыра в безопасности (по крайней мере можно много чего нехорошего сделать), но таких дыр там и без меня навалом (любая внешняя обработка, с обращением к некоторым методам типовой кофигурации, тоже может натворить тех еще делов). То есть с действиями типа Объект.Удалить нужно обращаться очень аккуратно. В общем, на свой страх и риск.

Разрабатывалось все под слегка измененную БП 3.0 (релиз 3.0.55.14, платформа 8.3.10.2299), но на типовой тоже пойдет. На других типовых вроде УТ или УПП тоже должно, по крайней мере если у вас есть модуль ДлительныеОперации.

Обработка приложена к публикации, модуль фонового задания (который приведен выше) лежит в макетах, его потребуется закинуть в один из неглобальных общих модулей (и в обработке указать путь к нему) или же создать свой.

5 Comments

  1. AlexandrSmith

    Хорошо что вы текст печатаете можно самому попробовать, поиграться. Без покупки, но лайк, я поставил.

    Reply
  2. dmalyshev

    Почему механизм дополнительных внешних обработок не подошел, которые запускаются по расписанию?

    Reply
  3. warden

    (2) Здесь речь не про работу по расписанию, а про возможность любой код выполнить в фоне, к любой выборке, и еще следить за прогрессом.

    Reply
  4. bulldog

    можно подробнее о процедурах в обработке? как получать сообщения с фонового задания?

    Reply
  5. warden

    (4)Легко. За это отвечают 2 процедуры:

    &НаКлиенте
    Процедура Подключаемый_ПроверитьВыполнение()
    
    Попытка
    Если ЗаданиеВыполнено(УИДЗаданиеПроведение) Тогда
    ВывестиРезультатФоновогоЗадания();
    Элементы.ТабДок.ОтображениеСостояния.ДополнительныйРежимОтображения = ДополнительныйРежимОтображения.НеИспользовать;
    Элементы.ТабДок.ОтображениеСостояния.Текст = ТекстПоследнегоСообщения;
    Элементы.ТабДок.ОтображениеСостояния.Картинка = БиблиотекаКартинок.ЗеленаяГалка;
    Иначе
    ДлительныеОперацииКлиент.ОбновитьПараметрыОбработчикаОжидания(ПараметрыОбработчикаОжидания);
    ПодключитьОбработчикОжидания(«Подключаемый_ПроверитьВыполнение», ПараметрыОбработчикаОжидания.ТекущийИнтервал, Истина);
    ПолучитьПоследнееСообщениеФоновогоЗадания(УИДЗаданиеПроведение);
    Элементы.ТабДок.ОтображениеСостояния.Видимость = Истина;
    Элементы.ТабДок.ОтображениеСостояния.ДополнительныйРежимОтображения = ДополнительныйРежимОтображения.Неактуальность;
    Элементы.ТабДок.ОтображениеСостояния.Текст = ТекстПоследнегоСообщения;
    Элементы.ТабДок.ОтображениеСостояния.Картинка = БиблиотекаКартинок.ДлительнаяОперация48;
    КонецЕсли;
    Исключение
    ВызватьИсключение;
    КонецПопытки;
    
    КонецПроцедуры
    
    &НаСервере
    Процедура ПолучитьПоследнееСообщениеФоновогоЗадания(УИДЗадания)
    
    Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(УИДЗадания);
    Если Задание = Неопределено Тогда
    Возврат;
    КонецЕсли;
    МассивСообщений = Задание.ПолучитьСообщенияПользователю(Ложь);
    Если МассивСообщений = Неопределено Тогда
    Возврат;
    КонецЕсли;
    Для Каждого Сообщение Из МассивСообщений Цикл
    ТекстПоследнегоСообщения = Сообщение.Текст;
    КонецЦикла;
    
    КонецПроцедуры

    Показать

    Здесь ТекстПоследнегоСообщения — реквизит формы, из него берутся данные для отображения на табличном документе. Сама работа с табдоком тоже здесь.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *