Рассмотрим частую задачу разработчиков при внедрении конфигурации «1С:Документооборот»: создание дополнительного обработчика, который автоматически запускает процесс ознакомления после согласования документа. При этом список адресатов должен формироваться динамически на основе дополнительных реквизитов документа (например, свойств «Адресат 2», «Адресат 3» и так далее).
Разберем по шагам, с какими трудностями можно столкнуться при решении этой задачи, проанализируем оптимальные методы работы с дополнительными реквизитами из Библиотеки стандартных подсистем (БСП) и посмотрим на готовый рабочий код.
В типовых конфигурациях 1С дополнительные реквизиты хранятся в специальных табличных частях объектов. Начинающие разработчики часто пытаются извлечь их с помощью прямых запросов к базе данных, отбирая записи по строковому наименованию свойства (например, ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Адресат 2")).
Проанализируем ситуацию: это плохая практика. Пользователь с правами настройки НСИ может в любой момент переименовать реквизит в режиме предприятия (например, назвать его «Второй адресат»), и наш программный код моментально перестанет работать. Кроме того, прямые запросы к табличным частям требуют тщательного контроля прав доступа и параметров.
Правильным и надежным подходом является использование программного интерфейса БСП — общего модуля УправлениеСвойствами. Рассмотрим подробнее методы, которые там доступны:
УправлениеСвойствами.ПолучитьСписокСвойств() — возвращает все доступные свойства для переданного объекта.УправлениеСвойствами.ПолучитьЗначенияСвойств() — возвращает таблицу значений с заполненными данными по этим свойствам.УправлениеСвойствами.ЗначениеСвойства(СсылкаНаОбъект, СсылкаНаДопРеквизит) — позволяет получить значение конкретного реквизита.Важный совет: В актуальных версиях БСП и «1С:Документооборот» у дополнительных реквизитов есть поле «Имя (для разработчиков)». Настоятельно рекомендуем задавать там уникальный латинский идентификатор (например, Addressee2) и обращаться к реквизиту именно по нему, а не по строковому представлению.
Допустим, мы получили значение дополнительного реквизита. У него составной тип данных: это может быть конкретный СправочникСсылка.Пользователи, либо СправочникСсылка.СтруктураПредприятия (подразделение), либо СправочникСсылка.РабочиеГруппы.
Если мы просто попытаемся добавить ссылку на подразделение в табличную часть Исполнители создаваемого бизнес-процесса, платформа выдаст ошибку несоответствия типов. Бизнес-процессу нужны конкретные исполнители. Значит, нам нужно «развернуть» эти объекты-контейнеры до списка конкретных сотрудников.
Вместо написания сложных многоэтажных запросов со связями таблиц, воспользуемся готовыми функциями менеджеров справочников, которые предоставляет 1С:
Справочники.СтруктураПредприятия.СоставКонтейнераПользователей(СсылкаНаПодразделение)Справочники.РабочиеГруппы.СоставКонтейнераПользователей(СсылкаНаГруппу)Обе функции возвращают массив или таблицу пользователей, которых мы можем простым циклом добавить в маршрут нашего бизнес-процесса.
Посмотрим на итоговый рабочий пример кода для дополнительного обработчика бизнес-события. Код отрабатывает при успешном завершении процесса согласования, собирает адресатов из доп. реквизитов и запускает новый процесс ознакомления:
БизнесПроцесс = Событие.Источник;
Если ТипЗнч(БизнесПроцесс) = Тип("БизнесПроцессСсылка.Согласование")
И (БизнесПроцесс.РезультатСогласования = Перечисления.РезультатыСогласования.Согласовано
ИЛИ БизнесПроцесс.РезультатСогласования = Перечисления.РезультатыСогласования.СогласованоСЗамечаниями) Тогда
ТипыДокументов = Новый Массив;
ТипыДокументов.Добавить(Тип("СправочникСсылка.ВнутренниеДокументы"));
ВидыДокументов = Новый Массив;
ВидыДокументов.Добавить(Справочники.ВидыВнутреннихДокументов.НайтиПоНаименованию("_Служебная записка"));
// Получаем предмет согласования
ОсновныеПредметы = МультипредметностьКлиентСервер.ПолучитьМассивПредметовОбъекта(БизнесПроцесс, ТипыДокументов, Истина);
Для Каждого Предмет ИЗ ОсновныеПредметы Цикл
Если ВидыДокументов.Найти(Предмет.ВидДокумента) <> Неопределено Тогда
ШаблонПроцесса = Справочники.ШаблоныОзнакомления.НайтиПоНаименованию("Ознакомление адресата");
Если ШаблонПроцесса <> Неопределено Тогда
Процесс = БизнесПроцессы.Ознакомление.СоздатьБизнесПроцесс();
Процесс.Заполнить(Новый Структура("Предметы, Шаблон", Предмет, ШаблонПроцесса));
Процесс.Автор = Предмет.Подготовил;
// Получаем доп. свойства предмета
СписокСвойств = УправлениеСвойствами.ПолучитьСписокСвойств(Предмет);
СписокЗначений = УправлениеСвойствами.ПолучитьЗначенияСвойств(Предмет);
Для Каждого Свойство ИЗ СписокСвойств Цикл
// Ищем нужные доп. реквизиты (рекомендуется переделать на поиск по Имени для разработчиков)
Если Лев(Свойство.Заголовок, 8) = "Адресат " Тогда
ДопАдресатЗначение = СписокЗначений.Найти(Свойство.Ссылка, "Свойство").Значение;
Если ЗначениеЗаполнено(ДопАдресатЗначение) Тогда
// Обрабатываем конкретного пользователя
Если ТипЗнч(ДопАдресатЗначение) = Тип("СправочникСсылка.Пользователи") Тогда
Строка = Процесс.Исполнители.Добавить();
Строка.Исполнитель = ДопАдресатЗначение;
// Обрабатываем подразделение
ИначеЕсли ТипЗнч(ДопАдресатЗначение) = Тип("СправочникСсылка.СтруктураПредприятия") Тогда
ПользователиПодразделения = Справочники.СтруктураПредприятия.СоставКонтейнераПользователей(ДопАдресатЗначение);
Для Каждого Пользователь ИЗ ПользователиПодразделения Цикл
Строка = Процесс.Исполнители.Добавить();
Строка.Исполнитель = Пользователь;
КонецЦикла;
// Обрабатываем рабочую группу
ИначеЕсли ТипЗнч(ДопАдресатЗначение) = Тип("СправочникСсылка.РабочиеГруппы") Тогда
ПользователиРабочейГруппы = Справочники.РабочиеГруппы.СоставКонтейнераПользователей(ДопАдресатЗначение);
Для Каждого Пользователь ИЗ ПользователиРабочейГруппы Цикл
Строка = Процесс.Исполнители.Добавить();
Строка.Исполнитель = Пользователь;
КонецЦикла;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Процесс.Дата = ТекущаяДата();
Процесс.Записать();
Процесс.Старт();
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЕсли;
Выясним причину, почему нельзя просто добавить новую строку кода вида БизнесПроцесс.Исполнители.Добавить() в уже стартовавший процесс. Архитектура движка платформы 1С устроена так, что она считывает табличную часть адресатов/исполнителей исключительно в момент старта процесса или при переходе маршрута на соответствующую точку действия.
Чтобы добавить исполнителя в процесс программно «на лету», недостаточно записать измененный бизнес-процесс. Потребуется обращаться к серверному API 1С:ДО для генерации физического объекта ЗадачаИсполнителя и корректной привязки к нужной точке маршрута. Гораздо проще и правильнее в типовых механизмах использовать пользовательский режим: вкладки «Визы» или «Процессы и задачи» в карточке документа имеют специальную кнопку «Добавить в процесс», которая выполнит всю служебную работу за пользователя.
Разберем еще один смежный вопрос: что делать, если руководитель хочет видеть все задачи, падающие на его заместителя (например, по исходящим письмам)? Писать код и создавать дубликаты задач на ознакомление — избыточно и вредно для базы.
Для таких случаев 1С:Документооборот предлагает штатный функционал без необходимости программирования: