В какой момент у задачи появляется ссылка и как правильно получить ее до записи в базу данных?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке бизнес-процессов в платформе "1С:Предприятие" программисты часто сталкиваются с необходимостью связать вновь создаваемые задачи с другими объектами системы еще до того, как эти задачи будут окончательно записаны в базу данных. Типичный пример такой задачи — ведение специализированного журнала согласования или заполнение табличной части бизнес-процесса ссылками на формируемые задачи прямо в момент их генерации.

Попытка обратиться к реквизиту Ссылка у объектов из коллекции ФормируемыеЗадачи в обработчике ПриСозданииЗадач возвращает пустую ссылку. В этой статье мы подробно разберем, почему так происходит, как работает жизненный цикл объектов в 1С, как правильно использовать механизм предварительного выделения ссылок и где архитектурно вернее реализовывать журнализацию действий.

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

Для того чтобы понять суть проблемы, проанализируем жизненный цикл задачи внутри бизнес-процесса. Когда бизнес-процесс переходит на точку действия, платформа автоматически инициирует генерацию задач. На этом этапе вызывается обработчик события точки маршрута ПриСозданииЗадач.

В этот обработчик передается параметр ФормируемыеЗадачи, представляющий собой массив еще не записанных в базу данных объектов типа ЗадачаОбъект. Физически в СУБД этих записей еще не существует.

В платформе "1С:Предприятие" любой объект (будь то справочник, документ или задача) получает свою постоянную уникальную ссылку (UUID) только в момент первой успешной физической записи в базу данных (метод Записать()). До этого момента обращение к свойству Ссылка нового объекта всегда возвращает пустую ссылку соответствующего типа (например, Задачи.Адресация.ПустаяСсылка).

Именно поэтому следующий код, написанный интуитивно, не решает задачу:


Для Каждого ТекЗадача Из ФормируемыеЗадачи Цикл
    НоваяСтрокаТЧ = РезультатыСогласования.Добавить;
    // Ошибка! ТекЗадача.Ссылка на данном этапе пустая
    НоваяСтрокаТЧ.Задача = ТекЗадача.Ссылка; 
    НоваяСтрокаТЧ.Сотрудник = ТекЗадача.Исполнитель;
КонецЦикла;

Опасный антипаттерн: Принудительная запись задачи

Иногда разработчики пытаются "выкрутиться", вызывая метод Записать() для задачи вручную прямо внутри обработчиков создания задач (например, в ПередФормированиемЗадач), чтобы принудительно получить ссылку. Рассмотрим, почему это крайне плохое решение.

Принудительный вызов записи нарушает транзакционную целостность платформенного механизма бизнес-процессов. Формирование задач происходит внутри общей транзакции запуска или продвижения бизнес-процесса. Если на одном из последующих этапов генерации задач или при записи самого бизнес-процесса произойдет ошибка, транзакция откатится.

Если же вы принудительно записывали задачи вне общего транзакционного контекста или искусственно фиксировали транзакции, вы рискуете получить:

  1. "Битые" ссылки в базе данных.
  2. Нарушение логики адресации.
  3. Зависание транзакционных блокировок СУБД.
  4. Задачи, которые фактически созданы, но потеряли связь со своим бизнес-процессом.

Решение проблемы: Механизм предварительного выделения ссылок

Для решения подобных задач платформа 1С предоставляет встроенный безопасный механизм предварительного резервирования ссылки для нового, еще не записанного объекта. За это отвечают два метода объекта:

  1. УстановитьСсылкуНового(<Ссылка>) — указывает платформе, какую именно ссылку необходимо присвоить объекту при его последующей записи в базу данных.
  2. ПолучитьСсылкуНового() — позволяет получить эту зарезервированную ссылку в коде до того, как объект будет физически записан.

Рассмотрим классическую ошибку, которую совершают разработчики при попытке использовать этот механизм:


НоваяСсылкаЗадачи = Задачи.АИС_АПК_ДО_ЗадачиИсполнителей.ПолучитьСсылку(Новый УникальныйИдентификатор); 
НоваяЗадача.УстановитьСсылкуНового(НоваяСсылкаЗадачи); 
// Ошибка! Попытка прочитать НоваяЗадача.Ссылка всё равно вернет пустую ссылку!
НужнаяСсылка = НоваяЗадача.Ссылка; 

Важное правило: Вызов метода УстановитьСсылкуНового() не заполняет свойство Ссылка мгновенно! Свойство Ссылка останется пустым до момента физического выполнения метода Записать(). Но платформа "запоминает" это сопоставление. Чтобы обратиться к этой зарезервированной ссылке прямо сейчас, необходимо вызвать метод ПолучитьСсылкуНового().

Правильный вариант реализации в «ПриСозданииЗадач»

Рассмотрим, как должен выглядеть рабочий и безопасный код в обработчике бизнес-процесса, который заполняет табличную часть ссылками на будущие задачи:


Для Каждого ТекЗадача Из ФормируемыеЗадачи Цикл
    
    // 1. Генерируем новый уникальный идентификатор (UUID)
    НовыйУИД = Новый УникальныйИдентификатор;
    
    // 2. Получаем ссылку для конкретного типа задач на основании созданного UUID
    НоваяСсылкаЗадачи = Задачи.АИС_АПК_ДО_ЗадачиИсполнителей.ПолучитьСсылку(НовыйУИД); 
    
    // 3. Резервируем эту ссылку в объекте задачи
    ТекЗадача.УстановитьСсылкуНового(НоваяСсылкаЗадачи); 
    
    // 4. Заполняем табличную часть бизнес-процесса.
    // Вместо ТекЗадача.Ссылка используем метод ПолучитьСсылкуНового()!
    НоваяСтрокаТЧ = РезультатыСогласования.Добавить; 
    НоваяСтрокаТЧ.Задача = ТекЗадача.ПолучитьСсылкуНового(); 
    НоваяСтрокаТЧ.Сотрудник = ТекЗадача.Исполнитель;
    НоваяСтрокаТЧ.ДатаСтарта = ТекЗадача.Дата;
    НоваяСтрокаТЧ.НаРассмотрении = Истина;
    
    // 5. Формируем уведомление, также используя зарезервированную ссылку
    Текст = "Необходимо назначить исполнителя.";
    Пояснение = "По " + ТекЗадача.Предмет;
    ВидУведомления = Перечисления.АИС_АПК_ВидУведомленияПользователя.Объект;
    
    АИС_АПК_УведомлениеПользователейСервер.ЗаписатьНовоеУведомление(
        ТекЗадача.Исполнитель,
        ТекЗадача.ПолучитьСсылкуНового(), // Здесь передаем подготовленную ссылку!
        Текст,
        Пояснение,
        ,
        ВидУведомления
    );
    
КонецЦикла;

Этот подход гарантирует, что при физической записи задач платформой они получат именно те идентификаторы, которые мы уже сохранили в табличной части бизнес-процесса и в уведомлениях. При этом транзакционная целостность полностью сохраняется.

Где правильно делать журнализацию (логирование) задач?

Если вашей основной задачей является логирование факта создания задач в зависимости от конкретных точек маршрута бизнес-процесса, делать это в обработчиках самого бизнес-процесса — не самое лучшее архитектурное решение. Код получается перегруженным и размазанным по различным точкам маршрута.

Гораздо эффективнее использовать событийную модель самой задачи. Каждая задача, как стандартный объект базы данных, при записи проходит через обработчик модуля объекта ПриЗаписи.

В этот момент ссылка на задачу уже гарантированно существует, физически записана в БД и доступна через ЭтотОбъект.Ссылка. При этом у задачи есть реквизиты, связывающие ее с контекстом бизнес-процесса:

Разберем пример реализации централизованной журнализации в модуле объекта задачи (или через общую Подписку на событие ПриЗаписи для класса задач):


Процедура ПриЗаписи(Отказ)
    
    // Если запись по какой-то причине отклонена, логирование не выполняем
    Если Отказ Тогда
        Возврат;
    КонецЕсли;
    
    // Ссылка на задачу на данном этапе гарантированно заполнена и валидна
    СсылкаНаЗадачу = ЭтотОбъект.Ссылка;
    БизнесПроцессСсылка = ЭтотОбъект.БизнесПроцесс;
    ТекущаяТочкаМаршрута = ЭтотОбъект.ТочкаМаршрута;
    
    // Фильтруем логирование в зависимости от того, какая точка маршрута создала задачу
    Если ТекущаяТочкаМаршрута = БизнесПроцессы.АИС_АПК_Согласование.ТочкиМаршрута.НазначениеИсполнителя Тогда
        
        // Вызываем логику журнализации для конкретной точки
        ЖурналДействийСервер.ЗаписатьСобытие(
            СсылкаНаЗадачу,
            БизнесProcessСсылка,
            "Создана задача назначения исполнителя для сотрудника: " + ЭтотОбъект.Исполнитель
        );
        
    ИначеЕсли ТекущаяТочкаМаршрута = БизнесПроцессы.АИС_АПК_Согласование.ТочкиМаршрута.СогласованиеРуководителем Тогда
        
        ЖурналДействийСервер.ЗаписатьСобытие(
            СсылкаНаЗадачу,
            БизнесProcessСсылка,
            "Создана задача согласования руководителем."
        );
        
    КонецЕсли;
    
КонецПроцедуры

Преимущества переноса журнализации в событие ПриЗаписи задачи:

  1. Чистота кода: Вся логика, связанная с фиксацией действий по задачам, сосредоточена в одном месте (в модуле задачи или подписке на событие), а не рассредоточена по многочисленным обработчикам точек маршрута в бизнес-процессе.
  2. Безопасность транзакций: Если транзакция записи задачи будет отменена платформой или СУБД, запись в транзакционный журнал (например, в регистр сведений) также автоматически откатится. Вы избежите ситуации "фантомного" логирования несуществующих задач.
  3. Универсальность: Логирование будет корректно отрабатывать как при интерактивном выполнении задач пользователями, так и при их программном формировании фоновыми заданиями или внешними интеграционными сервисами.

Резюме

Выделим основные правила работы со ссылками новых объектов в 1С:

← На главную