При разработке механизмов интеграции, синхронизации данных или обмена между различными информационными базами 1С (например, через COM-соединение, веб-сервисы или HTTP-сервисы) программисты часто сталкиваются с необходимостью создать элемент в текущей базе с точно таким же уникальным идентификатором (GUID/UUID), какой он имеет в базе-источнике. Это критически важно для обеспечения ссылочной целостности и предотвращения появления дублей при последующих обменах.
Однако при попытке напрямую присвоить новое значение свойству Ссылка создаваемого объекта, система выдает ошибку: «Поле объекта недоступно для записи (Ссылка)». В этой статье мы подробно разберем, почему возникает данная ошибка, как устроены идентификаторы в платформе 1С:Предприятие, и рассмотрим правильную методику программного назначения GUID новым объектам в обычных и управляемых формах.
Для того чтобы понять причину возникновения ошибки, проанализируем внутреннее устройство объектов базы данных 1С. Ссылка (тип данных СправочникСсылка, ДокументСсылка и др.) представляет собой уникальный 128-битный идентификатор записи в таблице СУБД. Платформа строго разграничивает объектную сущность (с которой мы работаем в оперативной памяти с возможностью изменения реквизитов) и саму запись в базе данных.
Свойство Ссылка у любого объекта базы данных (будь то справочник, документ или план видов характеристик) доступно исключительно для чтения. Это фундаментальное правило безопасности платформы. Если бы разработчикам разрешалось напрямую перезаписывать ссылки у существующих или новых объектов обычным присваиванием, это привело бы к катастрофическому нарушению ссылочной целостности базы данных, потере связей между таблицами и непредсказуемому поведению конфигурации.
Попытка выполнить следующий код предсказуемо вызовет аварийное завершение процедуры:
// Ошибочный код, вызывающий исключение
НовыйКонтрагент = Справочники.Контрагенты.СоздатьЭлемент();
НовыйКонтрагент.Ссылка = Справочники.Контрагенты.ПолучитьСсылку(Новый УникальныйИдентификатор(СтрокаGUID)); // Ошибка!
Выяснив причину ограничения, рассмотрим правильный и безопасный инструмент, который предоставляет платформа 1С для решения этой задачи.
Для программной установки конкретного уникального идентификатора новому, еще ни разу не записанному в базу данных объекту, предназначен специальный метод — УстановитьСсылкуНового().
Этот метод дает команду платформе: «При первой физической записи этого объекта в базу данных не генерируй случайный GUID, а запиши его с тем идентификатором, который я передаю в параметре».
Синтаксис метода выглядит следующим образом:
Объект.УстановитьСсылкуНового(Ссылка);
Где Ссылка — это пустая или подготовленная ссылка нужного типа, содержащая требуемый уникальный идентификатор (GUID).
Рассмотрим классический пример использования этого метода при программном создании элемента справочника (например, во внешней обработке или фоновом задании):
// Шаг 1. Получаем строковое представление GUID из внешней системы
СтрокаGUID = "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d";
// Шаг 2. Создаем объект УникальныйИдентификатор
НовыйUID = Новый УникальныйИдентификатор(СтрокаGUID);
// Шаг 3. Генерируем ссылку нужного типа на основе этого идентификатора
НоваяСсылка = Справочники.Контрагенты.ПолучитьСсылку(НовыйUID);
// Шаг 4. Создаем новый объект справочника
НовыйОбъект = Справочники.Контрагенты.СоздатьЭлемент();
НовыйОбъект.Наименование = "ООО Новые Технологии";
НовыйОбъект.ИНН = "7701020304";
// Шаг 5. Привязываем подготовленную ссылку к новому объекту
НовыйОбъект.УстановитьСсылкуНового(НоваяСсылка);
// Шаг 6. Записываем объект в базу данных
НовыйОбъект.Записать();
После вызова метода Записать() элемент появится в базе данных именно с тем уникальным идентификатором, который был передан на четвертом шаге.
Перейдем к ситуации, описанной автором темы. Разработчик пытается установить ссылку непосредственно в процессе интерактивного создания элемента через форму. В управляемом приложении 1С:Предприятие 8 существует специфика работы с данными на клиенте и сервере, которую необходимо учитывать.
В управляемой форме реквизит Объект имеет тип ДанныеФормыСтруктура (или ДанныеФормыЭлементДекорации и т.д.). Этот тип данных является лишь легковесной проекцией прикладного объекта для отображения на интерфейсе. Он не имеет метода УстановитьСсылкуНового().
Чтобы корректно перехватить момент записи и назначить объекту требуемую ссылку, мы должны использовать стандартные события формы на сервере. Наиболее подходящим местом для этого является обработчик события формы ПередЗаписьюНаСервере.
Проанализируем сигнатуру этого обработчика:
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
Обратите внимание на параметр ТекущийОбъект. На момент выполнения этой процедуры платформа уже преобразовала экранные данные формы в реальный прикладной объект (например, СправочникОбъект.Контрагенты), который готовится к записи в базу данных. Именно у этого параметра мы можем вызывать необходимые нам методы.
Разберем по шагам рабочий пример реализации этой логики в модуле управляемой формы справочника Контрагенты:
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
// Проверяем, что объект записывается впервые (он еще не существует в базе)
Если ТекущийОбъект.ЭтоНовый() Тогда
// В реальном сценарии здесь может быть обращение к COM-соединению, HTTP-запросу или реквизиту формы
// Для примера предположим, что GUID внешней базы сохранен в переменной или получен из внешней функции
СтрокаGUID = ПолучитьGUIDИзВнешнейСистемы();
Если ЗначениеЗаполнено(СтрокаGUID) Тогда
Попытка
НовыйUID = Новый УникальныйИдентификатор(СтрокаGUID);
НоваяСсылка = Справочники.Контрагенты.ПолучитьСсылку(НовыйUID);
// Проверяем, нет ли уже в нашей базе элемента с таким GUID, чтобы избежать дублирования и ошибок СУБД
СуществующийОбъект = НоваяСсылка.ПолучитьОбъект();
Если СуществующийОбъект = Неопределено Тогда
// Объект уникален, можем безопасно устанавливать ссылку
ТекущийОбъект.УстановитьСсылкуНового(НоваяСсылка);
Иначе
ВызватьИсключение "Объект с таким уникальным идентификатором уже зарегистрирован в базе данных!";
КонецЕсли;
Исключение
ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Не удалось установить GUID внешней системы: " + ОписаниеОшибки());
Отказ = Истина; // Прерываем запись объекта
КонецПопытки;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
При работе с программной установкой ссылок существует ряд тонкостей, несоблюдение которых может приводить к труднодиагностируемым ошибкам в работе прикладных решений. Рассмотрим их подробнее.
Если вы создаете новые объекты и устанавливаете им ссылки в кодовой цепочке обмена данными (например, в конвертации данных или правилах синхронизации), стандартные обработчики событий (такие как ПередЗаписью в модуле объекта) могут сбрасывать или переопределять некоторые реквизиты, а также выполнять тяжелые проверки, мешающие записи. Чтобы этого избежать, перед вызовом метода Записать() устанавливайте свойство автообмена:
НовыйОбъект.ОбменДанными.Загрузка = Истина;
Это заставит платформу выполнить быструю «чистую» запись объекта в СУБД, гарантируя применение установленного GUID без помех со стороны прикладного кода конфигурации.
Иногда возникает ситуация, когда при создании нового объекта (например, Договора) вам требуется одновременно заполнить подчиненный справочник или записать сведения в регистр до того, как сам объект физически запишется в базу данных. В таких случаях связка методов работает следующим образом:
НовыйДоговор = Справочники.ДоговорыКонтрагентов.СоздатьЭлемент();
НоваяСсылка = Справочники.ДоговорыКонтрагентов.ПолучитьСсылку();
НовыйДоговор.УстановитьСсылкуНового(НоваяСсылка);
// Теперь мы можем получить эту ссылку и использовать ее для заполнения подчиненных структур
СсылкаДляСвязи = НовыйДоговор.ПолучитьСсылкуНового();
// Записываем подчиненный объект, указывая СсылкаДляСвязи в качестве владельца...
Помните, что метод УстановитьСсылкуНового() игнорируется платформой, если объект уже записан в базу данных (то есть метод ЭтоНовый() возвращает Ложь). Изменить GUID у существующего в базе элемента стандартными средствами невозможно. Если возникла такая необходимость, вам придется создать точную копию элемента с новым GUID, перенести туда все реквизиты и табличные части, записать его, а старый объект пометить на удаление и перенаправить на новую ссылку все имеющиеся в базе документы через механизм «Поиск и замена дублей».
Если переданный вами идентификатор совпадает с уже существующим GUID другой записи в этой же таблице, СУБД вернет критическую ошибку нарушения уникальности индекса. Всегда делайте предварительную проверку существования элемента по сформированной ссылке методом ПолучитьОбъект(), как было продемонстрировано в примере кода для управляемой формы.
Таким образом, мы детально изучили причины возникновения ошибки недоступности поля Ссылка для записи, освоили механизм использования метода УстановитьСсылкуНового() и разобрали особенности его применения в управляемом интерфейсе 1С. Использование правильных серверных событий формы и проверка уникальности GUID застрахуют ваше приложение от сбоев и обеспечат надежную синхронизацию данных.