При разработке в среде «1С:Предприятие 8.3» программисты часто сталкиваются с ситуацией, когда программно присвоенное значение реквизита в процедуре ПриЗаписи «исчезает» после закрытия формы. В этой статье мы подробно разберем, почему это происходит, как работает жизненный цикл записи объекта и как правильно реализовать заполнение реквизитов (например, идентификаторов) при сохранении документа или элемента справочника.
Рассмотрим типичную ситуацию: разработчику необходимо сохранить уникальный идентификатор (UUID) ссылки в отдельный строковый реквизит ИдВнутренний для целей интеграции или построения сложных отчетов. Код пишется в обработчике ПриЗаписи, так как именно в этот момент у нового объекта уже гарантированно есть ссылка.
Проанализируем, что происходит внутри платформы в этот момент. Когда выполняется процедура ПриЗаписи, фактическая запись данных в таблицу базы данных (SQL-операция INSERT или UPDATE) уже произошла. Транзакция записи еще не завершена (не зафиксирована), но «образ» объекта уже передан в БД. Любые изменения свойств ЭтотОбъект внутри процедуры ПриЗаписи меняют состояние объекта только в оперативной памяти сервера 1С. Поскольку повторная автоматическая запись физической таблицы не вызывается, эти изменения просто не попадают в базу данных при завершении транзакции.
Почему же значение сохраняется при двойном нажатии кнопки «Записать»? Выясним причину: при первом нажатии код в ПриЗаписи срабатывает и меняет значение в памяти. При втором нажатии платформа видит уже измененный объект в памяти и записывает его актуальное состояние (с уже заполненным реквизитом) в базу данных. Это классический признак архитектурной ошибки в логике программы.
Для того чтобы реквизит был сохранен в базу данных за один проход, его необходимо заполнить в процедуре ПередЗаписью. Однако здесь возникает другая сложность: если объект новый, у него еще нет ссылки, и метод ЭтотОбъект.Ссылка.УникальныйИдентификатор() вернет пустой UUID.
Рассмотрим правильный алгоритм действий с использованием метода УстановитьСсылкуНового(). Этот метод позволяет программисту заранее определить, какая ссылка будет у объекта после его физического создания в базе данных.
Выполним следующие шаги в модуле объекта:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Проверяем, что объект новый и реквизит еще не заполнен
Если ЭтоНовый() И Не ЗначениеЗаполнено(ЭтотОбъект.ИдВнутренний) Тогда
// 1. Получаем ссылку, которая будет назначена объекту
НоваяСсылка = Документы.ИмяВашегоДокумента.ПолучитьСсылку();
// 2. Устанавливаем эту ссылку для текущего объекта
ЭтотОбъект.УстановитьСсылкуНового(НоваяСсылка);
// 3. Теперь мы можем получить UUID из этой будущей ссылки и записать в реквизит
ЭтотОбъект.ИдВнутренний = Строка(НоваяСсылка.УникальныйИдентификатор());
КонецЕсли;
КонецПроцедуры
Проанализируем ситуацию: теперь при записи платформа возьмет значение ИдВнутренний, подготовленное в ПередЗаписью, и отправит его в базу данных вместе со всеми остальными полями в одном SQL-пакете. Это надежно, быстро и не требует двойных записей.
Иногда возникают задачи, когда данные нельзя заполнить в ПередЗаписью (например, если логика зависит от результатов проведения или данных, которые появляются строго в момент фиксации). Разберем альтернативные подходы:
ПередЗаписью. Принцип работы с УстановитьСсылкуНового() там будет аналогичным.Посмотрим на ситуацию с копированием. Когда пользователь копирует существующий документ, реквизит ИдВнутренний будет скопирован вместе с остальными данными. Это приведет к тому, что у нового документа будет идентификатор старого, что нарушает логику уникальности.
Для предотвращения этой проблемы обязательно используйте обработчик ПриКопировании в модуле объекта:
Процедура ПриКопировании(ОбъектКопирования)
// Очищаем идентификатор, чтобы в ПередЗаписью сгенерировался новый
ЭтотОбъект.ИдВнутренний = "";
КонецПроцедуры
Стоит отметить, что начиная с версии платформы 8.3.23.1865, необходимость дублирования UUID в строковые реквизиты значительно снизилась. Теперь язык запросов 1С и Система компоновки данных (СКД) поддерживают функцию УникальныйИдентификатор() непосредственно в тексте запроса. Если ваша цель — просто выводить UUID в отчет или использовать его для связи в запросе, рассмотрите возможность обновления платформы. Это позволит избавиться от избыточного кода и лишних данных в базе, которые только увеличивают размер индексов и замедляют работу системы.
Резюмируем: Никогда не меняйте значения реквизитов текущего объекта в ПриЗаписи, если ожидаете их сохранения в текущей транзакции. Всегда используйте ПередЗаписью, а для получения ссылки нового объекта применяйте УстановитьСсылкуНового().