Почему не сохраняется значение реквизита в процедуре ПриЗаписи и как это исправить

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

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

Разбор проблемы: почему ПриЗаписи — это «поздно»

Рассмотрим типичную ситуацию: разработчику необходимо сохранить уникальный идентификатор (UUID) ссылки в отдельный строковый реквизит ИдВнутренний для целей интеграции или построения сложных отчетов. Код пишется в обработчике ПриЗаписи, так как именно в этот момент у нового объекта уже гарантированно есть ссылка.

Проанализируем, что происходит внутри платформы в этот момент. Когда выполняется процедура ПриЗаписи, фактическая запись данных в таблицу базы данных (SQL-операция INSERT или UPDATE) уже произошла. Транзакция записи еще не завершена (не зафиксирована), но «образ» объекта уже передан в БД. Любые изменения свойств ЭтотОбъект внутри процедуры ПриЗаписи меняют состояние объекта только в оперативной памяти сервера 1С. Поскольку повторная автоматическая запись физической таблицы не вызывается, эти изменения просто не попадают в базу данных при завершении транзакции.

Почему же значение сохраняется при двойном нажатии кнопки «Записать»? Выясним причину: при первом нажатии код в ПриЗаписи срабатывает и меняет значение в памяти. При втором нажатии платформа видит уже измененный объект в памяти и записывает его актуальное состояние (с уже заполненным реквизитом) в базу данных. Это классический признак архитектурной ошибки в логике программы.

Правильное решение: использование события ПередЗаписью

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

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

Выполним следующие шаги в модуле объекта:


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

КонецПроцедуры

Проанализируем ситуацию: теперь при записи платформа возьмет значение ИдВнутренний, подготовленное в ПередЗаписью, и отправит его в базу данных вместе со всеми остальными полями в одном SQL-пакете. Это надежно, быстро и не требует двойных записей.

Альтернативные методы и нюансы

Иногда возникают задачи, когда данные нельзя заполнить в ПередЗаписью (например, если логика зависит от результатов проведения или данных, которые появляются строго в момент фиксации). Разберем альтернативные подходы:

  1. Подписки на события: Если логику нужно применить ко многим документам сразу, используйте подписку на событие ПередЗаписью. Принцип работы с УстановитьСсылкуНового() там будет аналогичным.
  2. Использование Дополнительных сведений (БСП): Если вы не хотите менять структуру метаданных (добавлять реквизиты), можно использовать механизм «Дополнительных сведений» из Библиотеки стандартных подсистем. Однако помните, что их запись обычно инициируется отдельно, часто после записи основного объекта.
  3. Регламентные задания: Для массового заполнения пропущенных идентификаторов в старых документах можно настроить фоновое задание, которое будет находить пустые значения и дозаполнять их в периоды низкой нагрузки на систему.

Важное замечание про копирование объектов

Посмотрим на ситуацию с копированием. Когда пользователь копирует существующий документ, реквизит ИдВнутренний будет скопирован вместе с остальными данными. Это приведет к тому, что у нового документа будет идентификатор старого, что нарушает логику уникальности.

Для предотвращения этой проблемы обязательно используйте обработчик ПриКопировании в модуле объекта:


Процедура ПриКопировании(ОбъектКопирования)
    // Очищаем идентификатор, чтобы в ПередЗаписью сгенерировался новый
    ЭтотОбъект.ИдВнутренний = "";
КонецПроцедуры

Современный подход в платформе 8.3.23+

Стоит отметить, что начиная с версии платформы 8.3.23.1865, необходимость дублирования UUID в строковые реквизиты значительно снизилась. Теперь язык запросов 1С и Система компоновки данных (СКД) поддерживают функцию УникальныйИдентификатор() непосредственно в тексте запроса. Если ваша цель — просто выводить UUID в отчет или использовать его для связи в запросе, рассмотрите возможность обновления платформы. Это позволит избавиться от избыточного кода и лишних данных в базе, которые только увеличивают размер индексов и замедляют работу системы.

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

← На главную