Ключевое слово Экспорт в расширении: свои модули, обёртки над закрытыми методами и вызов из типовой

· 10 мин

Содержание

Процедура в расширении 1С становится экспортной так же, как в обычной конфигурации: после списка параметров пишут ключевое слово Экспорт. Например, Процедура Расш1_ПересчитатьСкидки(Документ) Экспорт. Дальше всё решает место, где метод объявлен: в собственном общем модуле расширения, в заимствованном модуле типового объекта или в модуле формы.

Сложности начинаются не с синтаксиса, а с видимости. Типовая конфигурация «не знает» о расширении на этапе разработки, а неэкспортный метод типового модуля нельзя просто «переключить» в экспортный. Оба ограничения обходятся штатными средствами платформы 8.3 (расширения появились в 8.3.6): обёрткой в заимствованном модуле и перехватом типового метода. Порядок действий одинаков для Бухгалтерии 3.0, ЗУП 3.1, УТ 11 и ERP: это механизм платформы, а не конкретной конфигурации.

Чем экспорт в расширении отличается от экспорта в конфигурации

Ключевое слово то же, правила видимости почти те же. Отличия в том, кто и откуда может увидеть ваш метод.

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

  • 📦 Собственный общий модуль расширения. Создан в расширении с нуля. Его экспортные методы видны всему остальному коду расширения по имени модуля: Расш1_Сервис.ПолучитьНастройки().
  • 🔗 Заимствованный модуль. Модуль типового объекта, добавленный в расширение. Ваш код в нём «склеивается» с кодом конфигурации в один модуль.
  • 🖼 Модуль формы. Заимствованной или собственной. Экспортные методы формы вызываются через объект формы.

Главное практическое следствие склейки: методы расширения в заимствованном модуле видят неэкспортные процедуры и переменные этого же модуля конфигурации. На этом построен основной приём из раздела про обёртки.

Задайте расширению префикс в свойстве «Префикс имен» и начинайте с него имена всех новых методов: Расш1_, МояФирма_. Это защищает от совпадения с методом, который фирма 1С добавит в следующем релизе.

Синтаксис: как объявить процедуру или функцию экспортной

Общая форма такая:

&НаСервере
Процедура Расш1_ЗаполнитьРеквизиты(Объект, Знач ДатаЗаполнения) Экспорт
	// код
КонецПроцедуры

Функция Расш1_СуммаПоДоговору(Договор) Экспорт
	Результат = 0;
	// расчёт
	Возврат Результат;
КонецФункции

Правила, которые стоит держать в голове:

  1. Экспорт пишут после закрывающей скобки параметров, до тела метода.
  2. Директива компиляции (&НаСервере, &НаКлиенте, &НаСервереБезКонтекста) нужна в модулях форм и команд. В общем модуле её заменяют флажки в свойствах самого модуля.
  3. В заимствованном модуле над перехватывающим методом сначала идёт директива, затем аннотация: &НаСервере, потом &После("ПриСозданииНаСервере").
  4. Перехватывающему методу (&Перед, &После, &Вместо) Экспорт не нужен. Его вызывает платформа, а видимость определяет исходный метод конфигурации.

Флажки общего модуля расширения

Для собственного общего модуля экспорт сам по себе ничего не даст, если модуль недоступен в нужном контексте. Флажки задают в палитре свойств модуля.

Откуда вызываетеКакие флажки нужны
Из серверного кода (модуль объекта, менеджера, серверная процедура формы)Сервер
С клиента, но выполнение нужно на сервереСервер + Вызов сервера
Только клиентская логика (диалоги, оповещения)Клиент (управляемое приложение)
Из фонового или регламентного заданияСервер

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

Как сделать экспортным закрытый метод типового модуля

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

Рабочий приём — экспортная обёртка в заимствованном модуле.

  1. Откройте Конфигурация → Расширения конфигурации, выберите расширение и откройте его дерево.
  2. В дереве основной конфигурации найдите нужный общий модуль, вызовите контекстное меню и выберите Добавить в расширение.
  3. Откройте модуль уже в дереве расширения. Он пустой: в нём пишут только ваш код.
  4. Добавьте новую экспортную функцию, которая просто вызывает закрытую:
// Заимствованный общий модуль ПродажиСервер (пример)
Функция Расш1_РассчитатьНаценку(Номенклатура, Цена) Экспорт
	// РассчитатьНаценку — неэкспортная функция типового модуля
	Возврат РассчитатьНаценку(Номенклатура, Цена);
КонецФункции
  1. Сохраните расширение и обновите конфигурацию базы данных (F7 в окне расширения).
  2. Вызывайте обёртку из кода расширения как обычный экспортный метод: ПродажиСервер.Расш1_РассчитатьНаценку(Товар, 150).

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

Названия модуля и функции в примере условные. Перед написанием обёртки откройте типовой модуль и посмотрите точную сигнатуру метода: число и порядок параметров, функция это или процедура.

Вызов кода расширения из основной конфигурации

Это самое неочевидное место. Типовая конфигурация в Конфигураторе компилируется без расширений. Если в её коде написать Расш1_Сервис.Выполнить(), синтаксический контроль конфигурации выдаст ошибку: модуль ей неизвестен. К тому же правка типового кода снимает конфигурацию с полной поддержки, а расширения нужны как раз чтобы этого избежать.

Поэтому из типовой не «вызывают расширение». Делают наоборот: расширение встраивается в нужный типовой метод.

СпособКогда подходитРиски
Перехват &После / &ПередНужно добавить действие до или после типовой логикиМинимальные: исходный код выполняется как был
Перехват &Вместо + ПродолжитьВызов()Нужно решить, вызывать ли типовой код, или подменить параметрыЕсли забыть ПродолжитьВызов(), типовая логика не выполнится
&ИзменениеИКонтрольНужна правка в середине типового методаПосле обновления типовой расширение может не примениться, нужна сверка
Переопределяемые модули БСПКонфигурация на БСП, есть подходящий модуль с суффиксом ПереопределяемыйПочти нет: это штатные точки расширения
Динамический вызов по имениКод расширения может быть, а может не быть подключёнОшибка в имени всплывёт только при выполнении

Перехват вместо прямого вызова

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

// Заимствованный модуль объекта документа
&После("ПриЗаписи")
Процедура Расш1_ПриЗаписиПосле(Отказ)
	Если Отказ Тогда
		Возврат;
	КонецЕсли;
	Расш1_Сервис.ЗарегистрироватьИзменение(Ссылка);
КонецПроцедуры

Типовой код не тронут. Ваш экспортный метод ЗарегистрироватьИзменение живёт в собственном модуле расширения и вызывается из кода расширения, где он виден.

Переопределяемые модули БСП

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

Динамический вызов

Если код конфигурации всё-таки должен вызвать метод, которого может не быть, используют вызов по имени. В конфигурациях на БСП для этого есть ОбщегоНазначения.ОбщийМодуль():

Если Метаданные.ОбщиеМодули.Найти("Расш1_Сервис") <> Неопределено Тогда
	Модуль = ОбщегоНазначения.ОбщийМодуль("Расш1_Сервис");
	Модуль.ЗарегистрироватьИзменение(Ссылка);
КонецЕсли;

Проверка через Метаданные нужна, чтобы код не падал, когда расширение отключено.

Параметры и возвращаемые значения

Экспортный метод расширения подчиняется обычным правилам встроенного языка. Но клиент-серверная граница добавляет нюансы.

  • 🔁 По ссылке и Знач. Без Знач параметр передаётся по ссылке, и метод может изменить переменную вызывающего кода. Ставьте Знач у всего, что метод менять не должен. Так поведение очевидно с первого взгляда на сигнатуру.
  • 📤 Результат — через функцию. Если метод что-то вычисляет, делайте его функцией с Возврат, а не процедурой, которая пишет в параметр. Такой код проще читать и тестировать.
  • 🧳 Серверный вызов с клиента. Параметры и результат передаются между клиентом и сервером, поэтому их значения должны сериализоваться. ДокументОбъект, Запрос, ТабличныйДокумент с сервера на клиент так не передать. Передавайте ссылку, структуру, массив, примитивы.
  • 🧩 Много значений. Верните Структуру с понятными ключами: Новый Структура("Сумма, Ошибка", 0, "").
  • ⏳ Необязательные параметры. Значение по умолчанию задают через =: Функция Расш1_Остаток(Товар, Склад = Неопределено) Экспорт. Необязательные ставьте в конец списка.

Для перехватчиков есть своё правило: список параметров метода с аннотацией повторяет список параметров исходного метода. Иначе расширение не применится.

Отладка: как найти, почему метод не вызывается

Отлаживать код расширения можно в Конфигураторе так же, как типовой.

  1. Убедитесь, что расширение сохранено и база обновлена: изменения в окне расширения сами в базу не попадают.
  2. Откройте модуль в дереве расширения и поставьте точку останова (F9) в начале экспортного метода.
  3. Запустите отладку (F5). Если точка не срабатывает, проблема в цепочке до метода, а не в нём самом.
  4. Поставьте точку останова в вызывающем коде и пройдите вызов по шагам (F11).
  5. Для серверных методов проверьте, что отладка на сервере включена: в клиент-серверной базе сервер 1С должен быть запущен с разрешённой отладкой.

В пользовательском режиме проверьте список расширений: в конфигурациях на БСП он открывается через Администрирование → Печатные формы, отчеты и обработки → Расширения. Там видно, активно ли расширение и не отключено ли из-за ошибки применения.

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

Расшифровка типичных сообщений

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

Практические сценарии

Три ситуации, где экспортная процедура в расширении решает задачу без правки типовой.

Общий сервис для нескольких перехватчиков. Логика регистрации изменений нужна в пяти документах. Вместо копирования кода в пять заимствованных модулей создайте общий модуль Расш1_РегистрацияИзменений с флажком Сервер и экспортной процедурой. Каждый перехватчик &После("ПриЗаписи") вызывает её одной строкой.

Доступ к закрытому расчёту типовой. Ваш отчёт в расширении должен считать цену так же, как типовой документ. Расчёт лежит в неэкспортной функции модуля менеджера. Заимствуйте модуль менеджера и добавьте экспортную обёртку. Отчёт вызывает её через Документы.ИмяДокумента.Расш1_..., и при изменении типовой логики вы автоматически получаете новый алгоритм.

Метод формы для вызова из другой формы. Форма подбора, созданная в расширении, должна передать результат в заимствованную форму документа. В модуле формы документа объявите клиентскую экспортную процедуру &НаКлиенте Процедура Расш1_ОбработатьПодбор(Результат) Экспорт. Форма подбора вызывает её через ВладелецФормы.Расш1_ОбработатьПодбор(Данные). Штатный вариант без прямого вызова — оповещение через ОписаниеОповещения при открытии формы подбора.

Как держать экспортные методы расширения в порядке

Расширение живёт дольше, чем кажется, и переживает десятки обновлений типовой. Несколько правил упрощают сопровождение.

  • 🏷 Префикс в каждом имени. И у модулей, и у методов в заимствованных модулях.
  • 🚪 Минимум точек входа. Экспортируйте только то, что действительно вызывается снаружи модуля. Всё остальное оставляйте локальным.
  • 🧱 Свой модуль вместо заимствованного. Если метод не опирается на закрытый код типового модуля, кладите его в собственный общий модуль. Заимствование нужно только для обёрток и перехватов.
  • 📝 Комментарий-описание над методом. Параметры, типы, возвращаемое значение. Конфигуратор показывает его в контекстной подсказке при вызове.
  • 🔒 Безопасный режим. Если у расширения включён безопасный режим, его код не сможет работать с файлами, COM-объектами и внешними компонентами. Учитывайте это при проектировании сервисных методов.
  • 🔍 Проверка после каждого обновления. Проверка применимости расширения ловит отвалившиеся перехваты и обёртки до того, как пользователи увидят ошибку.
Перед публикацией расширения с новыми экспортными методами
Выполнено: 0 / 6

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

Можно ли через расширение изменить экспортность типового метода?
Нет. Аннотации меняют поведение метода, но не его объявление. Нужна экспортная обёртка в заимствованном модуле.
Видны ли экспортные методы одного расширения другому расширению?
Во время выполнения расширения встраиваются в одну конфигурацию, поэтому вызов технически возможен. Но порядок применения и наличие соседнего расширения не гарантированы, так что используйте вызов по имени с проверкой через Метаданные.
Нужно ли снимать типовую с поддержки, чтобы добавить экспортную процедуру?
Нет. Новые методы, обёртки и перехватчики пишутся только в расширении, типовая остаётся на полной поддержке.
Почему в заимствованном модуле не видно типового кода?
Так и задумано: в модуле расширения хранится только ваш код. Типовой текст открывают в дереве основной конфигурации, а при &ИзменениеИКонтроль его копия появляется в теле перехватчика.
Можно ли сделать экспортную процедуру в модуле объекта, созданного в расширении?
Да. У собственных справочников, документов и обработок расширения модули обычные, экспорт работает так же, как в конфигурации.