При проектировании комплексных процессов в программе 1С:Документооборот разработчики часто используют встроенные скрипты и блоки обработки в схемах процессов. Типичный сценарий: на одном из шагов выполняется автоматическая обработка, которая заполняет вспомогательный или заполняемый предмет (например, находит контрагента по реквизитам основного документа и прикрепляет его к процессу), а на следующем шаге создается задача согласования, в которой этот предмет должен быть виден исполнителю.
Однако на практике разработчики часто сталкиваются с крайне странным поведением системы: в первой задаче согласования (которая идет сразу за блоком-обработкой) заполненный предмет отображается как пустой. При этом, если задачу согласовать и процесс перейдет на следующий этап (например, к задаче "Согласовать 2"), там этот предмет отображается корректно. Более того, в самой карточке процесса предмет также выглядит заполненным.
В этой статье мы подробно разберем внутренние механизмы работы бизнес-процессов 1С, выясним причину возникновения этой проблемы и рассмотрим правильный шаблон кода для гарантированного заполнения предметов в задачах.
Для того чтобы понять, почему первая задача создается с пустым предметом, нам необходимо заглянуть "под капот" движка бизнес-процессов 1С:Документооборота и проанализировать жизненный цикл объектов в оперативной памяти и базе данных.
Когда бизнес-процесс доходит до блока-обработки на схеме, система выполняет код этого блока в контексте уже инициализированного в памяти сервера приложения объекта бизнес-процесса. Этот объект доступен разработчику через предопределенную переменную Процесс, которая имеет тип БизнесПроцессОбъект.КомплексныйПроцесс.
Рассмотрим типичную ошибку в коде обработки, которую часто допускают программисты:
// ПРИМЕР НЕПРАВИЛЬНОГО КОДА
ВрЗапрос = Новый Запрос;
ВрЗапрос.Текст =
"ВЫБРАТЬ
| Стороны.Сторона КАК Контр
|ИЗ
| Справочник.ВнутренниеДокументы.Стороны КАК Стороны
|ГДЕ
| Стороны.Ссылка = &ОсновнойДокумент";
ВрЗапрос.УстановитьПараметр("ОсновнойДокумент", Процесс.Ссылка); // Ссылка на процесс
// ... получение контрагента ...
// Прямая запись в базу данных по ссылке процесса:
Мультипредметность.ЗаписатьПредметБизнесПроцесса(Процесс.Ссылка, ИмяПредмета, КонтрагентСсылка);
// Попытка принудительно обновить базу данных:
ПроцОб = Процесс.Ссылка.ПолучитьОбъект();
ПроцОб.Записать();
Давайте по шагам разберем, что происходит при выполнении этого некорректного кода:
Мультипредметность.ЗаписатьПредметБизнесПроцесса(...) принимает в качестве параметра ссылку Процесс.Ссылка и записывает изменения напрямую в таблицы базы данных. Конструкция Процесс.Ссылка.ПолучитьОбъект() считывает новую копию объекта из базы данных, модифицирует её и записывает обратно в СУБД. При этом текущая переменная Процесс, которая находится в оперативной памяти выполняющегося потока, остается неизменной! В ее табличной части Предметы строка с контрагентом по-прежнему пуста.Процесс напрямую из оперативной памяти (где он не обновлен) и копирует коллекцию предметов из него. В результате создается задача с незаполненным предметом.Процесс из оперативной памяти в базу данных. Это действие может даже затереть те изменения, которые разработчик пытался внести через ПолучитьОбъект().Записать().Таким образом, ключевая проблема заключается в том, что изменения вносились в базу данных в обход текущего серверного контекста объекта Процесс, а задачи первой очереди создавались на основе устаревших данных из оперативной памяти.
Для корректного решения задачи нам необходимо выполнить два условия:
Предметы) переменной Процесс, которая находится в памяти.Процесс.Записать() не обновит связанные регистры сведений и предметы задач. Для этого существует экспортный метод РаботаСБизнесПроцессами.ЗаписатьProcess() со специальным строковым флагом обновления.Рассмотрим подробнее правильный шаблон кода для размещения в блоке-обработке схемы комплексного процесса:
// Проверим наличие предметов у процесса
Если Процесс.Предметы.Количество() > 0 Тогда
// Получаем основной документ (как правило, это первый предмет в индексе 0)
ОсновнойДокумент = Процесс.Предметы[0].Предмет;
// Запросом получаем контрагента из табличной части сторон документа
ВрЗапрос = Новый Запрос;
ВрЗапрос.Текст =
"ВЫБРАТЬ
| Стороны.Сторона КАК Контр
|ИЗ
| Справочник.ВнутренниеДокументы.Стороны КАК Стороны
|ГДЕ
| Стороны.Ссылка = &ОсновнойДокумент";
ВрЗапрос.УстановитьПараметр("ОсновнойДокумент", ОсновнойДокумент);
РезЗапр = ВрЗапрос.Выполнить().Выгрузить();
Если РезЗапр.Количество() > 0 Тогда
КонтрагентСсылка = РезЗапр[0].Контр;
ИмяПредметаКонтрагент = Справочники.ИменаПредметов.НайтиПоНаименованию("Контрагент");
// Ищем строку заполняемого предмета "Контрагент" в ТЧ "Предметы" in-memory объекта Процесс
СтрокаПредмета = Неопределено;
Для Каждого Стр Из Процесс.Предметы Цикл
Если Стр.ИмяПредмета = ИмяПредметаКонтрагент Тогда
СтрокаПредмета = Стр;
Прервать;
КонецЕсли;
КонецЦикла;
// Если в шаблоне процесса строка предмета не была заведена заранее, создаем её вручную
Если СтрокаПредмета = Неопределено Тогда
СтрокаПредмета = Процесс.Предметы.Добавить();
СтрокаПредмета.ИмяПредмета = ИмяПредметаКонтрагент;
// Указываем роль предмета в соответствии с логикой (Заполняемый или Вспомогательный)
СтрокаПредмета.РольПредмета = Перечисления.РолиПредметов.Заполняемый;
КонецЕсли;
// Заполняем значение предмета в оперативной памяти текущего процесса
СтрокаПредмета.Предмет = КонтрагентСсылка;
// Формируем специальный параметр обновления для БСП-метода записи.
// Этот ключ указывает системе, какие связанные сущности нужно обновить принудительно.
ПараметрЗаписи = "ЗаписьСОбновлением_Предметов_ПредметовЗадач_Проекта_ОбщегоСпискаПроцессов_РабочихГруппПредметов_РабочихГруппПроцессов_ДопРеквизитовПоПредметам";
// Вызываем системную процедуру записи бизнес-процесса
РаботаСБизнесПроцессами.ЗаписатьПроцесс(Процесс, ПараметрЗаписи);
КонецЕсли;
КонецЕсли;
Проанализируем структуру и логику предложенного кода, чтобы понять, почему он работает стабильно и не вызывает ошибок платформы:
Процесс.Предметы. Это важно, так как в шаблоне комплексного процесса предметы обычно уже задекларированы разработчиком или администратором. Нам нужно именно найти существующую строку по имени предмета Справочники.ИменаПредметов и заполнить ее поле Предмет ссылкой на найденного контрагента.Перечисления.РолиПредметов.Заполняемый)."ЗаписьСОбновлением_Предметов_ПредметовЗадач..." при вызове общего модуля является ключевым решением. Этот параметр заставляет платформу выполнить каскадное обновление данных в базе. В частности, сегмент ПредметовЗадач в названии параметра дает команду обновить виртуальные таблицы предметов для еще не созданных задач текущей транзакции. В результате, когда система переходит к генерации задачи "Согласовать 1", она берет уже полностью актуализированную информацию.Если вы применили правильный программный код записи процесса, но предмет все равно отсутствует в карточке задачи исполнителя, рекомендуем проверить следующие настройки в режиме "1С:Предприятие":
Перечисления.РолиПредметов.Заполняемый, а в шаблоне процесса этот предмет настроен как "Основной", может возникнуть конфликт типов, из-за которого перезапись коллекции будет проигнорирована.Применяя данный подход к работе с предметами бизнес-процессов, вы обеспечите стабильное и предсказуемое поведение комплексных процессов в любых редакциях программы 1С:Документооборот 2.1 и 3.0.