Разберем одну из самых неприятных ситуаций, с которой сталкиваются разработчики при проектировании форм документов: почему в поле ввода, привязанном к реквизиту, внезапно появляется пугающая надпись «Объект не найден», даже если документ еще даже не был записан в базу?
Основная причина кроется в особенностях жизненного цикла объекта в памяти платформы 1С. Когда вы программно работаете с документом, который только что создали, но еще не сохранили, платформа присваивает ему уникальный идентификатор (GUID). Если в коде (например, в обработчике ПередЗаписью) вы получаете ссылку через метод ПолучитьСсылку() и записываете ее в какой-либо реквизит, вы сохраняете этот GUID в памяти.
Если после этого в транзакции записи происходит ошибка, срабатывает Отказ = Истина или транзакция прерывается по другой причине, объект физически не попадает в базу данных. Однако переменная, содержащая ссылку, продолжает хранить GUID. Когда форма пытается отрисовать значение этого реквизита, 1С обращается к базе данных, не находит там записи с таким идентификатором и выдает пользователю «Объект не найден».
Часто разработчики сталкиваются с необходимостью «закольцевать» документ на самого себя для отслеживания цепочки событий. Это порождает классическую проблему: запись ссылки на объект до того, как этот объект стал «постоянным» в базе данных. Рассмотрим типичный ошибочный сценарий:
Процедура ПередЗаписью(Отказ)
// Получаем ссылку нового объекта, который еще не в базе
СсылкаНаСебя = ПолучитьСсылку();
// Пишем ее в реквизит текущего объекта
ЭтотОбъект.ПервичныйДокумент = СсылкаНаСебя;
// В процессе записи возникает условие, при котором мы решаем не сохранять документ
Если Не ПроверкаУсловий() Тогда
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
В этом примере, если запись прерывается, в памяти объекта остается ссылка на несуществующий в базе документ. Поле ввода, связанное с ПервичныйДокумент, будет отображать ошибку.
Чтобы избежать подобных инцидентов, рассмотрим несколько стратегий решения:
ПослеЗаписи. Не стоит пытаться заполнить реквизит ссылкой на самого себя до завершения транзакции записи. Используйте событие ПослеЗаписи, где объект уже гарантированно записан в базу.ЭтоНовый(). Перед выполнением кода, который заполняет реквизиты ссылками, всегда проверяйте состояние объекта. Если объект новый, отложите присвоение ссылки.Попробуем перестроить нашу логику так, чтобы она стала устойчивой к ошибкам записи. Вместо попыток записать ссылку «на лету», используем событие ПослеЗаписи для объектов, которые уже зафиксированы в базе данных:
Процедура ПослеЗаписи(Отказ, РежимЗаписи, РежимПроведения)
Если Не Отказ И ЭтоНовый() Тогда
// Записываем ссылку только после успешной фиксации в базе
ЭтотОбъект.ПервичныйДокумент = ЭтотОбъект.Ссылка;
// Повторно записываем, чтобы сохранить значение реквизита
Записать();
КонецЕсли;
КонецПроцедуры
Чтобы в будущем оперативно отлавливать такие ситуации, рекомендуем следующие действия:
Анализ кода. Просмотрите все обработчики, где происходит изменение реквизитов, имеющих составной тип или определяемый тип. Ищите места, где метод ПолучитьСсылку() используется в связке с реквизитами, отображаемыми в интерфейсе.
Использование Технологического журнала. Если ошибка воспроизводится редко, настройте ТЖ на событие DBPROC. Это позволит увидеть, в какой момент происходит обращение к неверному GUID, и понять, какая именно часть алгоритма приводит к записи некорректной ссылки.
Интерфейсная проверка. В процедуре ПриПолученииДанных (для табличных полей) можно добавить проверку ЗначениеЗаполнено(Ссылка). Если ссылка не пустая, но объект по ней не находится, принудительно очищайте представление для пользователя, чтобы не вводить его в заблуждение сообщением об ошибке.
Помните, что запись «ссылки нового» — это работа с фантомом. Главная цель разработчика — сделать так, чтобы в базе данных всегда хранились только актуальные и существующие ссылки. Соблюдая правила транзакционности, вы избавите пользователей от проблем с «битыми» данными.