Почему не сохраняется значение реквизита объекта при записи из формы 1С?

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

Каждый разработчик 1С рано или поздно сталкивается с ситуацией, когда программно присвоенное значение реквизита управляемой формы (например, Объект.ИмяРеквизита = Значение) бесследно исчезает после нажатия кнопки «Записать». Вроде бы код отрабатывает без ошибок, отладчик на определенном этапе показывает, что значение заполнено, но после перезаписи или переоткрытия формы поле снова оказывается пустым.

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

Причина 1. Реквизит формы против реквизита объекта («синий» или «красный»)

Давайте начнем наш анализ с самого базового, но крайне важного аспекта работы с управляемыми формами — понимания контекста хранения данных. Откроем форму нашего объекта в конфигураторе и обратим внимание на правый верхний угол, где расположено дерево реквизитов формы.

В платформе 1С «Предприятие 8» реквизиты в этом дереве имеют цветовую индикацию, которая наглядно показывает их природу:

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

Причина 2. Реквизит не выведен на форму и оптимизация платформы

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

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

Чтобы решить эту проблему, выполним следующие действия:

  1. Откроем форму объекта в конфигураторе.
  2. В дереве реквизитов формы найдем наш реквизит внутри структуры Объект.
  3. Откроем палитру свойств этого реквизита (нажатием клавиши F4).
  4. Найдем свойство «Использовать всегда» (в английской версии — Use always) и установим для него значение Истина (поставим галочку).

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

Причина 3. Ошибка в выборе обработчика события записи

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

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


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

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

Правильным решением будет перенос логики заполнения реквизитов в обработчики группы ПередЗаписью. Разберем корректный пример кода:


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

Если вы выполняете присвоение на клиенте, используйте стандартный предопределенный обработчик формы ПередЗаписью:


&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
    // Работаем с реквизитом через структуру Объект на клиенте
    Объект.ЛеваяПочка = ПредопределенноеЗначение("Справочник.СостоянияОрганов.Отрезать");
КонецПроцедуры

Причина 4. Изменение значения через Ссылку вместо Объекта

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

Попытка записать значение «через ссылку» напрямую в коде выглядит следующим образом и является грубой ошибкой:


// Данный код вызовет ошибку "Поле объекта недоступно для записи"
Объект.Ссылка.ЛеваяПочка = ЗначениеЗаполнения; 

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


&НаСервереБезКонтекста
Процедура ИзменитьРеквизитОбъектаВБазе(СсылкаНаОбъект, НовоеЗначение)
    
    // Получаем объект для записи
    ПолученныйОбъект = СсылкаНаОбъект.ПолучитьОбъект();
    
    Если ПолученныйОбъект <> Неопределено Тогда
        ПолученныйОбъект.ЛеваяПочка = НовоеЗначение;
        // Записываем изменения непосредственно в базу данных
        ПолученныйОбъект.Записать();
    КонецЕсли;
    
КонецПроцедуры

Причина 5. Затирание значения в модуле объекта или подписках на события

Если мы проверили все предыдущие пункты: реквизит «синий», флаг «Использовать всегда» установлен, присвоение происходит в процедуре ПередЗаписью, но данные все равно исчезают после записи — значит, значение затирается на более поздних этапах транзакции.

Проанализируем, где именно может происходить очистка данных. Нам необходимо проверить два ключевых места в конфигурации:

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

Для быстрой диагностики рекомендуется использовать отладчик. Установим точку останова в процедуре ПередЗаписью модуля формы, затем в процедуре ПередЗаписью модуля объекта и в подписках на события. Пошагово проходя код клавишей F8, мы сможем поймать тот самый момент, когда заполненное значение реквизита очищается или заменяется на пустое значение.

← На главную