Почему не сохраняются данные при нажатии «Провести и закрыть» и как правильно записывать реквизиты на сервере

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

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

Проанализируем ситуацию: в чем корень проблемы?

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

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

Разберем разницу между Объект и ТекущийОбъект

Это ключевой момент, который нужно осознать для успешной работы с управляемыми формами. В модуле формы существуют две разные сущности:

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

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

Посмотрим на жизненный цикл записи (Событийный марафон)

Чтобы понять, где именно нужно писать код, проанализируем последовательность событий при нажатии «Провести и закрыть»:

  1. На сервере: ПередЗаписьюНаСервере (Форма). Это последний шанс внести изменения в данные перед тем, как они уйдут в базу. Здесь у нас есть доступ к ТекущийОбъект.
  2. В транзакции: ПередЗаписью (Модуль объекта). Здесь объект уже находится в состоянии записи.
  3. Запись в БД. Данные физически фиксируются в таблицах.
  4. В транзакции: ПриЗаписи (Модуль объекта). Объект уже записан, но транзакция еще не завершена.
  5. На сервере: ПриЗаписиНаСервере (Форма). Здесь и была ошибка автора! В этом обработчике данные уже записаны в БД. Если вы здесь измените Объект, форма обновится, но база данных — нет. Чтобы сохранить изменения, сделанные здесь, пришлось бы снова вызывать ТекущийОбъект.Записать(), что привело бы к бесконечной рекурсии.
  6. ОбработкаПроведения (Модуль объекта). Если нажато «Провести», выполняется этот код.
  7. Завершение транзакции.

Решение проблемы №1: Правильное использование ПередЗаписьюНаСервере

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

Рассмотрим пример кода, который решает проблему:


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

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

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

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

Рассмотрим ситуацию глубже. Если вы прописываете логику заполнения полей только в форме, то при создании документа программно (например, через загрузку из Excel или обмен данными) этот код не сработает. Формы просто не будет, а значит, поле останется пустым.

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


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

При таком подходе не имеет значения, как записывается документ: кнопкой «Записать», «Провести и закрыть» или программным методом ДокументОбъект.Записать(). Поля всегда будут заполнены корректно.

Использование ПараметровЗаписи для тонкой настройки

Иногда требуется, чтобы данные записывались только при проведении документа, но не трогались при обычном сохранении черновика. В процедуре ПередЗаписьюНаСервере для этого есть специальный параметр ПараметрыЗаписи. Разберем, как его использовать:


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

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

Подведем итоги

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

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

← На главную