Работа с расширениями конфигурации — мощный инструмент адаптации типовых решений без снятия с поддержки. Однако, когда архитектура доработку усложняется и появляется несколько расширений, разработчики часто сталкиваются с неочевидными ограничениями платформы. Одно из таких ограничений — невозможность в конфигураторе одного расширения (назовем его Расширение2) выбрать в качестве типа данных объект, добавленный в другое расширение (Расширение1).
Разберем подробно, почему возникает эта проблема, почему попытки обмануть платформу через XML не работают, и какие существуют профессиональные способы решения этой задачи.
Рассмотрим ситуацию, описанную в задаче. У нас есть:
Документ.Документ1.РегистрСведений.Регистр1.Задача: сделать так, чтобы измерение регистра в Расширении2 имело тип ссылки на Документ1 из Расширения1.
При попытке сделать это в конфигураторе вы обнаружите, что Документ1 просто отсутствует в списке доступных типов для Расширения2. Платформа видит только объекты основной конфигурации и собственные объекты текущего расширения.
Это происходит потому, что расширения конфигурации проектировались как независимые сущности, накладываемые на основную конфигурацию. Механизм расширений не подразумевает иерархии зависимостей «Расширение зависит от Расширения». При компиляции метаданных каждое расширение проверяется на целостность относительно основной конфигурации, но не относительно соседних расширений.
Многие разработчики пытаются обойти это ограничение, выгружая расширение в XML и вручную прописывая GUID или имя типа из соседнего расширения. Давайте проанализируем, к чему это приводит.
Если вы отредактируете файл Configuration.xml внутри выгрузки расширения и насильно укажете тип, загрузка может пройти успешно. Объект даже может отобразиться в дереве метаданных как «захваченный» или существующий. Однако при попытке обновить конфигурацию базы данных (F7) вы получите ошибку:
Ошибка проверки метаданных: Не найден объект Документ1
Платформа выполняет строгую проверку типов. В контексте Расширения2 метаданные Расширения1 не существуют. Следовательно, тип данных считается битым. Исправить это «волшебным тегом» невозможно — это жесткое ограничение ядра платформы 1С:Предприятие.
Самый распространенный способ решения этой задачи без объединения расширений — отказ от строгой типизации в пользу обобщенной.
Вместо того чтобы указывать конкретный тип ДокументСсылка.Документ1 (который невидим), мы можем использовать тип, который гарантированно существует в любой конфигурации — ДокументСсылка (без уточнения вида документа) или, в крайних случаях, ЛюбаяСсылка.
Как это реализовать:
ДокументСсылка (этот тип находится в ветке стандартных реквизитов или общих типов, он включает в себя ссылки на абсолютно все документы системы).Преимущества:
Документ1 из Расширения1, так как в пользовательском режиме все объекты существуют в едином пространстве имен.Недостатки и риски:
Чтобы нивелировать недостатки, в форме записи регистра или форме списка необходимо добавить программный отбор типов. Рассмотрим пример кода для модуля формы:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Ограничиваем выбор типа только нужным документом,
// используя его имя в виде строки (так как Тип("...") сработает в Предприятии)
МассивТипов = Новый Массив;
// Проверяем, существует ли тип в системе (подключено ли Расширение1)
ТипДокумента1 = Тип("ДокументСсылка.Документ1");
Если ТипДокумента1 <> Неопределено Тогда
МассивТипов.Добавить(ТипДокумента1);
Элементы.Измерение1.ОграничениеТипа = Новый ОписаниеТипов(МассивТипов);
Иначе
// Если расширение с документом отключено, можно скрыть поле или выдать ошибку
Элементы.Измерение1.Доступность = Ложь;
КонецЕсли;
КонецПроцедуры
Если ваша задача состоит в том, чтобы Расширение1 писало данные в регистр из Расширения2 (например, при проведении Документа1), то прямая видимость типов в конфигураторе не обязательна.
В режиме «1С:Предприятие» все расширения сливаются в единую конфигурацию. Это значит, что программный код Расширения1 может видеть и писать в регистры Расширения2.
Алгоритм действий:
ДокументСсылка (как описано выше) или УникальныйИдентификатор (если хотите хранить только ID).Документа1 или подпишитесь на событие записи.НаборыЗаписей или менеджер записи для формирования движений.Пример кода в модуле Расширения1 (обратите внимание, что компилятор может ругаться на неизвестные объекты, если проверять синтаксис строго, поэтому иногда используют Выполнить или получение метаданных через строку, но часто достаточно просто писать код, если вы уверены в именах):
&После("ОбработкаПроведения")
Процедура Расш1_ОбработкаПроведения(Отказ, Режим)
// Проверяем, есть ли в системе регистр из Расширения2
Если Метаданные.РегистрыСведений.Найти("РегистрСведений1") <> Неопределено Тогда
// Создаем набор записей через менеджер, используя строковое имя
Набор = РегистрыСведений["РегистрСведений1"].СоздатьНаборЗаписей();
// Устанавливаем отбор - в режиме Предприятия это сработает
Набор.Отбор.Измерение1.Установить(Ссылка);
НовЗапись = Набор.Добавить();
НовЗапись.Измерение1 = Ссылка;
НовЗапись.Ресурс1 = 100;
Набор.Записать();
КонецЕсли;
КонецПроцедуры
Этот подход позволяет сохранить независимость расширений. Если Расширение2 (с регистром) будет удалено, проверка Метаданные.Найти вернет Неопределено, и код просто не выполнится, не вызывая критических ошибок.
Если объекты двух расширений тесно связаны (один ссылается на другой в метаданных), это верный признак того, что логически они представляют собой единый функциональный блок.
Разделение на «Адаптацию» и «Дополнение» имеет смысл для управления порядком вызовов, но если возникает зависимость данных, лучшим решением будет объединение функционала в одно расширение.
Почему это лучше:
ДокументСсылка.Документ1 в поле типа).Как объединить:
Попытки «протащить» ссылки между независимыми расширениями через XML обречены на провал из-за механизма проверки метаданных платформой. У вас есть два пути:
ДокументСсылка (или СправочникСсылка) в метаданных принимающего расширения и накладывать ограничения программно в формах. Это подходит, если слияние расширений невозможно по административным причинам.