В процессе согласования и сопровождения договорной работы в «1С:Документооборот 2.1» пользователи нередко сталкиваются с избыточными ручными действиями. Классическая цепочка требует зарегистрировать документ после положительного утверждения или подписания, вручную внести полученный регистрационный номер в файл Word, а затем закрыть множество сопутствующих задач вроде «Ознакомиться с результатом» или «Исполнить». Разберем, как автоматизировать этот процесс: зарегистрировать договор программно, обновить номер в файле и исключить лишние шаги для исполнителей.
Для автоматической фиксации номера сразу после того, как документ перешел в состояние подписания, удобнее всего использовать механизм бизнес-событий и детекторы. Выясним причину такого выбора: механизм детекторов позволяет асинхронно реагировать на изменение данных документа без прямого вмешательства в логику записи объектов пользователем.
Проанализируем текст запроса для детектора событий. Запрос должен отслеживать появление подписанных внутренних документов конкретного вида, у которых еще отсутствует регистрационный номер:
Результат = Ложь;
Запрос = Новый Запрос();
Запрос.Текст =
"ВЫБРАТЬ
| ВнутренниеДокументы.Ссылка КАК Ссылка
|ИЗ
| Справочник.ВнутренниеДокументы КАК ВнутренниеДокументы
|ГДЕ
| НЕ ВнутренниеДокументы.ПометкаУдаления
| И ВнутренниеДокументы.ВидДокумента.Родитель В ИЕРАРХИИ (&ВидДокумента)
| И ВнутренниеДокументы.РегистрационныйНомер = """"
| И ВнутренниеДокументы.РезультатПодписания = &РезультатПодписания";
Запрос.УстановитьПараметр("ВидДокумента", Справочники.ВидыВнутреннихДокументов.НайтиПоНаименованию("Договоры"));
Запрос.УстановитьПараметр("РезультатПодписания", Перечисления.РезультатыПодписания.Подписан);
РезультатЗапроса = Запрос.Выполнить().Выбрать();
Если РезультатЗапроса.Следующий() Тогда
ПараметрыВозврата.Вставить("Источник", РезультатЗапроса.Ссылка);
Результат = Истина;
КонецЕсли;
Когда детектор фиксирует событие, вызывается дополнительный обработчик. В обработчике перехватываем переданную ссылку и передаем ее в серверную процедуру регистрации:
Попытка
Ур = УровеньЖурналаРегистрации.Информация;
Документ = Событие.Источник;
ЮИБ_ОбщегоНазначенияСервер.АвтоматическаяРегистрацияДоговора(Документ);
Исключение
Ур = УровеньЖурналаРегистрации.Ошибка;
Инфо = СокрЛП(ОписаниеОшибки());
ЗаписьЖурналаРегистрации("Договоры", Ур, , Документ, Инфо);
КонецПопытки;
Рассмотрим подробнее саму процедуру регистрации. Для генерации номера по правилам делопроизводства используем типовые методы подсистемы Делопроизводство:
Процедура АвтоматическаяРегистрацияДоговора(Документ) Экспорт
УстановитьПривилегированныйРежим(Истина);
// Проверим, что документ еще не зарегистрирован
Если ЗначениеЗаполнено(ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Документ, "РегистрационныйНомер")) Тогда
Возврат;
КонецЕсли;
СообщениеОбОшибке = "";
Если Делопроизводство.ЗарегистрироватьПредмет(Документ, СообщениеОбОшибке) Тогда
Делопроизводство.ЗаписатьСостояниеДокумента(
Документ,
ТекущаяДатаСеанса(),
Перечисления.СостоянияДокументов.Зарегистрирован,
ПользователиКлиентСервер.ТекущийПользователь());
КонецЕсли;
УстановитьПривилегированныйРежим(Ложь);
КонецПроцедуры
В типовой конфигурации автозаполнение файла данными документа происходит только в момент формирования файла из шаблона. Когда документ регистрируется позже, номер в печатной форме автоматически не обновляется. Посмотрим, как выполнить обновление программно.
Для перезаполнения тегов в файле формата .docx вызываем специализированный метод типового общего модуля:
АвтозаполнениеШаблоновФайловКлиентСервер.ЗаполнитьПоляФайлаДаннымиВладельца(Ложь, ФайлСсылка, Истина, , ДокументСсылка);
При использовании этого вызова важно учесть технические ограничения подсистемы работы с файлами:
Редактирует у элемента справочника Файлы. Если значение заполнено, перезаполнение вызовет ошибку..docx через разбор OpenXML. Устаревший формат .doc требует вызова внешних библиотек MS Word, что неприменимо на серверах под управлением ОС Linux.Для очистки рабочего стола определенного пользователя от рутинных шагов можно использовать несколько подходов — от программного закрытия до тонкой настройки комплексных процессов.
Если нет возможности перенастраивать действующие комплексные маршруты, можно отлавливать создание задач на нужного пользователя и программно переводить их в статус завершенных.
Текст запроса детектора для поиска незавершенных задач конкретного сотрудника:
Результат = Ложь;
Запрос = Новый Запрос();
Запрос.Текст =
"ВЫБРАТЬ
| ЗадачаИсполнителя.Ссылка КАК Ссылка
|ИЗ
| Задача.ЗадачаИсполнителя КАК ЗадачаИсполнителя
|ГДЕ
| ЗадачаИсполнителя.Исполнитель = &Исполнитель
| И НЕ ЗадачаИсполнителя.Выполнена
| И ЗадачаИсполнителя.СостояниеБизнесПроцесса = ЗНАЧЕНИЕ(Перечисление.СостоянияБизнесПроцессов.Активен)
| И (ЗадачаИсполнителя.ТочкаМаршрута = ЗНАЧЕНИЕ(БизнесПроцесс.Ознакомление.ТочкаМаршрута.Ознакомиться)
| ИЛИ ЗадачаИсполнителя.ТочкаМаршрута = ЗНАЧЕНИЕ(БизнесПроцесс.Согласование.ТочкаМаршрута.Ознакомиться)
| ИЛИ ЗадачаИсполнителя.ТочкаМаршрута = ЗНАЧЕНИЕ(БизнесПроцесс.Регистрация.ТочкаМаршрута.Ознакомиться)
| ИЛИ ЗадачаИсполнителя.ТочкаМаршрута = ЗНАЧЕНИЕ(БизнесПроцесс.Утверждение.ТочкаМаршрута.Ознакомиться)
| ИЛИ ЗадачаИсполнителя.ТочкаМаршрута = ЗНАЧЕНИЕ(БизнесПроцесс.Исполнение.ТочкаМаршрута.Исполнить)
| ИЛИ ЗадачаИсполнителя.ТочкаМаршрута = ЗНАЧЕНИЕ(БизнесПроцесс.Исполнение.ТочкаМаршрута.ОтветственноеИсполнение))";
Запрос.УстановитьПараметр("Исполнитель", Справочники.Пользователи.НайтиПоРеквизиту("ЮИБ_ТабельныйНомер", "01001"));
РезультатЗапроса = Запрос.Выполнить().Выбрать();
Если РезультатЗапроса.Следующий() Тогда
ПараметрыВозврата.Вставить("Источник", РезультатЗапроса.Ссылка);
Результат = Истина;
КонецЕсли;
Проанализируем процедуру завершения задачи. Прямой вызов ЗадачаОбъект.ВыполнитьЗадачу() требует обязательной подготовки реквизитов, иначе бизнес-логика процесса может вызвать отмену транзакции:
Процедура АвтоматическоеВыполнениеЗадач(ЗадачаСсылка) Экспорт
ЗадачаОбъект = ЗадачаСсылка.ПолучитьОбъект();
// Заполняем фактического исполнителя для корректной истории процесса
ЗадачаОбъект.ФактическийИсполнитель = ЗадачаОбъект.Исполнитель;
// Для процессов "Исполнение" обязательно требуется текстовый отчет
Если ПустаяСтрока(ЗадачаОбъект.РезультатВыполнения) Тогда
ЗадачаОбъект.РезультатВыполнения = "Выполнено автоматически";
КонецЕсли;
ЗадачаОбъект.ВыполнитьЗадачу();
КонецПроцедуры
Важное предостережение: детектор срабатывает при возникновении бизнес-событий. Если попытаться мгновенно заблокировать и выполнить задачу внутри той же транзакции, в которой создается бизнес-процесс, высок риск получить взаимоблокировку (Deadlock) на уровне СУБД. Рекомендуется передавать такие задачи в очередь фонового регламентного задания или выполнять с небольшой задержкой.
Создание задачи только для того, чтобы фоновое задание сразу ее закрыло, создает избыточную нагрузку на базу данных. Разберем по шагам альтернативные решения без лишней записи объектов:
ПриСозданииЗадач бизнес-процесса. Если текущий исполнитель совпадает с целевым пользователем, можно прерывать создание задачи или сразу устанавливать признак ее выполнения до сохранения в базу.