Объекты расширения в коде 1С 8.3: ссылки, реквизиты, перехват методов и права

· 9 мин

Содержание

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

Типичная ситуация: разработчик добавил в расширение свой реквизит к справочнику «Номенклатура», а коллега пытается прочитать его из общего модуля основной конфигурации и получает ошибку при проверке модулей. Ниже разобраны оба направления: из расширения к конфигурации и из конфигурации к расширению.

Как устроено пространство имён расширения

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

Из этого следуют три правила:

  • 🧩 Свои объекты расширения (справочники, документы, общие модули, обработки) попадают в те же коллекции: Справочники, Документы, ОбщиеМодули в метаданных и т. д.
  • 🔗 Заимствованные объекты — это объекты основной конфигурации, взятые в расширение. Свои реквизиты и табличные части, добавленные к ним, становятся частью того же объекта.
  • 🏷️ Префикс имён из свойств расширения (по умолчанию вида Расш1_) ставится перед именами всех своих объектов и реквизитов. Так их не спутать с объектами конфигурации и не получить конфликт имён при обновлении.

Расширение применяется к сеансу при его начале. Если расширение подключили или изменили, новые объекты станут видны только после перезапуска сеанса пользователя.

Откуда обращаемсяК чемуКак
Модуль расширенияСвой объект расширенияНапрямую по имени
Модуль расширенияОбъект основной конфигурацииНапрямую по имени
Модуль основной конфигурацииОбъект расширенияТолько по имени-строке, с проверкой наличия
Модуль расширенияОбъект другого расширенияТолько по имени-строке, с проверкой наличия

Хорошая архитектура — когда основная конфигурация вообще не обращается к расширению. Расширение само встраивается в нужные места через перехват методов. Тогда отключение расширения ничего не ломает.

Получение ссылки на объект расширения

Внутри расширения: прямое обращение

В модулях расширения пишите код так, будто это обычная конфигурация. Менеджеры, ссылки и предопределённые элементы доступны по полным именам:

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

// Предопределённый элемент
Основной = ПредопределенноеЗначение("Справочник.Расш1_Склады.Основной");

// Пустая ссылка для сравнения
Пустая = Справочники.Расш1_Склады.ПустаяСсылка();

Конфигуратор проверяет такие имена при синтаксическом контроле расширения. Опечатка в имени объекта сразу даст ошибку компиляции.

Из основной конфигурации: по имени-строке

Если код лежит в основной конфигурации, прямое Справочники.Расш1_Склады писать нельзя: без расширения оно даст ошибку. Менеджер получают через индекс коллекции, а тип — через строку:

ИмяСправочника = "Расш1_Склады";

Если Метаданные.Справочники.Найти(ИмяСправочника) <> Неопределено Тогда
    Менеджер = Справочники[ИмяСправочника];
    Склад = Менеджер.НайтиПоКоду("000000001");
    ТипСклада = Тип("СправочникСсылка." + ИмяСправочника);
КонецЕсли;

Строка внутри Тип() проверяется только во время выполнения. Если расширение не подключено, без предварительной проверки вы получите ошибку о неопределённом типе.

Ссылка по уникальному идентификатору

Когда ссылку передают между системами или храните строкой, восстановить её можно так:

УИД = Новый УникальныйИдентификатор("2f1c6c0e-3b0a-4c1e-9a2d-5e7f8a9b0c1d");
Ссылка = Справочники["Расш1_Склады"].ПолучитьСсылку(УИД);

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

Динамический доступ к реквизитам расширения

Чаще всего задача звучит так: к справочнику «Номенклатура» в расширении добавлен реквизит Расш1_Производитель, а читать его нужно в коде, который не знает о расширении.

Чтение и запись через индекс

Объекты и ссылки поддерживают обращение к реквизиту по имени в квадратных скобках:

ИмяРеквизита = "Расш1_Производитель";
МетаданныеНоменклатуры = Метаданные.Справочники.Номенклатура;

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

Через ссылку Ссылка[ИмяРеквизита] тоже работает, но каждое такое обращение читает из базы весь объект. В цикле по тысячам ссылок это медленно. Для массового чтения берите запрос.

Проверка, что реквизит существует

Без проверки код упадёт с ошибкой Поле объекта не обнаружено (Расш1_Производитель) у пользователя, у которого расширение не применилось. Варианты проверки:

  1. Через метаданные: Метаданные.Справочники.Номенклатура.Реквизиты.Найти("Расш1_Производитель").
  2. В конфигурациях на БСП: ОбщегоНазначения.ЕстьРеквизитОбъекта("Расш1_Производитель", Метаданные.Справочники.Номенклатура).
  3. Узнать, из какого расширения пришёл объект метаданных: метод РасширениеКонфигурации() у объекта метаданных возвращает описание расширения или Неопределено для объекта основной конфигурации.

Запрос к данным расширения

Текст запроса — это строка. Поэтому в запросе можно обращаться к таблицам и реквизитам расширения даже из основной конфигурации, синтаксический контроль модуля не мешает:

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

Если расширение не подключено, Выполнить() выдаст ошибку разбора текста запроса. Оберните выполнение в проверку наличия реквизита, а не в Попытка … Исключение вслепую: попытка скроет и другие ошибки.

В БСП для чтения одного-двух полей удобен ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "Расш1_Производитель"). Он строит такой же запрос по имени-строке.

Табличные части и дополнительные данные

Своя табличная часть у заимствованного объекта

Если в расширении к документу «Реализация» добавлена табличная часть Расш1_Упаковки, внутри расширения с ней работают напрямую:

НоваяСтрока = ДокументОбъект.Расш1_Упаковки.Добавить();
НоваяСтрока.Упаковка = Упаковка;
НоваяСтрока.Количество = 3;

Из основной конфигурации — через индекс: ДокументОбъект["Расш1_Упаковки"].Добавить(). Наличие проверяют по коллекции ТабличныеЧасти объекта метаданных.

Свой реквизит в чужой табличной части

К заимствованной табличной части «Товары» тоже можно добавить реквизит . Обращение — через строку табличной части:

Для Каждого СтрокаТовара Из ДокументОбъект.Товары Цикл
    СтрокаТовара.Расш1_Партия = ПолучитьПартию(СтрокаТовара.Номенклатура);
КонецЦикла;

В основной конфигурации тот же код пишут как СтрокаТовара["Расш1_Партия"]. В запросе поле доступно как РеализацияТовары.Расш1_Партия в таблице Документ.РеализацияТоваровУслуг.Товары (имя документа зависит от конфигурации).

Перенос значений между объектами

ЗаполнитьЗначенияСвойств(Приёмник, Источник) переносит и реквизиты расширения, если они есть у обоих объектов. Это удобно при копировании строк: не нужно знать имена полей расширения заранее.

Когда расширение не нужно

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

Перехват методов и событий объектов

Обратное направление: расширение должно изменить поведение объекта конфигурации. Для этого объект (его модуль или форму) заимствуют и пишут процедуру с аннотацией.

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

Пример: дозаполнить свой реквизит перед записью номенклатуры. В модуле объекта заимствованного справочника:

&Перед("ПередЗаписью")
Процедура Расш1_ПередЗаписью(Отказ)
    Если Не ЗначениеЗаполнено(Расш1_Производитель) Тогда
        Расш1_Производитель = Справочники.Расш1_Производители.НеУказан;
    КонецЕсли;
КонецПроцедуры

Внутри модуля объекта к реквизитам обращаются без ЭтотОбъект, как в исходном модуле.

Подмена функции через «Вместо»

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

Без ПродолжитьВызов() исходная функция не выполнится совсем. Это частая причина «пропавшей» типовой логики после установки расширения.

&ИзменениеИКонтроль привязан к тексту исходной процедуры. После обновления типовой конфигурации, если исходник поменялся, расширение перестанет применяться, пока вы не сверите изменения. Поэтому его берут только там, где «Перед» и «После» не справляются.

Обработчики форм

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

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

Если без вызова не обойтись, в БСП есть ОбщегоНазначения.ОбщийМодуль("Расш1_Сервис"). Он находит общий модуль по имени-строке:

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

Правильнее другой путь: оставить в конфигурации пустую процедуру-заглушку и перехватить её в расширении через &После. Код конфигурации тогда не содержит ни одного имени из расширения.

Ошибки при обращении к расширению и отладка

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

Где смотреть подключённые расширения

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

Отладка кода расширения

Точки останова ставят в модулях расширения так же, как в конфигурации: откройте расширение в Конфигураторе и подключите отладчик к сеансу. Отладчик заходит в процедуры с аннотациями и показывает, в каком порядке выполняется ваш код и код конфигурации.

Если подозреваете, что ошибка именно в расширении, отключите его для одного пользователя в тестовой копии базы и повторите действие. Совпало поведение с типовым — ищите причину в коде расширения.

Права доступа и безопасный режим расширения

Права на свои объекты

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

  1. Создайте в расширении свою роль, например Расш1_ИспользованиеСкладов.
  2. Выдайте в ней права на свои объекты: чтение, добавление, изменение, просмотр.
  3. Назначьте роль пользователям или включите её в профиль групп доступа (в конфигурациях на БСП).

Второй путь — заимствовать роль конфигурации и добавить в неё права на объекты расширения. Свои реквизиты заимствованных объектов наследуют права объекта.

Безопасный режим

У расширения есть свойство «Безопасный режим». Расширение, подключённое в пользовательском режиме, по умолчанию работает в безопасном режиме. В нём запрещены операции с файлами, внешними компонентами, COM-объектами и другие потенциально опасные действия. Код, который на тестовой базе писал файл, у пользователя может упасть с ошибкой безопасного режима.

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

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

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

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