Почему не сохраняются данные в реквизите при программном изменении во время записи?

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

При разработке в среде 1С:Предприятие, особенно на управляемых формах, часто возникает задача автоматического заполнения реквизитов документа непосредственно в момент его записи. Типичная ситуация: программист пишет код для заполнения реквизита, в отладчике видит, что значение присвоено, но после закрытия формы и повторного открытия поле оказывается пустым. Давайте подробно разберем, почему так происходит, изучим жизненный цикл записи объекта и найдем правильные способы решения этой задачи.

Суть проблемы: Отладчик показывает значение, но база его не видит

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

Частая ошибка, которую мы совершаем — попытка присвоить значение в обработчике события формы ПриЗаписиНаСервере следующим образом:


&НаСервере
Процедура ПриЗаписиНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    // ОШИБКА: Изменение данных формы после записи объекта
    Объект.ВнутреннийНомер = "СформированныйНомер"; 
КонецПроцедуры

Почему это не работает, даже если отладчик показывает, что в Объект.ВнутреннийНомер попало нужное значение? Давайте проанализируем последовательность событий.

Разбираем жизненный цикл записи документа

Чтобы понять причину, нам необходимо четко представлять порядок вызова событий при записи документа из управляемой формы. В упрощенном виде цепочка выглядит так:

  1. ПередЗаписью (Форма, Клиент) — здесь мы можем проверить данные перед отправкой на сервер.
  2. ПередЗаписьюНаСервере (Форма, Сервер) — последняя возможность изменить данные объекта до начала транзакции записи или в её начале (зависит от режима).
  3. ПередЗаписью (Модуль объекта) — событие внутри самого объекта (не формы). Здесь происходит основная логика проверок и дозаполнения.
  4. Запись в базу данных — физическое сохранение данных в таблицы БД.
  5. ПриЗаписи (Модуль объекта) — запись уже произошла, транзакция еще открыта.
  6. ПриЗаписиНаСервере (Форма, Сервер) — объект уже записан.

В коде, приведенном в примере с ошибкой, мы пытаемся изменить Объект (данные формы) в событии ПриЗаписиНаСервере (шаг 6). В этот момент физическая запись в базу данных (шаг 4) уже произошла.

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

Решение 1: Использование события ПередЗаписьюНаСервере и ТекущийОбъект

Логичным шагом кажется перенос кода в событие ПередЗаписьюНаСервере. Однако автор темы столкнулся с тем, что даже там данные не сохранялись. Давайте выясним почему.

В процедуре ПередЗаписьюНаСервере у нас есть два "объекта":

  1. Объект — это основной реквизит формы (ДанныеФормыСтруктура). Это то, что мы видим на экране.
  2. ТекущийОбъект — это параметр процедуры, представляющий собой реальный объект базы данных (ДокументОбъект), который готовится к записи.

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

Поэтому, если нам необходимо гарантированно изменить данные перед записью именно в модуле формы, мы должны обращаться к параметру ТекущийОбъект.

Давайте посмотрим на правильный код:


&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    // Правильный подход: меняем именно тот объект, который сейчас будет записан
    ТекущийОбъект.ВнутреннийНомер = СформироватьВнутреннийНомер();
КонецПроцедуры

Важно: После того как вы изменили ТекущийОбъект, данные на форме (в Объект) могут остаться старыми визуально до момента перечитывания формы. Однако в базу попадут именно данные из ТекущийОбъект.

Решение 2: Перенос логики в Модуль объекта (Рекомендуемое)

Хотя решение с формой рабочее, с точки зрения архитектуры правильнее выносить логику заполнения реквизитов в Модуль объекта. Почему это лучше?

Давайте рассмотрим, как это реализовать. Откройте модуль объекта документа и найдите процедуру ПередЗаписью.


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

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

В этом контексте ЭтотОбъект (или просто обращение к реквизитам по имени) гарантирует, что данные станут частью транзакции записи.

Сложность с номером документа (НомерДокумента)

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

Если ваша логика зависит от стандартного Номер, то в процедуре ПередЗаписью он может быть еще пустым (для нового документа).

Как быть в таком случае?

Вариант А: Принудительная установка номера.
Мы можем заставить систему выдать номер раньше времени в модуле объекта:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    Если ЭтоНовый() И ПустаяСтрока(Номер) Тогда
        УстановитьНовыйНомер(); // Принудительно генерируем стандартный номер
    КонецЕсли;
    
    // Теперь Номер заполнен, и мы можем использовать его
    ВнутреннийНомер = "Договор-" + Номер;

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

Вариант Б: Подписка на событие ПриУстановкеНовогоНомера.
Это более "чистый" метод, если вам нужно просто поймать момент появления номера.

Резюме и план действий

Давайте подведем итог и составим чек-лист для решения проблемы "несохраняющихся" реквизитов.

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

Следуя этим правилам, мы гарантируем целостность данных и предсказуемое поведение системы.

← На главную