Аннотации расширения 1С определяют, когда ваш метод выполнится относительно метода типовой конфигурации. &Перед — до исходного, &После — после него, &Вместо — вместо, с возможностью вызвать исходный через ПродолжитьВызов(). Для функций доступна только &Вместо (и &ИзменениеИКонтроль), для процедур — все три. Правильный выбор аннотации решает, переживёт ли доработка следующее обновление.
Ниже — как это работает, примеры для каждой аннотации, порядок выполнения при нескольких расширениях и ошибки, которые я чаще всего нахожу при аудите чужих расширений.
Как расширение перехватывает методы
Когда вы заимствуете модуль в расширение, пространства имён расширяемого и расширяющего модулей объединяются. Из кода расширения видны переменные и методы исходного модуля, а экспортные методы расширения доступны остальной конфигурации.
В заимствованном модуле можно:
- писать свои процедуры, функции и переменные;
- назначать обработчики событий, которые в конфигурации не обработаны;
- перехватывать существующие методы аннотациями.
Механизм появился в ветке платформы 8.3.9 и с тех пор стал основным способом дорабатывать типовые конфигурации без снятия с поддержки. Есть ограничения: глобальные серверные общие модули заимствовать нельзя, а в самом расширении нельзя создавать привилегированные и глобальные серверные общие модули. Методы привилегированного общего модуля, перехваченные в расширении, по умолчанию выполняются без привилегированного режима; это регулируется профилем безопасности.
Добавить метод в расширение просто: в модуле конфигурации правой кнопкой по заголовку процедуры → «Добавить в расширение», выбрать расширение и тип вызова. Конфигуратор сам заимствует модуль и создаст заготовку с префиксом расширения и аннотацией.
&Перед: код до типового метода
Подходит, когда нужно подготовить данные или добавить проверку до того, как отработает типовой код. Пример: в документе ЗаказКлиента сделать обязательным реквизит Комментарий для заказов с ручной скидкой. В модуле объекта уже есть ОбработкаПроверкиЗаполнения, перехватываем его:
&Перед("ОбработкаПроверкиЗаполнения")
Процедура Расш1_ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
// Комментарий обязателен, если менеджер дал ручную скидку
Если Расш1_ЕстьРучнаяСкидка() Тогда
ПроверяемыеРеквизиты.Добавить("Комментарий");
КонецЕсли;
КонецПроцедуры
Функция Расш1_ЕстьРучнаяСкидка()
Для Каждого СтрокаТовара Из Товары Цикл
Если СтрокаТовара.ПроцентРучнойСкидки <> 0 Тогда
Возврат Истина;
КонецЕсли;
КонецЦикла;
Возврат Ложь;
КонецФункцииКод расширения видит табличную часть Товары объекта напрямую — пространство имён общее. Имена реквизитов в вашей конфигурации могут отличаться, проверьте их перед копированием.
Важно: &Перед не может отменить вызов исходного метода. Если в исходной процедуре нет нужного события (например, ОбработкаПроверкиЗаполнения не определена в модуле конфигурации), перехватывать нечего — назначьте обработчик события самого объекта в расширении.
&После: доработка результата типового кода
Аннотация для случаев, когда типовой код должен отработать полностью, а вы дополняете или корректируете его результат. Частый сценарий — форма: типовой ПриЧтенииНаСервере заполнил данные, а вы меняете оформление по их значениям.
&НаСервере
&После("ПриЧтенииНаСервере")
Процедура Расш1_ПриЧтенииНаСервере(ТекущийОбъект)
ЭлементСуммы = Элементы.Найти("СуммаДокумента");
Если ЭлементСуммы = Неопределено Тогда
Возврат;
КонецЕсли;
Порог = 1000000;
Если ТекущийОбъект.СуммаДокумента >= Порог Тогда
ЭлементСуммы.ЦветТекста = WebЦвета.Красный;
Иначе
ЭлементСуммы.ЦветТекста = Новый Цвет;
КонецЕсли;
КонецПроцедурыПроверка Элементы.Найти здесь не формальность: после обновления типовой формы элемент могут переименовать, и без проверки расширение начнёт падать на открытии формы. Новый Цвет возвращает автоцвет, то есть стандартное оформление. В модуле формы директива компиляции (&НаСервере) пишется перед аннотацией перехвата — так её генерирует конфигуратор.
Тот же приём работает для процедур общих модулей, которые формы вызывают из своих обработчиков. В типовых конфигурациях логика форм часто вынесена в общие модули вида ИмяОбъектаФормы, и перехватывать удобнее там.
&Вместо и ПродолжитьВызов
&Вместо отключает исходный метод: выполняется только код расширения. Это единственная классическая аннотация для функций — у функции есть возвращаемое значение, и платформа не может решать за вас, чей результат возвращать.
Внутри &Вместо доступен метод ПродолжитьВызов(...): он вызывает исходный метод (или следующий слой расширений) с переданными параметрами и возвращает его результат. С ним &Вместо превращается в гибкую обёртку.
Обработка результата функции
Пусть типовая функция ПолучитьАдресФото(ФизическоеЛицо) в общем модуле возвращает адрес картинки во временном хранилище или пустую строку. Нужна картинка-заглушка, если фото нет:
&Вместо("ПолучитьАдресФото")
Функция Расш1_ПолучитьАдресФото(ФизическоеЛицо)
// Сначала выполняем типовую логику
АдресФото = ПродолжитьВызов(ФизическоеЛицо);
Если ЗначениеЗаполнено(АдресФото) Тогда
Возврат АдресФото;
КонецЕсли;
// Фото нет — отдаем картинку-заглушку из расширения
ДанныеЗаглушки = БиблиотекаКартинок.Расш1_НетФото.ПолучитьДвоичныеДанные();
Возврат ПоместитьВоВременноеХранилище(ДанныеЗаглушки, Новый УникальныйИдентификатор);
КонецФункцииОбщая картинка Расш1_НетФото добавлена в само расширение. Это аналог &После для функции: типовой код выполнился, вы работаете с результатом.
Условный вызов исходного метода
Нужно скрывать фото от пользователей без отдельной роли расширения:
&Вместо("ПолучитьАдресФото")
Функция Расш1_ПолучитьАдресФото(ФизическоеЛицо)
Если Не РольДоступна("Расш1_ПросмотрФотоСотрудников") Тогда
Возврат "";
КонецЕсли;
Возврат ПродолжитьВызов(ФизическоеЛицо);
КонецФункцииТиповой код не копируется в расширение, а только вызывается при выполнении условия. Если в следующей версии поставщик поменяет логику получения фото, ваша доработка её подхватит. Это главное преимущество перед вариантом «скопировать тело функции в &Вместо и поправить».
ПродолжитьВызов в процедуре
Для процедуры &Вместо с ПродолжитьВызов эквивалентна паре &Перед + &После, но с одним отличием: код до и после вызова живёт в одном контексте. Локальная переменная, вычисленная до ПродолжитьВызов, доступна после него. С отдельными &Перед и &После пришлось бы передавать состояние через переменную модуля или параметры.
&Вместо("ПересчитатьСуммы")
Процедура Расш1_ПересчитатьСуммы(Отказ)
// Модуль объекта: реквизиты документа доступны напрямую
СуммаДоПересчета = СуммаДокумента;
ПродолжитьВызов(Отказ);
Если Не Отказ И СуммаДоПересчета <> СуммаДокумента Тогда
Расш1_ЗарегистрироватьИзменениеСуммы(СуммаДоПересчета, СуммаДокумента);
КонецЕсли;
КонецПроцедурыПересчитатьСуммы и Расш1_ЗарегистрироватьИзменениеСуммы здесь условные имена — подставьте метод своей конфигурации.
Порядок выполнения при нескольких расширениях
Расширения накладываются на конфигурацию слоями, как луковица. Внешний слой — расширение, применённое последним, затем предыдущие, в центре — основная конфигурация. Вызов метода проходит от внешнего слоя к центру и обратно:
| Ситуация | Что выполнится |
|---|---|
Расш2 &Перед, Расш1 &Перед | Расш2, Расш1, конфигурация |
Расш2 &После, Расш1 &После | Конфигурация, Расш1, Расш2 |
Расш2 &Вместо с ПродолжитьВызов, Расш1 &Перед | Расш2 (до вызова), Расш1, конфигурация, Расш2 (после вызова) |
Расш2 &Вместо без ПродолжитьВызов | Только Расш2; Расш1 и конфигурация не выполняются |
Последняя строка — источник самых неприятных ошибок. Кто-то ставит «временное» расширение с &Вместо без ПродолжитьВызов, и доработки из других расширений молча перестают работать. Поэтому моё правило: одно расширение — одна предметная задача, и &Вместо без ПродолжитьВызов только тогда, когда типовой код действительно должен быть выключен.
&ИзменениеИКонтроль: когда правка нужна внутри метода
Бывает, что нужно изменить пару строк в середине длинной типовой процедуры. &Вместо с копией всего тела опасна: после обновления копия устареет, а вы этого не заметите. Для таких случаев есть &ИзменениеИКонтроль("ИмяМетода"). В расширение копируется текст метода, правки отмечаются директивами #Вставка/#КонецВставки и #Удаление/#КонецУдаления, а платформа сверяет исходный текст: если поставщик изменил метод, расширение не применится, пока вы не актуализируете его.
&ИзменениеИКонтроль("ЗаполнитьПоОснованию")
Процедура Расш1_ЗаполнитьПоОснованию(Основание)
// ... исходный текст метода ...
#Вставка
Комментарий = СтрШаблон("Создан на основании %1", Основание);
#КонецВставки
// ... исходный текст метода ...
КонецПроцедурыЭто лучший выбор для точечных правок: обновление не пройдёт мимо вас.
Типичные ошибки при работе с аннотациями
- Копия типового тела в
&Вместо. Работает до первого обновления. ИспользуйтеПродолжитьВызовили&ИзменениеИКонтроль. - Ожидание, что
&Передотменит вызов. Не отменит. Нужна условная логика —&Вместо. - Несовпадение параметров. Сигнатура метода расширения должна совпадать с исходной. После обновления, если типовой метод получил новый параметр, расширение перестанет применяться.
- Обращение к элементам формы без проверки. Переименованный в обновлении элемент ломает открытие формы. Всегда
Элементы.Найти(...). ПродолжитьВызоввне&Вместо. В&Переди&Послеэтот метод не используется.- Конфликт расширений. Два расширения с
&Вместона одном методе, одно безПродолжитьВызов. Проверяйте пересечения при установке нового расширения.
Хорошее расширение не дублирует типовой код, а оборачивает его. Чем меньше скопированных строк, тем дешевле каждое обновление.
Если в базе уже накопились расширения, которые ломаются после каждого обновления, начните с ревизии: какие методы перехвачены, где скопирован типовой код, где можно перейти на ПродолжитьВызов. Такие работы я делаю в рамках услуг по расширениям 1С и обновлению 1С, а новые доработки — через доработку 1С без снятия конфигурации с поддержки.
Итог
&Переди&После— для процедур, когда типовой код должен выполниться полностью; отменить его они не могут.&Вместо— единственная классическая аннотация для функций; сПродолжитьВызовона становится обёрткой с общим контекстом до и после вызова.- Расширения выполняются слоями: последнее применённое — снаружи;
&ВместобезПродолжитьВызовотключает все внутренние слои. - Для правки в середине метода используйте
&ИзменениеИКонтроль: изменения типового кода не пройдут незамеченными. - Проверяйте элементы формы через
Найтии держите сигнатуры методов в актуальном состоянии после обновлений.




