Перехват и подмена кода типовой процедуры через расширение конфигурации
Содержание
Чтобы заменить процедуру в расширении 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С:Предприятия (
Администрирование → Печатные формы, отчеты и обработки → Расширенияв типовых конфигурациях на БСП) и убедитесь, что расширение подключено и не помечено ошибкой.
Отлаживайте на копии базы. Выгрузите её в .dt (Администрирование → Выгрузить информационную базу) и загрузите в отдельную базу. Рабочую базу трогайте, когда всё проверено.
Проверка после обновления типовой конфигурации
Это главное, о чём забывают. После каждого обновления:
- для
&ИзменениеИКонтрольплатформа сама сообщит о несовпадении — откройте типовую процедуру, сравните с копией в расширении и перенесите правки на новый текст; - для
&Вместоникакой проверки нет — сравните свою версию с новой типовой вручную, иначе исправления разработчиков конфигурации не попадут в базу; - для
&Переди&Послепроверьте, что процедура не переименована и сигнатура (число и порядок параметров) не изменилась.
В Конфигураторе есть проверка применимости расширений: она показывает, какие расширения не смогут примениться к текущей конфигурации, до того как об этом узнают пользователи.
Ошибки при замене процедур и как их не допустить
&ИзменениеИКонтроль или исходный метод переименован. Сравните код, перенесите правки на новую версию, сохраните расширение.&НаСервере/&НаКлиенте — как у оригинала.&Вместо нет вызова ПродолжитьВызов(). Добавьте его в ту ветку, где оригинал нужен.ОбновитьПовторноИспользуемыеЗначения().Ещё несколько ошибок, которые не видно сразу:
- ❌ Копирование всей процедуры в
&Вместоради одной строки. Через полгода типовая процедура изменится, а ваша копия останется старой. Для точечной правки есть&ИзменениеИКонтроль. - ❌ Правка параметров «на всякий случай». Если перехватчик меняет переданный по ссылке параметр, это видят все вызывающие методы. Меняйте параметры осознанно и комментируйте зачем.
- ❌ Изменение основной конфигурации вместе с расширением. Если снять конфигурацию с поддержки и параллельно перехватывать её методы, обновление превращается в двойную работу. Выберите один путь.
- ❌ Нет комментария о причине. Через год никто не вспомнит, зачем отключали типовую проверку. Пишите над перехватчиком, что исправлено и почему.