Когда мы, как разработчики 1С, сталкиваемся с кодом, который использует методы ПолучитьСсылкуНового() и УстановитьСсылкуНового() для работы с новыми объектами, часто возникает недоумение. Автор темы на форуме столкнулся с ситуацией, когда переменная, содержащая такую "новую" ссылку, в отладчике отображается как "Объект не найден", хотя код, казалось бы, выполняется корректно. Более того, это приводит к проблемам при попытке связать такой незаписанный документ с другими данными, например, при записи в регистр.
Давайте вместе разберем эту ситуацию, выясним, почему такое поведение является нормальным, и как правильно работать с ссылками на объекты до их фактической записи в базу данных. Мы рассмотрим как стандартные механизмы платформы, так и типовой код, чтобы полностью понять суть проблемы.
Для начала, давайте четко определим назначение и работу методов УстановитьСсылкуНового() и ПолучитьСсылкуНового(). Эти методы являются мощным инструментом платформы 1С для работы с уникальными идентификаторами (GUID) объектов до их фактической записи в базу данных.
УстановитьСсылкуНового(<Ссылка>): Этот метод позволяет нам заранее присвоить новому (только что созданному в оперативной памяти, но еще не записанному в базу данных) объекту конкретную ссылку. Мы передаем ему заранее сформированный уникальный идентификатор, который будет использован в качестве ссылки при последующей записи объекта. Фактически, мы резервируем место в базе данных для будущего объекта под определенным GUID. Это очень полезно, когда нам нужно связать несколько объектов или данные регистров с еще не существующим объектом.ПолучитьСсылкуНового(): Этот метод возвращает ссылку, которая была предварительно установлена для текущего нового объекта с помощью метода УстановитьСсылкуНового(). То есть, он предоставляет нам тот самый зарезервированный GUID.Основное применение этих методов заключается в оптимизации и решении задач, требующих знания ссылки на объект до его физического сохранения в базе. Например, при программном создании сложной цепочки взаимосвязанных документов или элементов справочников, или при обмене данными, когда необходимо сохранить уникальный идентификатор из базы-источника.
Теперь давайте разберем ключевой момент, который вызывает наибольшее замешательство у автора темы и многих других разработчиков: почему переменная, содержащая ссылку, полученную через ПолучитьСсылкуНового(), отображается в отладчике как "Объект не найден".
Мы должны понимать фундаментальное различие между ссылкой и представлением ссылки (объектом):
УстановитьСсылкуНового(), мы фактически резервируем такой GUID, но по этому адресу пока нет данных.Сообщить() пытается отобразить значение ссылочной переменной, он не просто показывает GUID. Он пытается получить представление объекта, т.е. прочитать из базы данных ключевые поля объекта (например, код и наименование для справочника), чтобы отобразить их пользователю в удобном виде.Суть проблемы: Когда отладчик или любой другой механизм пытается получить представление объекта по ссылке, которая была только зарезервирована, но сам объект еще не записан в базу данных, он выполняет запрос к этой базе. Поскольку по зарезервированному GUID в базе данных нет никакой информации (объект физически еще не существует там), этот запрос возвращает пустой результат. В такой ситуации платформа 1С совершенно корректно информирует нас, что по данному GUID "объект не найден".
Это не ошибка в ссылке! Ссылка (GUID) сама по себе является абсолютно корректной и уникальной. Она просто указывает на место, которое пока еще пусто. Как только объект будет записан в базу данных под этим зарезервированным GUID, представление ссылки начнет отображаться нормально.
Если мы хотим убедиться, что ссылка действительно содержит корректный GUID, а не просто "пустоту", мы можем получить его напрямую. Для любой ссылочной переменной мы можем вызвать метод УникальныйИдентификатор():
СсылкаОбъекта = ПолучитьСсылкуНового(); // Допустим, мы ранее установили эту ссылку
ИдентификаторСсылки = СсылкаОбъекта.УникальныйИдентификатор();
// В отладчике "ИдентификаторСсылки" покажет реальный GUID, например:
// {8cbbdf84-3ca4-4385-90c0-208b14dcfc4f}
Таким образом, мы видим, что GUID существует и корректно присвоен, несмотря на то, что представление ссылки отображается как "Объект не найден". Это подтверждает, что проблема не в самой ссылке, а в ее представлении до записи объекта.
Автор темы упомянул, что использует типовой код, содержащий такую конструкцию:
СсылкаНового = Справочники.ВнутренниеДокументы.ПолучитьСсылку();
УстановитьСсылкуНового(СсылкаНового);
В ходе дискуссии возникло заблуждение, что метод ПолучитьСсылку() без параметров всегда возвращает ссылку на существующий объект. Давайте развеем это недопонимание, опираясь на документацию 1С. Метод ПолучитьСсылку() может быть вызван двумя способами:
Справочники.<ИмяСправочника>.ПолучитьСсылку(<GUID>). В этом случае платформа попытается сформировать ссылку из переданного GUID. Если GUID соответствует существующему объекту, будет получена ссылка на него. Если нет – ссылка на несуществующий объект.Справочники.<ИмяСправочника>.ПолучитьСсылку(). Как ясно указано в синтакс-помощнике 1С: "Если параметр не указан, то будет сформирована новая уникальная ссылка." Это означает, что при вызове без параметров метод генерирует совершенно новый, уникальный GUID, который еще не связан ни с каким объектом в базе данных.Следовательно, строка СсылкаНового = Справочники.ВнутренниеДокументы.ПолучитьСсылку(); в типовом коде вполне корректно формирует новый, уникальный GUID, который затем передается в УстановитьСсылкуНового() для резервирования.
Автор темы также упомянул, что иногда "связь установиться не может", потому что "нельзя сделать запись в регистр без ссылки на документ". Это ключевой момент, который объясняет, почему, несмотря на "корректную" ссылку (GUID), система не позволяет использовать ее для некоторых операций.
Хотя УстановитьСсылкуНового() резервирует GUID для будущего объекта, сам объект физически не существует в базе данных до того момента, пока мы не выполним метод Записать(). Многие механизмы платформы, особенно при работе с регистрами накопления, сведений или бухгалтерии, ожидают, что ссылки, на которые они указывают, соответствуют существующим записям в соответствующих таблицах объектов (справочников, документов и т.д.).
Когда мы пытаемся записать запись в регистр, в которой одним из измерений или реквизитов является ссылка на еще незаписанный объект (т.е. объект, который существует только в оперативной памяти и имеет лишь зарезервированный GUID, но не реальную запись в БД), платформа может отказать в этой операции. Это связано с контролем ссылочной целостности: регистр не может ссылаться на то, чего еще нет.
Периодичность возникновения проблемы, описанная автором ("иногда я не могу получить ссылку нового"), может быть вызвана следующими факторами:
ОбменДанными.Загрузка: В типовых конфигурациях многие модули объектов имеют код, который выполняется только при обычном интерактивном режиме работы, но пропускается при загрузке данных (например, из других систем). Это контролируется через проверку Если Не ОбменДанными.Загрузка Тогда ... КонецЕсли;. Если этот признак некорректно устанавливается или сбрасывается, это может приводить к тому, что стандартные механизмы резервирования или записи не срабатывают в определенных условиях.В конце концов, вывод автора "проще записать" оказался верным. Если нам нужна полноценная, "живая" ссылка для использования в регистрах и других объектах, объект должен быть записан в базу данных.
Для эффективной и безошибочной работы с новыми объектами и их ссылками в 1С, мы рекомендуем придерживаться следующих практик:
УстановитьСсылкуНового() и ПолучитьСсылкуНового() работают с объектом на стадии "в оперативной памяти", до того как он станет постоянной записью в БД.УстановитьСсылкуНового() и планируете использовать ее для связывания с другими данными (особенно в регистрах), обязательно вызовите метод Записать() для этого объекта. Только после этого объект станет полноценным элементом базы данных, и его ссылка будет полностью функциональна.УстановитьСсылкуНового() для оптимизации, а не для избегания записи: Этот механизм создан для того, чтобы избежать лишних операций записи (например, сначала записать объект, получить его ссылку, а затем обновить его с этой ссылкой) или для возможности ссылаться на объект еще до его полной записи в транзакции. Но он не отменяет необходимости финальной записи объекта.УникальныйИдентификатор().ОбменДанными.Загрузка и возможное влияние сторонних разработок (расширений).Давайте посмотрим на предоставленный автором код, который находится в модуле объекта справочника "ВнутренниеДокументы", в событии ПередЗаписью:
// Обработка рабочей группы
СсылкаОбъекта = Ссылка; // Установка ссылки нового
Если Не ЗначениеЗаполнено(СсылкаОбъекта) Тогда
СсылкаОбъекта = ПолучитьСсылкуНового();
Если Не ЗначениеЗаполнено(СсылкаОбъекта) Тогда
СсылкаНового = Справочники.ВнутренниеДокументы.ПолучитьСсылку();
УстановитьСсылкуНового(СсылкаНового);
СсылкаОбъекта = СсылкаНового;
КонецЕсли;
КонецЕсли;
Разберем этот код пошагово:
СсылкаОбъекта = Ссылка;: В начале события ПередЗаписью переменная Ссылка (доступная как реквизит объекта) будет содержать ссылку на объект. Для нового объекта она будет пустой (Неопределено или ПустаяСсылка).Если Не ЗначениеЗаполнено(СсылкаОбъекта) Тогда: Это условие проверяет, является ли объект новым (то есть, если его ссылка еще не заполнена). Для нового объекта условие будет истинным.СсылкаОбъекта = ПолучитьСсылкуНового();: Здесь предпринимается попытка получить ранее зарезервированную ссылку. Если УстановитьСсылкуНового() была вызвана ранее в этой же транзакции или в родительском вызове, то ПолучитьСсылкуНового() вернет эту ссылку. Если нет, то она вернет пустую ссылку.Если Не ЗначениеЗаполнено(СсылкаОбъекта) Тогда: Если и после предыдущей попытки получить зарезервированную ссылку, СсылкаОбъекта все еще пуста (что означает, что никто ранее не вызвал УстановитьСсылкуНового() для этого объекта), тогда происходит следующее:
СсылкаНового = Справочники.ВнутренниеДокументы.ПолучитьСсылку();: Генерируется совершенно новый, уникальный GUID, как мы уже обсуждали.УстановитьСсылкуНового(СсылкаНового);: Этот только что сгенерированный GUID устанавливается как "будущая" ссылка для текущего объекта.СсылкаОбъекта = СсылкаНового;: Переменная СсылкаОбъекта теперь содержит этот зарезервированный GUID.Цель этого типового кода — гарантировать, что у нового объекта будет своя уникальная ссылка (GUID) еще до того, как он будет записан. Это позволяет другим подсистемам (например, механизмам рабочей группы, о которых идет речь в комментарии) использовать этот GUID для связывания данных до окончательной записи объекта.
Однако, как мы выяснили, сам факт наличия этого зарезервированного GUID не означает, что объект уже существует в базе данных. Для полной функциональности, особенно для использования в ссылочных полях регистров, объект должен быть записан.
Если проблема возникает периодически, это может указывать на сценарии, когда объект по каким-то причинам не доходит до финальной записи после выполнения этого кода. Возможно, пользователь отменяет операцию, или возникают ошибки, прерывающие транзакцию до коммита.
В заключение, проблема "Объект не найден" при работе с ПолучитьСсылкуНового() не является признаком ошибки в ссылке или в работе платформы. Это нормальное поведение для ссылок на объекты, которые еще не были записаны в базу данных.
Ключевым выводом является понимание того, что зарезервированная ссылка (GUID) позволяет нам работать с уникальным идентификатором объекта до его записи, но для полноценного функционирования и использования этой ссылки в других механизмах (таких как запись в регистры), объект должен быть в конечном итоге записан. Если вы используете эти механизмы, всегда убедитесь, что объект достигает стадии записи, иначе вы столкнетесь с проблемами ссылочной целостности и ошибками.