Почему не записывается новый реквизит документа в 1С и как это исправить?

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

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

Анализируем исходную проблему: почему код не работал?

Давайте обратимся к фрагменту кода, который вызвал затруднения у автора темы на форуме:


&НаСервере
Процедура ЗаписатьОб()
    УстановитьПривилегированныйРежим(Истина);
    Объект.Ссылка.ПолучитьОбъект().ВыгруженвXML = Истина;
    Объект.Ссылка.ПолучитьОбъект().Записать(РежимЗаписиДокумента.Проведение);
КонецПроцедуры

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

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

В первой строке кода:

  1. Конструкция Объект.Ссылка.ПолучитьОбъект() создает в памяти Экземпляр №1 нашего документа.
  2. В свойство ВыгруженвXML этого Экземпляра №1 записывается значение Истина.
  3. Поскольку созданный объект не был присвоен никакой переменной, ссылка на него сразу же теряется. Платформа понимает, что объект больше нигде в коде не используется, и сборщик мусора немедленно удаляет Экземпляр №1 из памяти. Изменения в базу данных при этом физически не записываются.

Во второй строке кода:

  1. Конструкция Объект.Ссылка.ПолучитьОбъект() отрабатывает заново! Она опять обращается к СУБД и создает совершенно другой Экземпляр №2 того же самого документа. В базе данных реквизит ВыгруженвXML все еще равен Ложь, поэтому новый объект считывается со старым значением.
  2. Для этого Экземпляра №2 (в котором реквизит равен Ложь) вызывается метод Записать(РежимЗаписиДокумента.Проведение).
  3. Документ успешно проводится, но в базу данных записывается старое значение реквизита.

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

Скрытая угроза: Игнорирование контекста управляемой формы

Предположим, разработчик исправил явную ошибку и написал код с использованием одной переменной-посредника на сервере:


&НаСервере
Процедура ЗаписатьОб()
    УстановитьПривилегированныйРежим(Истина);
    ДокОбъект = Объект.Ссылка.ПолучитьОбъект();
    ДокОбъект.ВыгруженвXML = Истина;
    ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
КонецПроцедуры

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

Когда пользователь работает в интерактивном режиме с формой документа, на клиенте открыт и запущен объект типа ДанныеФормыСтруктура (в коде это переменная Объект). Если мы параллельно «за спиной» у формы получаем объект напрямую из базы данных через метод ПолучитьОбъект(), меняем его и записываем, то данные в открытой форме на клиенте остаются старыми. Там реквизит ВыгруженвXML по-прежнему равен значению Ложь.

Это неизбежно приведет к двум неприятным последствиям:

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

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

Способ 1. Изменение данных формы (Наиболее простой и надежный вариант)

Если наша задача — изменить реквизит документа прямо во время работы пользователя с формой, нам абсолютно не требуется обращаться напрямую к базе данных и использовать метод ПолучитьОбъект(). Гораздо проще и правильнее работать непосредственно с реквизитами формы.

Посмотрим на пример правильной реализации этого механизма:


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

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

Преимущества данного подхода:

Способ 2. Использование связки «РеквизитФормыВЗначение» и «ЗначениеВРеквизитФормы»

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

В этом случае мы должны использовать специальные встроенные функции взаимного преобразования контекста. Давайте проанализируем пример такого решения:


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

Благодаря вызову метода ЗначениеВРеквизитФормы, форма на клиенте мгновенно узнает обо всех изменениях, произошедших на сервере. Реквизит на экране обновит свое визуальное представление, а пользователь сможет продолжить комфортную работу с документом без каких-либо ошибок блокировок.

Резюме и правила хорошего тона для разработчика

Подводя итог, давайте зафиксируем основные правила работы с реквизитами объектов в управляемом приложении 1С:

← На главную