Как код расширения 1С вызывает модули, формы и данные основной конфигурации

· 9 мин

Содержание

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

Расширение работает в контексте объединённой конфигурации. Платформа «накладывает» его на основную, и код расширения видит её общие модули, менеджеры объектов, константы и метаданные так же, как видит их доработка внутри самой конфигурации. Поддержку поставщика снимать не нужно. Расширения появились в платформе 8.3.6, всё описанное ниже относится к 8.3.

Что расширению доступно сразу, а что — после заимствования

Главное правило: читать и вызывать можно без заимствования, менять — только через него. Таблица ниже снимает большую часть вопросов.

ЗадачаНужно заимствовать?Что заимствовать
Вызвать экспортный метод общего модуляНет—
Прочитать данные запросом или через менеджерНет—
Записать объект основной конфигурации из своего кодаНет—
Выполнить код перед, после или вместо метода основной конфигурацииДаМодуль, где лежит метод
Добавить реквизит или табличную часть в справочник, документДаСам объект
Добавить поле, кнопку, обработчик на формуДаФорму (объект-владелец заимствуется вместе с ней)
Указать тип основного объекта в своём реквизите или подпискеДаОбъект, чей тип используется
Вызвать неэкспортную процедуру модуля объектаДаОбъект, код пишется в модуле его расширения

Заимствуют в Конфигураторе. В дереве основной конфигурации щёлкните правой кнопкой по объекту и выберите Добавить в расширение, затем укажите нужное расширение. Объект появится в дереве расширения с пометкой заимствования.

Задайте расширению префикс имён в свойствах (например, Расш1_) и пишите его в начале каждого своего объекта и метода. Так имена не столкнутся с объектами, которые появятся в основной конфигурации после обновления.

Вызов методов общих модулей основной конфигурации

Экспортные процедуры и функции общих модулей вызываются из расширения как обычно: ИмяМодуля.ИмяМетода(Параметры). Работают те же правила контекста, что и внутри конфигурации.

  • 🖥️ Серверный общий модуль вызывается из серверного кода расширения.
  • 📡 С клиента серверный модуль доступен, только если у него стоит флажок «Вызов сервера».
  • 💻 Клиентские модули основной конфигурации вызываются из клиентского кода расширения.
  • 🔒 Неэкспортные методы общего модуля из своего модуля расширения не видны.

Пример: общий серверный модуль расширения Расш1_Сервер берёт ИНН контрагента функцией БСП.

// Общий модуль расширения Расш1_Сервер (флажок «Сервер»)
Функция ИННКонтрагента(Контрагент) Экспорт
    Возврат ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Контрагент, "ИНН");
КонецФункции

Модуль ОбщегоНазначения при этом не заимствуется. Он есть во всех типовых на БСП: БП 3.0, ЗУП 3.1, УТ 11, КА 2, ERP.

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

Перехват методов: Перед, После, Вместо и ИзменениеИКонтроль

Чтобы изменить поведение кода основной конфигурации, метод перехватывают аннотацией в заимствованном модуле. Быстрее всего — открыть модуль основной конфигурации, встать на процедуру, вызвать контекстное меню Добавить в расширение и выбрать тип вызова. Конфигуратор сам создаст заготовку с аннотацией.

АннотацияЧто делаетКогда братьРиски
&Перед("Метод")Ваш код выполняется до исходногоПроверки, заполнение параметровМинимальные
&После("Метод")Ваш код выполняется после исходногоДозаполнение, дополнительные записиМинимальные
&Вместо("Метод")Исходный метод не выполняется, пока не вызван ПродолжитьВызов()Подмена результата функцииМожно потерять логику при обновлении
&ИзменениеИКонтроль("Метод")Копия текста метода с вставками и удалениямиПравка в середине алгоритмаРасширение не применится, если исходный текст изменился

Директива компиляции (&НаСервере, &НаКлиенте) и параметры перехватчика должны совпадать с исходным методом. Имя вашего метода может быть любым, привязка идёт по строке в аннотации.

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

&Вместо("ЦенаПоУмолчанию")
Функция Расш1_ЦенаПоУмолчанию(Номенклатура)
    Результат = ПродолжитьВызов(Номенклатура);   // исходная логика
    Если Результат = 0 Тогда
        Результат = Расш1_Сервер.ЦенаИзПрайса(Номенклатура);
    КонецЕсли;
    Возврат Результат;
КонецФункции

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

Для правки в середине метода используют &ИзменениеИКонтроль. Платформа хранит текст метода и сверяет его с основной конфигурацией. Свои строки обрамляют #Вставка … #КонецВставки, лишние — #Удаление … #КонецУдаления.

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

Перехватить можно только метод, который существует в основном модуле. Если в модуле объекта нет ПередЗаписью, аннотацию поставить не на что — используйте подписку на событие (раздел ниже).

Формы основной конфигурации: элементы и обработчики

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

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

  • 📝 Объект — данные объекта формы со всеми реквизитами.
  • 🧩 Элементы.ИмяЭлемента — элементы основной формы.
  • ⚙️ Процедуры модуля основной формы вызываются по имени, включая неэкспортные.

Элементы можно добавить и программно, если их состав зависит от условий:

&НаСервере
Процедура Расш1_ПриСозданииНаСервереПосле(Отказ, СтандартнаяОбработка)
    Поле = Элементы.Добавить("Расш1_Примечание", Тип("ПолеФормы"));
    Поле.Вид = ВидПоляФормы.ПолеВвода;
    Поле.ПутьКДанным = "Объект.Расш1_Примечание";
КонецПроцедуры

Реквизит Расш1_Примечание при этом должен быть добавлен в заимствованный объект расширения. Если нужна конкретная группа, третьим параметром Элементы.Добавить передайте её: Элементы.ИмяГруппы. Имя группы смотрите в дереве элементов формы.

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

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

Данные читаются и пишутся без заимствования. Код расширения обращается к тем же менеджерам и таблицам, что и основная конфигурация.

// Поиск через менеджер справочника
Контрагент = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", ИНН);

// Запрос к любой таблице основной конфигурации
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    Контрагенты.Ссылка,
|    Контрагенты.Наименование
|ИЗ
|    Справочник.Контрагенты КАК Контрагенты
|ГДЕ
|    Контрагенты.ИНН = &ИНН";
Запрос.УстановитьПараметр("ИНН", ИНН);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    Сообщить(Выборка.Наименование);
КонецЦикла;

Так же доступны Константы.Имя.Получить(), регистры сведений и накопления, перечисления (Перечисления.Имя.Значение), коллекция Метаданные.

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

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

// Модуль объекта заимствованного документа
&Перед("ОбработкаПроведения")
Процедура Расш1_ОбработкаПроведения(Отказ, РежимПроведения)
    Если Не ЗначениеЗаполнено(Контрагент) Тогда
        ОбщегоНазначения.СообщитьПользователю("Не заполнен контрагент", ЭтотОбъект, "Контрагент", , Отказ);
    КонецЕсли;
КонецПроцедуры

Своих реквизитов в заимствованном объекте можно добавить сколько нужно. Такое расширение меняет структуру данных: при его подключении платформа реструктуризирует таблицы, поэтому ставить его лучше в нерабочее время.

Удаление расширения, которое добавило реквизиты, удаляет и данные этих реквизитов. Перед отключением выгрузите базу в .dt или сделайте резервную копию средствами СУБД.

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

Подписка на событие — способ встроиться в запись или проведение объекта, не трогая его модуль. Создайте подписку в дереве расширения, в поле «Источник» отметьте типы объектов основной конфигурации (сами объекты для этого заимствуются), выберите событие и обработчик.

Обработчик — экспортная процедура общего серверного модуля расширения. Параметры повторяют событие плюс первым идёт Источник:

// Общий модуль Расш1_ПодпискиСервер, подписка на ПередЗаписью документа
Процедура Расш1_ПередЗаписьюДокумента(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
    Если Источник.ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;
    Если РежимЗаписи = РежимЗаписиДокумента.Проведение И Источник.СуммаДокумента = 0 Тогда
        Отказ = Истина;
    КонецЕсли;
КонецПроцедуры

Проверка ОбменДанными.Загрузка нужна, чтобы подписка не мешала обменам. Реквизит СуммаДокумента есть не у всех документов — в своём примере подставьте реальный.

Второй вид точек встраивания — переопределяемые модули БСП. Их имена заканчиваются на Переопределяемый, например ОбщегоНазначенияПереопределяемый. Поставщик оставил в них пустые процедуры специально для доработок. Позаимствуйте такой модуль и перехватите нужную процедуру с &После: так вы добавите свою печатную форму, команду или настройку без перехвата основной логики.

СпособПлюсыМинусы
Подписка на событиеНе зависит от текста модулей, один обработчик на много объектовПорядок срабатывания относительно других подписок не задаётся
Перехват &Перед/&ПослеТочное место в алгоритмеЛомается, если поставщик переименует метод
Переопределяемый модуль БСПШтатная точка доработки, переживает обновленияЕсть не для всех сценариев

Ограничения и совместимость с версиями основной конфигурации

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

Что чаще всего ломается:

  • 🔁 перехваченный метод переименовали или удалили — расширение с аннотацией на него не применится;
  • ✂️ текст метода под &ИзменениеИКонтроль изменился — платформа сообщает о несовпадении, нужно пересобрать вставки;
  • 🏷️ у заимствованного объекта изменилось контролируемое свойство (тип реквизита, состав формы);
  • 🧱 режим совместимости расширения выше, чем у основной конфигурации.

Контроль свойств настраивают в заимствованном объекте: у каждого свойства можно включить или отключить проверку. Контролируйте только то, от чего реально зависит код, иначе расширение будет падать на каждой мелочи.

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

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

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

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

Можно ли из основной конфигурации вызвать процедуру расширения?
Напрямую нет: основная конфигурация ничего не знает о расширениях. Обходной путь — проверить, что модуль расширения подключён (Метаданные.ОбщиеМодули.Найти("Расш1_Сервер") <> Неопределено), и получить его по имени строкой через ОбщегоНазначения.ОбщийМодуль("Расш1_Сервер") из БСП. Но такой код нужно писать в основной конфигурации, что обычно и пытаются избежать.
Нужно ли снимать конфигурацию с поддержки, чтобы обращаться к ней из расширения?
Нет. Расширение подключается поверх конфигурации на поддержке, и типовая обновляется в обычном режиме.
Что будет, если два расширения перехватят один метод через Вместо?
Перехватчики выстраиваются в цепочку по порядку применения расширений. ПродолжитьВызов() в одном передаёт управление следующему, а не сразу исходному методу.
Видит ли основная конфигурация реквизиты, которые добавило расширение?
В режиме 1С:Предприятие — да, они хранятся в той же таблице. Код основной конфигурации о них не знает и обращаться к ним не будет, если вы сами не перехватите нужный метод.
Работает ли обращение к основной конфигурации в файловой базе так же, как в клиент-серверной?
Да. Правила видимости модулей и заимствования от варианта работы базы не зависят.