Как проверить изменение реквизита при проведении документа и передать данные в модуль объекта в 1С

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

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

Почему нельзя проверять старое значение в процедуре ОбработкаПроведения

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

  1. Вызывается событие ПередЗаписью модуля объекта (данные в таблицах базы данных еще хранят старые значения).
  2. Вызывается событие ПриЗаписи модуля объекта. На этом этапе происходит непосредственная запись новых данных объекта в базу данных.
  3. Вызывается событие ОбработкаПроведения модуля объекта.

В момент, когда управление передается в процедуру ОбработкаПроведения, изменения уже зафиксированы в СУБД. Если попытаться обратиться к базе через Ссылка.ИмяРеквизита или выполнить запрос к таблице документа по ссылке, они вернут уже новое, только что записанное значение. Сравнение текущего реквизита со значением по ссылке внутри проведения всегда покажет, что значения идентичны.

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

Попытка перехватить изменение значения в событиях формы (например, при изменении поля или перед записью на клиенте) имеет критический недостаток: код формы выполняется исключительно при интерактивной работе пользователя. Если документ будет перепроводиться в рамках регламентного задания, групповой обработки, обмена данными через веб-сервисы или программного вызова ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение), форма открываться не будет. Вся логика проверки изменения реквизитов просто пропустится.

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

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

Разберем по шагам реализацию этого механизма:

  1. В модуле объекта в процедуре ПередЗаписью проверяем, что документ не является новым с помощью метода ЭтоНовый().
  2. Сравниваем текущее значение реквизита (содержится в свойствах объекта) со значением, которое в данный момент записано в базе данных (доступно через обращение к ссылке).
  3. Для передачи результата проверки в обработчик проведения используем системную коллекцию ДополнительныеСвойства.
  4. В процедуре ОбработкаПроведения считываем признак изменения из ДополнительныеСвойства и выполняем необходимые действия.

Посмотрим на пример кода в модуле объекта:


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

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

Оптимизация производительности: обращение через точку против запроса

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


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    Если Не ЭтоНовый() Тогда
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ
            |   Док.Контрагент КАК Контрагент
            |ИЗ
            |   Документ.РеализацияТоваровУслуг КАК Док
            |ГДЕ
            |   Док.Ссылка = &Ссылка";
        
        Запрос.УстановитьПараметр("Ссылка", Ссылка);
        Выборка = Запрос.Выполнить().Выбрать();
        
        Если Выборка.Следующий() Тогда
            Если Выборка.Контрагент <> Контрагент Тогда
                ДополнительныеСвойства.Вставить("КонтрагентИзменен", Истина);
            КонецЕсли;
        КонецЕсли;
    КонецЕсли;
    
КонецПроцедуры

Решение 2. Передача параметров из модуля формы в модуль объекта

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

Для управляемых форм передача выполняется через серверный обработчик ПередЗаписьюНаСервере с использованием параметра ТекущийОбъект:

  1. В модуле формы в процедуре ПередЗаписьюНаСервере обращаемся к свойству ДополнительныеСвойства объекта ТекущийОбъект.
  2. Помещаем туда необходимые переменные.
  3. В модуле объекта в процедурах ПередЗаписью или ОбработкаПроведения считываем переданные параметры.

Посмотрим на пример реализации в модуле управляемой формы:


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

В модуле объекта доступ к этим свойствам получается стандартным методом структуры:


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

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

← На главную