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

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

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

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


Разберем транзакционный цикл записи и проведения документа

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

Упрощенная схема жизненного цикла записи и проведения документа выглядит следующим образом:

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

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


Почему категорически нельзя вызывать метод Записать() внутри проведения?

У начинающих разработчиков часто возникает соблазн обойти это ограничение «в лоб» — принудительно вызвать метод Записать() прямо внутри обработчика проведения. Давайте посмотрим на пример такого ошибочного кода:


Процедура ОбработкаПроведения_Подписка(Источник, Отказ, РежимПроведения) Экспорт
    
    Источник.Б = 1;
    // КАТЕГОРИЧЕСКАЯ ОШИБКА! ТАК ДЕЛАТЬ НЕЛЬЗЯ!
    Источник.Записать(РежимЗаписиДокумента.Запись); 
    
КонецПроцедуры

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


Архитектурно верные способы решения проблемы

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

Способ 1. Заполнение реквизита на этапе «ПередЗаписью» (Рекомендуемый)

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

Рассмотрим пример реализации кода в общем модуле:


Процедура ПередЗаписьюДокумента_Подписка(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
    
    // Проверяем, что документ записывается именно в режиме проведения
    Если РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда
        
        // Заполняем наш реквизит. Платформа запишет его автоматически на следующем шаге.
        Источник.Б = 1; 
        
    КонецЕсли;
    
КонецПроцедуры

В данном случае транзакция отработает корректно: значение запишется в базу данных СУБД вместе со всеми остальными реквизитами шапки документа.

Способ 2. Использование регистров сведений для динамических данных

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

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

  1. Создадим вспомогательный регистр сведений (например, СтатусыДокументов или ДополнительныеПоказателиПроведения) с измерением Документ (тип — ссылка на наш документ) и ресурсом ЗначениеПоказателя.
  2. В процессе проведения документа в подписке на ОбработкаПроведения сформируем движение по этому регистру сведений.
  3. При необходимости отображения этого значения на форме документа мы сможем динамически считывать его легким запросом при открытии или обновлении формы.

Посмотрим на пример кода формирования движений в регистре сведений во время проведения:


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

Такой подход полностью соответствует стандартам разработки, не вызывает конфликтов блокировок и гарантирует целостность данных.

Способ 3. Передача параметров через «ДополнительныеСвойства»

Если нам требуется связать логику этапа ПередЗаписью и этапа ОбработкаПроведения, мы можем воспользоваться встроенным свойством объекта ДополнительныеСвойства. Это структура, которая существует в оперативной памяти на протяжении всего жизненного цикла объектной сущности в рамках транзакции.

Проанализируем пример передачи информации:


// В обработчике ПередЗаписью:
Источник.ДополнительныеСвойства.Вставить("ВыполнитьОсобоеПроведение", Истина);

// В обработчике ОбработкаПроведения:
Если Источник.ДополнительныеСвойства.Свойство("ВыполнитьОсобоеПроведение") Тогда
    // Выполняем специфические алгоритмы проведения на основе переданного параметра
КонецЕсли;

Контекст использования в типовых конфигурациях (УНФ, ERP, БСП)

При работе в современных конфигурациях, таких как «1С:Управление нашей фирмой» (УНФ), крайне важно помнить, что они построены на базе Библиотеки стандартных подсистем (БСП). Механизмы проведения в БСП оптимизированы для достижения максимальной параллельности работы пользователей. Изменение реквизитов документа во время проведения «на лету» способно нарушить работу механизмов версионирования объектов, вызвать рассинхронизацию при обмене данными в распределенных информационных базах (РИБ) и привести к некорректной регистрации изменений для планов обмена.

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

← На главную