Ключевое слово Экспорт в расширении: свои модули, обёртки над закрытыми методами и вызов из типовой
Содержание
Процедура в расширении 1С становится экспортной так же, как в обычной конфигурации: после списка параметров пишут ключевое слово Экспорт. Например, Процедура Расш1_ПересчитатьСкидки(Документ) Экспорт. Дальше всё решает место, где метод объявлен: в собственном общем модуле расширения, в заимствованном модуле типового объекта или в модуле формы.
Сложности начинаются не с синтаксиса, а с видимости. Типовая конфигурация «не знает» о расширении на этапе разработки, а неэкспортный метод типового модуля нельзя просто «переключить» в экспортный. Оба ограничения обходятся штатными средствами платформы 8.3 (расширения появились в 8.3.6): обёрткой в заимствованном модуле и перехватом типового метода. Порядок действий одинаков для Бухгалтерии 3.0, ЗУП 3.1, УТ 11 и ERP: это механизм платформы, а не конкретной конфигурации.
Чем экспорт в расширении отличается от экспорта в конфигурации
Ключевое слово то же, правила видимости почти те же. Отличия в том, кто и откуда может увидеть ваш метод.
Расширение встраивается в конфигурацию при запуске сеанса. Его объекты и код подмешиваются к типовым. Поэтому важно различать три вида модулей:
- 📦 Собственный общий модуль расширения. Создан в расширении с нуля. Его экспортные методы видны всему остальному коду расширения по имени модуля:
Расш1_Сервис.ПолучитьНастройки(). - 🔗 Заимствованный модуль. Модуль типового объекта, добавленный в расширение. Ваш код в нём «склеивается» с кодом конфигурации в один модуль.
- 🖼 Модуль формы. Заимствованной или собственной. Экспортные методы формы вызываются через объект формы.
Главное практическое следствие склейки: методы расширения в заимствованном модуле видят неэкспортные процедуры и переменные этого же модуля конфигурации. На этом построен основной приём из раздела про обёртки.
Задайте расширению префикс в свойстве «Префикс имен» и начинайте с него имена всех новых методов: Расш1_, МояФирма_. Это защищает от совпадения с методом, который фирма 1С добавит в следующем релизе.
Синтаксис: как объявить процедуру или функцию экспортной
Общая форма такая:
&НаСервере
Процедура Расш1_ЗаполнитьРеквизиты(Объект, Знач ДатаЗаполнения) Экспорт
// код
КонецПроцедуры
Функция Расш1_СуммаПоДоговору(Договор) Экспорт
Результат = 0;
// расчёт
Возврат Результат;
КонецФункции
Правила, которые стоит держать в голове:
Экспортпишут после закрывающей скобки параметров, до тела метода.- Директива компиляции (
&НаСервере,&НаКлиенте,&НаСервереБезКонтекста) нужна в модулях форм и команд. В общем модуле её заменяют флажки в свойствах самого модуля. - В заимствованном модуле над перехватывающим методом сначала идёт директива, затем аннотация:
&НаСервере, потом&После("ПриСозданииНаСервере"). - Перехватывающему методу (
&Перед,&После,&Вместо)Экспортне нужен. Его вызывает платформа, а видимость определяет исходный метод конфигурации.
Флажки общего модуля расширения
Для собственного общего модуля экспорт сам по себе ничего не даст, если модуль недоступен в нужном контексте. Флажки задают в палитре свойств модуля.
| Откуда вызываете | Какие флажки нужны |
|---|---|
| Из серверного кода (модуль объекта, менеджера, серверная процедура формы) | Сервер |
| С клиента, но выполнение нужно на сервере | Сервер + Вызов сервера |
| Только клиентская логика (диалоги, оповещения) | Клиент (управляемое приложение) |
| Из фонового или регламентного задания | Сервер |
В модуле с флажком Вызов сервера каждый экспортный метод становится точкой входа с клиента. Не кладите туда процедуры, которые меняют данные без проверки прав: их сможет вызвать любой код на клиенте.
Как сделать экспортным закрытый метод типового модуля
Частая задача: в типовом общем модуле или модуле менеджера есть полезная неэкспортная функция. Ваш код в расширении хочет её вызвать из другого места. Объявить её экспортной через расширение нельзя: перехват меняет поведение метода, но не его сигнатуру и видимость.
Рабочий приём — экспортная обёртка в заимствованном модуле.
- Откройте
Конфигурация → Расширения конфигурации, выберите расширение и откройте его дерево. - В дереве основной конфигурации найдите нужный общий модуль, вызовите контекстное меню и выберите
Добавить в расширение. - Откройте модуль уже в дереве расширения. Он пустой: в нём пишут только ваш код.
- Добавьте новую экспортную функцию, которая просто вызывает закрытую:
// Заимствованный общий модуль ПродажиСервер (пример)
Функция Расш1_РассчитатьНаценку(Номенклатура, Цена) Экспорт
// РассчитатьНаценку — неэкспортная функция типового модуля
Возврат РассчитатьНаценку(Номенклатура, Цена);
КонецФункции
- Сохраните расширение и обновите конфигурацию базы данных (
F7в окне расширения). - Вызывайте обёртку из кода расширения как обычный экспортный метод:
ПродажиСервер.Расш1_РассчитатьНаценку(Товар, 150).
Обёртку держите тонкой: только вызов и возврат. Если в следующем релизе типовой метод переименуют или поменяют параметры, исправлять придётся одну строку, а проверка применимости расширения сразу покажет проблему.
Названия модуля и функции в примере условные. Перед написанием обёртки откройте типовой модуль и посмотрите точную сигнатуру метода: число и порядок параметров, функция это или процедура.
Вызов кода расширения из основной конфигурации
Это самое неочевидное место. Типовая конфигурация в Конфигураторе компилируется без расширений. Если в её коде написать Расш1_Сервис.Выполнить(), синтаксический контроль конфигурации выдаст ошибку: модуль ей неизвестен. К тому же правка типового кода снимает конфигурацию с полной поддержки, а расширения нужны как раз чтобы этого избежать.
Поэтому из типовой не «вызывают расширение». Делают наоборот: расширение встраивается в нужный типовой метод.
| Способ | Когда подходит | Риски |
|---|---|---|
Перехват &После / &Перед | Нужно добавить действие до или после типовой логики | Минимальные: исходный код выполняется как был |
Перехват &Вместо + ПродолжитьВызов() | Нужно решить, вызывать ли типовой код, или подменить параметры | Если забыть ПродолжитьВызов(), типовая логика не выполнится |
&ИзменениеИКонтроль | Нужна правка в середине типового метода | После обновления типовой расширение может не примениться, нужна сверка |
| Переопределяемые модули БСП | Конфигурация на БСП, есть подходящий модуль с суффиксом Переопределяемый | Почти нет: это штатные точки расширения |
| Динамический вызов по имени | Код расширения может быть, а может не быть подключён | Ошибка в имени всплывёт только при выполнении |
Перехват вместо прямого вызова
Пример: после записи документа нужно вызвать вашу экспортную процедуру из общего модуля расширения.
// Заимствованный модуль объекта документа
&После("ПриЗаписи")
Процедура Расш1_ПриЗаписиПосле(Отказ)
Если Отказ Тогда
Возврат;
КонецЕсли;
Расш1_Сервис.ЗарегистрироватьИзменение(Ссылка);
КонецПроцедуры
Типовой код не тронут. Ваш экспортный метод ЗарегистрироватьИзменение живёт в собственном модуле расширения и вызывается из кода расширения, где он виден.
Переопределяемые модули БСП
В конфигурациях на Библиотеке стандартных подсистем есть общие модули с окончанием Переопределяемый. Типовой код вызывает их экспортные процедуры в определённые моменты: при старте, при заполнении списков печатных форм, команд и так далее. Заимствуйте такой модуль и повесьте &После на нужную процедуру. Это самый «законный» путь передать управление из типовой в расширение.
Динамический вызов
Если код конфигурации всё-таки должен вызвать метод, которого может не быть, используют вызов по имени. В конфигурациях на БСП для этого есть ОбщегоНазначения.ОбщийМодуль():
Если Метаданные.ОбщиеМодули.Найти("Расш1_Сервис") <> Неопределено Тогда
Модуль = ОбщегоНазначения.ОбщийМодуль("Расш1_Сервис");
Модуль.ЗарегистрироватьИзменение(Ссылка);
КонецЕсли;
Проверка через Метаданные нужна, чтобы код не падал, когда расширение отключено.
Параметры и возвращаемые значения
Экспортный метод расширения подчиняется обычным правилам встроенного языка. Но клиент-серверная граница добавляет нюансы.
- 🔁 По ссылке и
Знач. БезЗначпараметр передаётся по ссылке, и метод может изменить переменную вызывающего кода. СтавьтеЗначу всего, что метод менять не должен. Так поведение очевидно с первого взгляда на сигнатуру. - 📤 Результат — через функцию. Если метод что-то вычисляет, делайте его функцией с
Возврат, а не процедурой, которая пишет в параметр. Такой код проще читать и тестировать. - 🧳 Серверный вызов с клиента. Параметры и результат передаются между клиентом и сервером, поэтому их значения должны сериализоваться.
ДокументОбъект,Запрос,ТабличныйДокументс сервера на клиент так не передать. Передавайте ссылку, структуру, массив, примитивы. - 🧩 Много значений. Верните
Структурус понятными ключами:Новый Структура("Сумма, Ошибка", 0, ""). - ⏳ Необязательные параметры. Значение по умолчанию задают через
=:Функция Расш1_Остаток(Товар, Склад = Неопределено) Экспорт. Необязательные ставьте в конец списка.
Для перехватчиков есть своё правило: список параметров метода с аннотацией повторяет список параметров исходного метода. Иначе расширение не применится.
Отладка: как найти, почему метод не вызывается
Отлаживать код расширения можно в Конфигураторе так же, как типовой.
- Убедитесь, что расширение сохранено и база обновлена: изменения в окне расширения сами в базу не попадают.
- Откройте модуль в дереве расширения и поставьте точку останова (
F9) в начале экспортного метода. - Запустите отладку (
F5). Если точка не срабатывает, проблема в цепочке до метода, а не в нём самом. - Поставьте точку останова в вызывающем коде и пройдите вызов по шагам (
F11). - Для серверных методов проверьте, что отладка на сервере включена: в клиент-серверной базе сервер 1С должен быть запущен с разрешённой отладкой.
В пользовательском режиме проверьте список расширений: в конфигурациях на БСП он открывается через Администрирование → Печатные формы, отчеты и обработки → Расширения. Там видно, активно ли расширение и не отключено ли из-за ошибки применения.
После включения или загрузки расширения через пользовательский режим новые методы появятся только в новых сеансах. Пользователям, которые уже работают, придётся перезапустить программу.
Расшифровка типичных сообщений
Практические сценарии
Три ситуации, где экспортная процедура в расширении решает задачу без правки типовой.
Общий сервис для нескольких перехватчиков. Логика регистрации изменений нужна в пяти документах. Вместо копирования кода в пять заимствованных модулей создайте общий модуль Расш1_РегистрацияИзменений с флажком Сервер и экспортной процедурой. Каждый перехватчик &После("ПриЗаписи") вызывает её одной строкой.
Доступ к закрытому расчёту типовой. Ваш отчёт в расширении должен считать цену так же, как типовой документ. Расчёт лежит в неэкспортной функции модуля менеджера. Заимствуйте модуль менеджера и добавьте экспортную обёртку. Отчёт вызывает её через Документы.ИмяДокумента.Расш1_..., и при изменении типовой логики вы автоматически получаете новый алгоритм.
Метод формы для вызова из другой формы. Форма подбора, созданная в расширении, должна передать результат в заимствованную форму документа. В модуле формы документа объявите клиентскую экспортную процедуру &НаКлиенте Процедура Расш1_ОбработатьПодбор(Результат) Экспорт. Форма подбора вызывает её через ВладелецФормы.Расш1_ОбработатьПодбор(Данные). Штатный вариант без прямого вызова — оповещение через ОписаниеОповещения при открытии формы подбора.
Как держать экспортные методы расширения в порядке
Расширение живёт дольше, чем кажется, и переживает десятки обновлений типовой. Несколько правил упрощают сопровождение.
- 🏷 Префикс в каждом имени. И у модулей, и у методов в заимствованных модулях.
- 🚪 Минимум точек входа. Экспортируйте только то, что действительно вызывается снаружи модуля. Всё остальное оставляйте локальным.
- 🧱 Свой модуль вместо заимствованного. Если метод не опирается на закрытый код типового модуля, кладите его в собственный общий модуль. Заимствование нужно только для обёрток и перехватов.
- 📝 Комментарий-описание над методом. Параметры, типы, возвращаемое значение. Конфигуратор показывает его в контекстной подсказке при вызове.
- 🔒 Безопасный режим. Если у расширения включён безопасный режим, его код не сможет работать с файлами, COM-объектами и внешними компонентами. Учитывайте это при проектировании сервисных методов.
- 🔍 Проверка после каждого обновления. Проверка применимости расширения ловит отвалившиеся перехваты и обёртки до того, как пользователи увидят ошибку.
Частые вопросы
Можно ли через расширение изменить экспортность типового метода?
Видны ли экспортные методы одного расширения другому расширению?
Нужно ли снимать типовую с поддержки, чтобы добавить экспортную процедуру?
Почему в заимствованном модуле не видно типового кода?
&ИзменениеИКонтроль его копия появляется в теле перехватчика.