Почему процедура ПередЗаписью в расширении формы вызывается дважды и как это исправить?

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

При разработке расширений для современных конфигураций на базе БСП (таких как УТ 11, ERP 2 или КА 2) программисты часто сталкиваются с неприятным эффектом: процедура, описанная в расширении (например, с аннотацией &После("ПередЗаписью")), срабатывает два раза при одном нажатии кнопки «Записать». Это может привести к дублированию данных, лишним проверкам или ошибкам блокировки объектов.

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

Анализируем стек вызовов

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

  1. Установите точку останова в вашей процедуре в расширении.
  2. Дождитесь первого срабатывания и откройте окно «Стек вызовов» (Call Stack).
  3. Запишите или сфотографируйте последовательность функций.
  4. Нажмите «Продолжить» (F5) и при втором срабатывании снова изучите стек.

Проанализируем ситуацию: скорее всего, в первом случае вызов будет инициирован стандартной командой формы, а во втором — программным вызовом из недр типовых механизмов. Обратите внимание на наличие в стеке процедур типа ОбщегоНазначенияУТКлиент.ОбработатьЗаписьОбъектаВФорме или подключаемых команд. Если последовательности различаются, значит, система инициирует запись повторно для выполнения сопутствующих действий (например, обновления связанных данных или изменения статуса документа).

Влияние подписок на события и рекурсивная запись

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

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

Посмотрим на пример того, как делать нельзя:


// ОШИБОЧНЫЙ КОД в подписке
Процедура ПриЗаписиДокумента(Источник, Отказ) Экспорт
    Если Источник.Ответственный.Пустая() Тогда
        Источник.Ответственный = ПараметрыСеанса.ТекущийПользователь;
        Источник.Записать(); // Это вызовет повторный цикл событий записи!
    КонецЕсли;
КонецПроцедуры

Вызов Источник.Записать() инициирует новый цикл, который снова пройдет через ПередЗаписью в вашем расширении. Платформа имеет защиту от бесконечной рекурсии, но один дополнительный вызов все равно произойдет.

Особенности механизмов БСП в УТ 11

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


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

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

Способ решения №1: Использование флага в ДополнительныхСвойствах

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

  1. В начале вашей процедуры проверяйте наличие флага в ЭтотОбъект.ДополнительныеСвойства.
  2. Если флага нет — выполняйте код и устанавливайте флаг.
  3. Если флаг уже установлен — прерывайте выполнение.

Рассмотрим пример кода для расширения:


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

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

Способ решения №2: Анализ параметров записи

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

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


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

Способ решения №3: Перенос логики в модуль объекта

Работа с расширением формы (ПередЗаписью на клиенте или на сервере в контексте формы) всегда более подвержена двойным вызовам из-за интерактивных действий пользователя и логики элементов управления.

Посмотрим на ситуацию шире: если ваша задача заключается в заполнении реквизитов или проверке данных перед сохранением в базу, проанализируем возможность переноса этого кода в расширение модуля объекта. Событие ПередЗаписью в модуле объекта срабатывает непосредственно перед помещением данных в СУБД и гораздо меньше зависит от интерфейсных «наворотов» УТ 11.

Резюме

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

Следуя этим правилам, вы сделаете свои расширения более устойчивыми и производительными.

← На главную