Каждый разработчик 1С рано или поздно сталкивается с ситуацией, когда программно присвоенное значение реквизита управляемой формы (например, Объект.ИмяРеквизита = Значение) бесследно исчезает после нажатия кнопки «Записать». Вроде бы код отрабатывает без ошибок, отладчик на определенном этапе показывает, что значение заполнено, но после перезаписи или переоткрытия формы поле снова оказывается пустым.
В этой статье мы подробно разберем, почему так происходит, изучим внутренние механизмы работы управляемого приложения 1С и рассмотрим пошаговые инструкции по устранению этой проблемы.
Давайте начнем наш анализ с самого базового, но крайне важного аспекта работы с управляемыми формами — понимания контекста хранения данных. Откроем форму нашего объекта в конфигураторе и обратим внимание на правый верхний угол, где расположено дерево реквизитов формы.
В платформе 1С «Предприятие 8» реквизиты в этом дереве имеют цветовую индикацию, которая наглядно показывает их природу:
Объект, имеющий тип ДанныеФормыСтруктура). Только данные этих реквизитов автоматически записываются в базу данных при стандартном вызове команды записи формы.Если мы случайно создали реквизит в дереве реквизитов формы (сделали его «красным») и пытаемся записать его значение в базу данных напрямую, платформа этого сделать не сможет, так как в самой структуре таблицы базы данных этого поля просто не существует. Выясним, связан ли ваш реквизит с объектом метаданных. Если он «красный», то для сохранения данных вам необходимо создать соответствующий реквизит в самом объекте конфигурации (в дереве метаданных справочника или документа), а затем перетащить его на форму в состав реквизита Объект.
Проанализируем следующую ситуацию: реквизит действительно является частью объекта (он «синий»), но на самой форме в виде элемента интерфейса (поля ввода, флажка и т.д.) он отсутствует. Как подтвердил автор исходного обращения на форуме, это довольно частый сценарий, когда реквизит заполняется исключительно программным путем на основе внутренних алгоритмов системы.
Здесь вступает в силу механизм оптимизации передачи данных между клиентом и сервером. Чтобы не гонять по сети лишние «тяжелые» данные, платформа 1С по умолчанию может исключать из клиентского контекста те реквизиты объекта, которые физически не выведены на форму. В результате при передаче данных обратно на сервер для записи измененное на клиенте значение может просто потеряться.
Чтобы решить эту проблему, выполним следующие действия:
Объект.Этот флаг принудительно указывает платформе, что значение реквизита должно полноценно участвовать в обмене данными между клиентом и сервером при любых вызовах контекстных процедур, даже если элемента управления для него на форме нет.
Рассмотрим еще одну распространенную ошибку, связанную с жизненным циклом записи объекта из управляемой формы. Разработчики часто путают моменты времени, когда вызываются процедуры ПередЗаписью и ПриЗаписи.
Посмотрим на типичный пример неправильного кода:
&НаСервере
Процедура ПриЗаписиНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
// Ошибка! Физическая запись в базу данных уже произошла
ТекущийОбъект.ЛеваяПочка = Справочники.СостоянияОрганов.Отрезать;
КонецПроцедуры
Почему этот код не работает? Дело в том, что в обработчиках группы ПриЗаписи (как на клиенте, так и на сервере в событии ПриЗаписиНаСервере) транзакция записи уже практически завершена. Объект базы данных уже зафиксирован на диске. Да, мы меняем свойство в оперативной памяти (в переменной ТекущийОбъект), и в этот момент в отладчике значение действительно будет заполнено. Но повторной отправки этих данных в СУБД уже не происходит! При закрытии формы или её обновлении данные зачитаются из базы заново, и наше изменение пропадет.
Правильным решением будет перенос логики заполнения реквизитов в обработчики группы ПередЗаписью. Разберем корректный пример кода:
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
// Правильно! Значение присваивается ДО отправки данных в СУБД
ТекущийОбъект.ЛеваяПочка = Справочники.СостоянияОрганов.Отрезать;
КонецПроцедуры
Если вы выполняете присвоение на клиенте, используйте стандартный предопределенный обработчик формы ПередЗаписью:
&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
// Работаем с реквизитом через структуру Объект на клиенте
Объект.ЛеваяПочка = ПредопределенноеЗначение("Справочник.СостоянияОрганов.Отрезать");
КонецПроцедуры
Иногда начинающие программисты путают два принципиально разных понятия: ссылку на объект базы данных (тип СправочникСсылка, ДокументСсылка) и сам объект метаданных, доступный для записи (тип СправочникОбъект, ДокументОбъект).
Попытка записать значение «через ссылку» напрямую в коде выглядит следующим образом и является грубой ошибкой:
// Данный код вызовет ошибку "Поле объекта недоступно для записи"
Объект.Ссылка.ЛеваяПочка = ЗначениеЗаполнения;
Ссылка предназначена исключительно для чтения данных. Чтобы изменить данные существующего в базе объекта программно (например, во внешней обработке или серверной процедуре общего модуля), нам обязательно нужно получить его объектное представление, изменить реквизит и явно вызвать метод Записать(). Посмотрим на пример:
&НаСервереБезКонтекста
Процедура ИзменитьРеквизитОбъектаВБазе(СсылкаНаОбъект, НовоеЗначение)
// Получаем объект для записи
ПолученныйОбъект = СсылкаНаОбъект.ПолучитьОбъект();
Если ПолученныйОбъект <> Неопределено Тогда
ПолученныйОбъект.ЛеваяПочка = НовоеЗначение;
// Записываем изменения непосредственно в базу данных
ПолученныйОбъект.Записать();
КонецЕсли;
КонецПроцедуры
Если мы проверили все предыдущие пункты: реквизит «синий», флаг «Использовать всегда» установлен, присвоение происходит в процедуре ПередЗаписью, но данные все равно исчезают после записи — значит, значение затирается на более поздних этапах транзакции.
Проанализируем, где именно может происходить очистка данных. Нам необходимо проверить два ключевых места в конфигурации:
ПередЗаписью. Очень часто там пишется общая логика проверки заполнения или сброса реквизитов при невыполнении каких-либо условий.ПередЗаписьюДокументов), которые выполняют дополнительную обработку объектов данного типа перед их сохранением в СУБД. Нам нужно найти эти подписки, проанализировать их код и убедиться, что они не очищают наш реквизит.Для быстрой диагностики рекомендуется использовать отладчик. Установим точку останова в процедуре ПередЗаписью модуля формы, затем в процедуре ПередЗаписью модуля объекта и в подписках на события. Пошагово проходя код клавишей F8, мы сможем поймать тот самый момент, когда заполненное значение реквизита очищается или заменяется на пустое значение.