Перехват и подмена кода типовой процедуры через расширение конфигурации

· 9 мин

Содержание

Чтобы заменить процедуру в расширении 1С, заимствуйте объект с нужным модулем в расширение, откройте модуль основной конфигурации, щёлкните правой кнопкой по процедуре и выберите Добавить в расширение. В диалоге укажите тип вызова «Вместо» — платформа создаст в расширении процедуру с аннотацией &Вместо("ИмяПроцедуры"). Её код будет выполняться вместо типового, а исходную логику при необходимости вызывают через ПродолжитьВызов().

Если нужно поменять не всю процедуру, а пару строк внутри, выбирайте «Изменение и контроль» (&ИзменениеИКонтроль, платформа 8.3.11 и выше). Он копирует тело процедуры в расширение, и правки отмечаются директивами #Вставка и #Удаление. Типовая конфигурация при этом остаётся на поддержке и обновляется без ручного объединения.

Что проверить до замены: архитектура расширения и поиск процедуры

Расширения появились в платформе 8.3.6, но механизм перехвата методов с аннотациями стал удобным позже. Для &ИзменениеИКонтроль нужна платформа с номером не ниже 8.3.11. Режим совместимости расширения не может быть новее режима совместимости основной конфигурации.

Перед правкой ответьте себе на три вопроса:

  • 🔍 Где живёт код. Модуль объекта, модуль менеджера, модуль формы или общий модуль — от этого зависит, что именно заимствовать.
  • 🧭 Как процедура вызывается. Обработчик события формы, обработчик события объекта, экспортный метод, который зовут из других модулей, или локальная процедура.
  • 🧩 Нужна ли исходная логика. Если типовой код нужен целиком, а вы добавляете действие до или после — замена не нужна, хватит &Перед или &После.

Как найти нужную процедуру

Самый быстрый путь — глобальный поиск в Конфигураторе: Правка → Глобальный поиск (Ctrl+Shift+F). Ищите по тексту сообщения, которое видит пользователь, или по имени реквизита.

Второй способ — отладчик. Поставьте точку останова в предполагаемом месте, выполните действие в режиме 1С:Предприятия и посмотрите стек вызовов (Отладка → Стек вызовов, Ctrl+Alt+C). Стек показывает всю цепочку: какая форма вызвала какой общий модуль и в какой строке.

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

Создание расширения

Если расширения ещё нет: Конфигурация → Расширения конфигурации, кнопка «Добавить». Задайте имя, префикс (например, Расш1_) и назначение. Для правок типового поведения подходит назначение «Исправление» или «Адаптация».

Четыре способа перехватить метод и порядок их срабатывания

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

АннотацияЧто происходитКогда выбиратьРиск при обновлении
&Перед("Имя")Ваш код, затем типовойПроверка, подготовка данныхНизкий
&После("Имя")Типовой код, затем вашДозаполнение, доп. действияНизкий
&Вместо("Имя")Только ваш код; типовой — если вызвать ПродолжитьВызов()Логику нужно переписать или вызвать условноСредний: типовые исправления не попадут в вашу версию
&ИзменениеИКонтроль("Имя")Копия типового кода с вашими вставкамиПоменять несколько строк в серединеКонтролируемый: при изменении исходника расширение перестанет применяться

ПродолжитьВызов: условная замена

&Вместо не обязательно отключает типовую логику полностью. Внутри можно решить, звать ли оригинал:

&Вместо("ПроверитьЗаполнениеСклада")
Процедура Расш1_ПроверитьЗаполнениеСклада(Отказ)

    Если ЭтотОбъект.Комментарий = "#БезСклада" Тогда
        Возврат; // типовая проверка не выполняется
    КонецЕсли;

    ПродолжитьВызов(Отказ); // во всех остальных случаях работает типовой код

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

Для функции ПродолжитьВызов() возвращает её результат. Его можно скорректировать и вернуть своё значение. Параметры перед вызовом тоже можно изменить — оригинал получит уже изменённые.

Подписки на события вместо перехвата

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

Подписка удобна, когда одно правило нужно навесить на десяток документов. Перехват удобнее, когда правка касается одного объекта и нужен доступ к его внутренним процедурам.

Если расширений несколько

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

Замена кода в модуле объекта: пошаговая инструкция

Разберём на примере модуля документа. Для модуля менеджера и модуля формы порядок тот же, отличается только то, какой модуль вы открываете.

Шаг 1. Заимствуйте объект

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

Для формы заимствуйте саму форму: правой кнопкой по форме — Добавить в расширение. Заимствованная форма тянет за собой владельца.

Шаг 2. Откройте модуль основной конфигурации

Откройте модуль объекта именно в основной конфигурации, а не в расширении. Найдите процедуру, которую нужно заменить.

Шаг 3. Добавьте метод в расширение

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

&Вместо("ЗаполнитьПоОснованию")
Процедура Расш1_ЗаполнитьПоОснованию(Основание)
    // ваш код
КонецПроцедуры

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

Шаг 4. Напишите новый код

В модуле объекта доступны ЭтотОбъект, реквизиты и экспортные методы основного модуля. Локальные процедуры типового модуля тоже доступны из расширения: модуль расширения работает как продолжение модуля объекта.

Для модуля формы директиву компиляции оставьте той же, что у оригинала:

&НаСервере
&После("ПриСозданииНаСервере")
Процедура Расш1_ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)

    Если Не ЗначениеЗаполнено(Объект.Ответственный) Тогда
        Объект.Ответственный = Пользователи.ТекущийПользователь();
    КонецЕсли;

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

Директива &НаСервере или &НаКлиенте у перехватчика должна совпадать с директивой исходного метода. Иначе расширение не применится.

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

Шаг 5. Правка нескольких строк через «Изменение и контроль»

Если выбрали этот тип, платформа скопирует тело процедуры в расширение. Вносите правки только внутри директив:

&ИзменениеИКонтроль("ЗаполнитьТоварыПоОснованию")
Процедура Расш1_ЗаполнитьТоварыПоОснованию(Основание)

    Запрос = Новый Запрос;
    // ... типовой текст без изменений ...
    #Удаление
    НоваяСтрока.Скидка = 0;
    #КонецУдаления
    #Вставка
    НоваяСтрока.Скидка = СтрокаОснования.Скидка;
    #КонецВставки
    // ... типовой текст без изменений ...

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

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

Шаг 6. Сохраните расширение в базе

Сделайте окно расширения активным и выполните Конфигурация → Обновить конфигурацию базы данных (F7). Для расширения не нужен монопольный доступ, если оно не добавляет новых реквизитов и таблиц. Пользователи увидят изменения после перезапуска сеанса.

Перехват методов общих модулей и глобального контекста

С общими модулями работает тот же механизм: заимствуйте общий модуль (Добавить в расширение по модулю в дереве), откройте его в основной конфигурации и добавьте в расширение нужную процедуру.

На что обратить внимание:

  • 📌 Свойства модуля. Заимствованный модуль наследует флаги «Сервер», «Клиент», «Вызов сервера», «Глобальный». Поменять их в расширении нельзя — пишите код с учётом того, где модуль выполняется.
  • 📌 Экспортные методы. Перехват экспортной процедуры меняет поведение во всех местах вызова сразу. Это плюс для сквозного исправления и минус, если правка нужна только в одной форме.
  • 📌 Модули с повторным использованием. Если у модуля включено повторное использование возвращаемых значений, результат функции кэшируется на время вызова или сеанса. Ваша замена сработает только на первом вызове, дальше вернётся кэш.
  • 📌 Глобальные модули. Процедуры глобального модуля вызываются без имени модуля, но перехватываются так же — по имени в аннотации.

Если нужна новая служебная процедура, не перехватывайте ничего: создайте в расширении собственный общий модуль и вызывайте его из перехватчика. Так замена остаётся короткой, а логика лежит отдельно и читается легче.

Какую аннотацию выбрать?

Отладка и проверка изменённой логики

Отладка расширения ничем не отличается от отладки конфигурации. Запустите 1С:Предприятие из Конфигуратора (Отладка → Начать отладку, F5), поставьте точку останова в процедуре расширения и выполните действие.

Порядок проверки:

  1. Поставьте точки останова и в перехватчике, и в исходной процедуре. Так видно, вызывается ли оригинал и в какой момент.
  2. Пройдите сценарий целиком: создание, заполнение, запись, проведение, печать. Замена в одном методе может повлиять на соседние.
  3. Проверьте вызов с клиента и с сервера. Если процедура вызывается из фонового задания или обработки обмена, проверьте и этот путь.
  4. Откройте список расширений в режиме 1С:Предприятия (Администрирование → Печатные формы, отчеты и обработки → Расширения в типовых конфигурациях на БСП) и убедитесь, что расширение подключено и не помечено ошибкой.

Отлаживайте на копии базы. Выгрузите её в .dt (Администрирование → Выгрузить информационную базу) и загрузите в отдельную базу. Рабочую базу трогайте, когда всё проверено.

Проверка после обновления типовой конфигурации

Это главное, о чём забывают. После каждого обновления:

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

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

Ошибки при замене процедур и как их не допустить

Диагностика: выберите симптом

Ещё несколько ошибок, которые не видно сразу:

  • ❌ Копирование всей процедуры в &Вместо ради одной строки. Через полгода типовая процедура изменится, а ваша копия останется старой. Для точечной правки есть &ИзменениеИКонтроль.
  • ❌ Правка параметров «на всякий случай». Если перехватчик меняет переданный по ссылке параметр, это видят все вызывающие методы. Меняйте параметры осознанно и комментируйте зачем.
  • ❌ Изменение основной конфигурации вместе с расширением. Если снять конфигурацию с поддержки и параллельно перехватывать её методы, обновление превращается в двойную работу. Выберите один путь.
  • ❌ Нет комментария о причине. Через год никто не вспомнит, зачем отключали типовую проверку. Пишите над перехватчиком, что исправлено и почему.

Частые вопросы

Можно ли заменить процедуру, у которой нет слова «Экспорт»?
Да. Перехват работает и для локальных процедур модуля объекта, формы и общего модуля — достаточно заимствовать объект.
Чем «Изменение и контроль» лучше копии кода в «Вместо»?
Платформа сверяет неизменённые строки с оригиналом. Если типовой код поменялся при обновлении, вы узнаете об этом сразу, а не по жалобам пользователей.
Нужен ли монопольный доступ, чтобы сохранить расширение?
Если расширение меняет только код и формы — нет. Он нужен, когда расширение добавляет реквизиты или таблицы и требует реструктуризации данных.
Как временно отключить замену, не удаляя расширение?
Снимите флаг «Активно» у расширения в списке расширений в режиме 1С:Предприятия или в Конфигураторе и перезапустите сеансы пользователей.
Можно ли перехватить процедуру, которую уже перехватило другое расширение?
Да, обработчики сработают цепочкой. Но поведение зависит от порядка применения расширений, поэтому правки одного метода лучше держать в одном расширении.