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