Как присвоить Уникальный Идентификатор (GUID) новому документу до его записи в базу данных

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

В процессе разработки в среде 1С:Предприятие 8.3 часто возникают ситуации, когда необходимо знать ссылку на документ еще до того, как он будет физически сохранен в базе данных. Например, это требуется для записи данных в независимые регистры сведений, привязки присоединенных файлов или логирования действий пользователя в еще не созданном документе. Стандартно платформа генерирует УникальныйИдентификатор (GUID) в момент первой записи объекта, но мы можем переопределить это поведение.

Рассмотрим подробно, как правильно назначить свою ссылку новому документу, какие методы для этого существуют и какие подводные камни могут встретиться при работе в управляемых формах.

Зачем нужно предварительно устанавливать ссылку?

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

Решение заключается в том, чтобы зарезервировать конкретный GUID для документа в момент открытия его формы. Выясним, как это реализовать технически.

Использование метода УстановитьСсылкуНового()

Основным инструментом для решения нашей задачи является метод объекта УстановитьСсылкуНового(). Разберем механизм его работы. Этот метод позволяет явно указать платформе, какую именно ссылку должен получить объект при сохранении в информационную базу.

Важные правила работы с методом:

  1. Метод вызывается только у объекта (документа, справочника), а не у формы или ссылки.
  2. Передаваемый параметр должен иметь тип Ссылка соответствующего объекта.
  3. Метод должен быть вызван до выполнения физической записи объекта.

Посмотрим на пример формирования ссылки через менеджер документа:


// Сначала создаем новый уникальный идентификатор
НовыйGUID = Новый УникальныйИдентификатор();

// Формируем ссылку на основе этого GUID через менеджер нужного документа
БудущаяСсылка = Документы.РеализацияТоваровУслуг.ПолучитьСсылку(НовыйGUID);

// Устанавливаем эту ссылку для объекта
ДокументОбъект.УстановитьСсылкуНового(БудущаяСсылка);

Реализация в управляемой форме

В управляемом приложении мы работаем не напрямую с объектом, а с его представлением в памяти формы — реквизитом Объект, имеющим тип ДанныеФормыСтруктура. У этого типа нет метода УстановитьСсылкуНового(). Чтобы применить нашу логику, нам необходимо выполнить преобразование данных формы в прикладной объект на сервере.

Рассмотрим наиболее подходящее место для этого кода — процедуру ПриСозданииНаСервере. Проанализируем ситуацию: если документ новый (параметр Ключ пустой), мы генерируем ссылку.


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    // Проверяем, что это создание нового документа, а не открытие существующего
    Если Параметры.Ключ.Пустая() Тогда
        
        // Конвертируем данные формы в объект документа
        ДокОбъект = РеквизитВЗначение("Объект");
        
        // Генерируем новый GUID и создаем ссылку
        НовыйGUID = Новый УникальныйИдентификатор();
        БудущаяСсылка = Документы.РеализацияТоваровУслуг.ПолучитьСсылку(НовыйGUID);
        
        // Резервируем ссылку
        ДокОбъект.УстановитьСсылкуНового(БудущаяСсылка);
        
        // Возвращаем объект обратно в данные формы
        ЗначениеВРеквизитФормы(ДокОбъект, "Объект");
        
    КонецЕсли;
    
КонецПроцедуры

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

Назначение ссылки в обработчике ПередЗаписьюНаСервере

Если ваша задача — просто гарантировать определенный GUID при сохранении, лучше всего сделать это непосредственно перед записью. В этом обработчике платформа передает нам объект ТекущийОбъект, который и будет записан в базу.


&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    
    // Если документ еще не был записан (новый)
    Если ТекущийОбъект.ЭтоНовый() Тогда
        
        // Проверяем, не была ли ссылка установлена ранее
        Если ТекущийОбъект.ПолучитьСсылкуНового().Пустая() Тогда
            НовыйGUID = Новый УникальныйИдентификатор();
            СсылкаНового = Документы.РеализацияТоваровУслуг.ПолучитьСсылку(НовыйGUID);
            ТекущийОбъект.УстановитьСсылкуНового(СсылкаНового);
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

Как получить зарезервированную ссылку в коде?

Важно понимать, что даже после вызова УстановитьСсылкуНового(), обращение к Объект.Ссылка (или ТекущийОбъект.Ссылка) все равно будет возвращать пустую ссылку до тех пор, пока транзакция записи не будет завершена. Это происходит потому, что физически в таблицах базы данных записи еще нет.

Чтобы получить ту самую зарезервированную ссылку для передачи в другие процедуры или записи в регистры, следует использовать парный метод ПолучитьСсылкуНового(). Разберем пример:


// В контексте работы с объектом на сервере
БудущаяСсылка = ДокОбъект.ПолучитьСсылкуНового();

Если Не БудущаяСсылка.Пустая() Тогда
    // Теперь мы можем использовать БудущаяСсылка для записи в регистры сведений
    ЗаписатьДанныеВРегистр(БудущаяСсылка, НашиДанные);
КонецЕсли;

Проблема "битых" ссылок в регистрах

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

Для предотвращения замусоривания базы данных рекомендуем рассмотреть следующие стратегии:

  1. Очистка при закрытии: В обработчике ПриЗакрытии формы можно проверять, был ли записан документ. Если нет — запускать фоновое задание или серверный вызов для удаления временных записей из регистров по этому GUID.
  2. Использование идентификатора формы: Вместо ссылки будущего документа используйте УникальныйИдентификатор самой формы как временный ключ в регистре. В момент окончательной записи документа в процедуре ПриЗаписи (в модуле объекта) просто замените в регистре ID формы на реальную ссылку документа.
  3. Принудительная запись: Если бизнес-логика позволяет, можно записывать документ сразу при создании (программно), а затем давать пользователю его редактировать. Но это не всегда удобно, так как создает много пустых/черновых документов.

Особенности в распределенных базах (РИБ)

Выясним, как ручное назначение GUID влияет на обмен данными. При использовании стандартного метода Новый УникальныйИдентификатор() вероятность коллизии (создания одинаковых GUID в разных узлах) крайне мала, но она теоретически выше, чем при автоматической генерации платформой. Однако на практике УстановитьСсылкуНового() является штатным механизмом и полностью поддерживается в РИБ. Главное — следить за тем, чтобы логика генерации GUID не зависела от локальных настроек времени или узла, которые могут дублироваться.

Заключение

Мы рассмотрели механизм предварительного назначения ссылки новому документу. Подведем итог: использование УстановитьСсылкуНового() в связке с ПолучитьСсылкуНового() позволяет гибко управлять связями объектов еще до их появления в базе данных. Однако помните о необходимости контроля целостности данных и своевременной очистке регистров от записей, ссылающихся на незаписанные документы.

← На главную