Почему процедура в расширении 1С выполняется дважды и как это исправить?

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

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

Анализируем ситуацию: симптомы и причины

Типичный сценарий проблемы выглядит так: создается расширение для существующей формы документа или справочника. Программист захватывает обработчик события ПриОткрытии и использует аннотацию &После (&After). Внутри размещается простой код, например, заполнение колонки в табличной части:


Процедура Расш1_ПриОткрытииПосле(Отказ)
    Для каждого итератор из Объект.Товары Цикл
        итератор.ПроцентПрибыли = 5;
        Сумма = Сумма + итератор.ПроцентПрибыли;
    КонецЦикла;
КонецПроцедуры

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

Основными причинами такого поведения являются:

  1. Механизм подключаемых команд БСП: В современных типовых конфигурациях (УТ 11, ERP, КА 2) используется Библиотека стандартных подсистем. Процедура ПодключаемыеКомандыКлиент.НачатьОбновлениеКоманд инициирует обновление интерфейса формы. Это может приводить к повторному срабатыванию цепочки событий при открытии формы.
  2. Особенности аннотации &После: Когда мы используем расширение, наш код встраивается в общую логику выполнения. Если основная конфигурация или другое расширение внутри ПриОткрытии выполняет действия, требующие обновления контекста формы, клиентская часть может «перерисовать» форму, что визуально в отладчике выглядит как повторный вход.
  3. Двойная привязка обработчика: Иногда процедура может быть назначена обработчиком события непосредственно в свойствах формы в расширении и одновременно содержать аннотацию &После. В этом случае система честно выполнит её два раза.

Почему изменять данные в «ПриОткрытии» — плохая идея?

Прежде чем исправлять двойной заход, разберем, почему сам подход изменения данных в событии ПриОткрытии является некорректным с точки зрения архитектуры 1С. Посмотрим на пример кода выше. Программист устанавливает ПроцентПрибыли = 5 для каждой строки при каждом открытии формы.

Во-первых, это событие происходит на клиенте. Изменение данных объекта (Объект.Товары) приводит к тому, что форма сразу помечается как модифицированная (свойство Модифицированность = Истина). Пользователь только открыл документ, ничего не менял, но при закрытии система настойчиво предложит сохранить изменения.

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

Решение №1: Перенос логики на сервер (правильный путь)

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

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


&НаСервере
&После("ПриСозданииНаСервере")
Процедура Расш1_ПриСозданииНаСервереПосле(Отказ, СтандартнаяОбработка)
    // Код выполняется один раз на сервере
    Для каждого СтрокаТовар из Объект.Товары Цикл
        СтрокаТовар.ПроцентПрибыли = 5;
    КонецЦикла;
    // Суммирование лучше делать через агрегатные функции ТЧ или Итог()
КонецПроцедуры

Решение №2: Использование программного флага (Workaround)

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

Добавим в модуль формы переменную, которая будет хранить состояние выполнения нашего кода. Важно объявить её так, чтобы она была доступна в контексте модуля формы.


&НаКлиенте
Переменная_ДанныеЗаполнены = Ложь;

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

Этот метод гарантирует, что даже если платформа по какой-то причине (например, из-за инициализации подключаемых команд БСП) вызовет обработчик события повторно, ваш тяжелый цикл или логика расчетов не запустятся второй раз.

Решение №3: Проверка стека вызовов и очистка кэша

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

  1. Проверка стека: В момент «второго захода» в процедуру, откройте в конфигураторе панель «Стек вызовов». Посмотрите, какая системная процедура инициировала вызов. Если это НачатьОбновлениеКоманд, то это штатное поведение БСП, и вам нужно использовать Решение №1 или №2.
  2. Очистка кэша: Закройте 1С, удалите временные файлы пользователя (папки Local AppData и Roaming в профиле пользователя Windows, связанные с 1С). Иногда старые версии расширений «застревают» в кэше и вызывают конфликты обработчиков.
  3. Проверка свойств формы: Убедитесь, что в дереве расширения у формы в поле «При открытии» не стоит имя вашей процедуры Расш1_ПриОткрытииПосле. Если оно там есть, и при этом у процедуры стоит аннотация &После, то вы сами создали двойную привязку.

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

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

Соблюдение этих правил сделает ваш код в расширениях более надежным, а работу пользователей с программой — более комфортной и быстрой.

← На главную