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

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

Часто начинающие разработчики сталкиваются с ситуацией: в коде реквизиту документа присваивается новое значение, но после выполнения этого кода в базе данных остаются старые данные. Кажется, что платформа "не слушается" и не выполняет присваивание. Давайте разберем, почему так происходит и как это исправить.

Основной вопрос, который мы рассмотрим: почему после выполнения кода вида Док.Ответственный = ТекущийПользователь поле "Ответственный" в документе остается пустым или не меняется?

Анализ проблемы: объект в памяти и запись в базу данных

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

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

Именно эта команда — вызов метода .Записать() — и была пропущена в исходной ситуации.

Пример ошибочного кода

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


&НаСервере
Процедура ЗаписатьИсправленныеДанныеНаСервере()

    // ... код запроса для получения регистратора ...
    Выборка = РезультатЗапроса.Выбрать();

    Если Выборка.Следующий() Тогда
        // 1. Получаем объект документа в память
        Док = Выборка.Регистратор.ПолучитьОбъект();

        // 2. Изменяем реквизит у объекта в памяти
        Док.Ответственный = Пользователи.ТекущийПользователь();

        // 3. ОШИБКА: отсутствует запись изменений самого документа
        // Док.Записать();  <-- ЭТА СТРОКА ПРОПУЩЕНА!
    КонецЕсли;

    // Далее идет работа с регистром, которая выполняется корректно,
    // но сам документ "Док" остается неизменным в базе данных
    НаборЗаписей = РегистрыСведений.РеестрКадровыхПриказов.СоздатьНаборЗаписей();
    НаборЗаписей.Отбор.Регистратор.Установить(Док.Ссылка);
    // ...
    НаборЗаписей.Записать();
    // ...

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

Как мы видим, после строки Док.Ответственный = Пользователи.ТекущийПользователь(); нет вызова Док.Записать();. В результате все изменения, внесенные в объект Док, так и остались в памяти и не были сохранены в базу данных.

Правильное решение: добавляем метод .Записать()

Чтобы исправить ситуацию, достаточно добавить одну строку кода. Этот метод явно указывает системе, что все изменения, сделанные в объекте Док, необходимо сохранить.

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


&НаСервере
Процедура ЗаписатьИсправленныеДанныеНаСервере()

    // ... код запроса для получения регистратора ...
    Выборка = РезультатЗапроса.Выбрать();

    Если Выборка.Следующий() Тогда
        // 1. Получаем объект документа в память
        Док = Выборка.Регистратор.ПолучитьОбъект();

        // 2. Изменяем реквизит у объекта в памяти
        Док.Ответственный = Пользователи.ТекущийПользователь();
        // Можно изменить и другие реквизиты, если нужно
        // Док.Комментарий = "Ответственный изменен обработкой";

        // 3. ПРАВИЛЬНО: Сохраняем все изменения объекта в базу данных
        Док.Записать(); 
    КонецЕсли;

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

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

После добавления Док.Записать(); значение реквизита "Ответственный" будет корректно сохранено.

Дополнительные важные моменты

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

  1. Режимы записи документа

    Метод Записать() может принимать параметр, указывающий на режим записи. Основные режимы:

    • РежимЗаписиДокумента.Запись — просто сохраняет документ без создания движений по регистрам. Это значение по умолчанию.
    • РежимЗаписиДокумента.Проведение — сохраняет документ и выполняет его проведение (формирует движения).
    • РежимЗаписиДокумента.ОтменаПроведения — сохраняет документ и отменяет его проведение (удаляет движения).

    В нашем случае, когда нужно просто изменить реквизит шапки, достаточно вызова Док.Записать();, что эквивалентно Док.Записать(РежимЗаписиДокумента.Запись);.

  2. Получение текущего пользователя

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

    • Пользователи.ТекущийПользователь() — универсальный метод для получения текущего пользователя, особенно актуальный при использовании Библиотеки стандартных подсистем (БСП).
    • ПараметрыСеанса.ТекущийПользователь — также надежный способ, возвращает пользователя, определенного в параметрах текущего сеанса.
  3. Конструкция Попытка-Исключение

    На этапе отладки избегайте "пустых" конструкций Попытка ... Исключение ... КонецПопытки. Если внутри блока Попытка произойдет ошибка (например, сработает проверка заполнения или не хватит прав на запись), блок Исключение ее "проглотит", и вы не увидите сообщение об ошибке. Это сильно затрудняет поиск проблемы. Используйте эту конструкцию только тогда, когда вы точно знаете, какую ошибку ожидаете, и хотите ее обработать особым образом.

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

← На главную