В практике разработки на платформе 1С:Предприятие 8.3 часто возникает ситуация, когда в базе уже установлено одно расширение (например, отраслевое решение), которое добавляет свои документы или справочники. Перед разработчиком встает задача: дополнить функционал этого «чужого» расширения (добавить кнопку на форму или новый реквизит), не снимая его с поддержки и не внося в него прямых изменений. В этой статье мы разберем, как реализовать «расширение для расширения», проанализируем ограничения платформы и рассмотрим современные программные способы решения этой задачи.
Разберем стандартную ситуацию: в Расширении1 создан новый Документ1. Мы создаем Расширение2 и хотим захватить в него форму этого документа, чтобы добавить на нее закладку. Однако при попытке сделать это через дерево метаданных мы обнаруживаем, что Расширение2 просто «не видит» объекты, созданные в Расширении1. Они не отображаются в списке доступных для заимствования объектов основной конфигурации.
Это происходит потому, что по умолчанию каждое расширение взаимодействует только с объектами основной конфигурации. Тем не менее, решение существует, и оно делится на два уровня: настройка платформенных зависимостей и программная модификация.
Начиная с версии платформы 8.3.16, разработчики получили штатный механизм взаимодействия. Рассмотрим, как настроить видимость метаданных одного расширения в другом:
Расширения2 (которое должно дополнять функционал).Расширения1.Что это нам дает? Теперь Расширение2 «знает» о существовании типов данных первого расширения. Это позволяет нам программно обращаться к документам, справочникам и экспортным методам общих модулей первого расширения. Однако важно помнить: визуально «захватить» форму объекта из первого расширения во второе (через контекстное меню «Добавить в расширение») все равно не получится. Взаимодействие будет строиться исключительно через код.
Если нам нужно добавить кнопку или закладку на форму документа, созданного в Расширении1, мы воспользуемся методом программного изменения состава элементов. Рассмотрим ситуацию, когда мы не можем или не хотим менять код первого расширения.
Проанализируем алгоритм: мы будем использовать «перехват» процедур в основной конфигурации, которые вызываются при создании формы. Рассмотрим пример кода, который нужно разместить в Расширении2. Допустим, форма документа из первого расширения при открытии вызывает какой-то стандартный механизм основной конфигурации (например, общие модули БСП).
// В Расширении2 создаем программный обработчик
&После("ПриСозданииНаСервере")
Процедура Расш2_ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Проверяем, что мы находимся в форме нужного нам документа из другого расширения
Если ЭтотОбъект.ИмяФормы = "Документ.Расш1_НовыйДокумент.Форма.ФормаДокумента" Тогда
// Программно добавляем группу (закладку)
НоваяСтраница = Элементы.Добавить("Расш2_ДополнительнаяСтраница", Тип("ГруппаФормы"), Элементы.ГруппаСтраницы);
НоваяСтраница.Вид = ВидГруппыФормы.Страница;
НоваяСтраница.Заголовок = "Дополнительно (Расширение 2)";
// Добавляем кнопку
НоваяКнопка = Элементы.Добавить("Расш2_СпециальнаяКоманда", Тип("КнопкаФормы"), НоваяСтраница);
НоваяКнопка.ИмяКоманды = "Расш2_ВыполнитьДействие";
КонецЕсли;
КонецПроцедуры
Если в Расширении1 разработчики предусмотрели вызовы процедур из общих модулей основной конфигурации (например, механизмы подключаемых команд), то задача упрощается: мы просто расширяем тот же общий модуль в Расширении2, и наш код выполнится в контексте формы документа из первого расширения.
Рассмотрим более надежный вариант, который часто обсуждается опытными разработчиками. Чтобы избежать «ректальных» методов (как выразились участники форума), при возможности стоит придерживаться следующего правила:
Новые объекты метаданных (документы, справочники, реквизиты) лучше добавлять в основную конфигурацию, а расширения использовать только для логики и интерфейса.
Если вы добавите Документ1 в основную конфигурацию (даже если она на поддержке, это допустимо), то любые расширения (и первое, и второе, и десятое) будут видеть этот объект в дереве метаданных. В этом случае:
Расширение1 добавит свою логику проведения.Расширение2 добавит свои кнопки и формы.Выясним, как платформа определяет, чей код выполнится первым, если оба расширения модифицируют один и тот же метод. Для этого существует свойство Порядок в списке расширений.
Разберем пример: если Расширение1 имеет порядок 10, а Расширение2 — порядок 20, то при использовании аннотации &После сначала выполнится код первого расширения, а затем — второго. Это критично, если ваше второе расширение полагается на элементы, которые должны быть созданы кодом первого расширения.
Частый вопрос: «Как в Расширении2 обработать нажатие кнопки, добавленной на форму документа из Расширения1?». Поскольку мы создаем кнопку программно, нам нужно также программно назначить ей действие. Выполним это следующим образом:
&НаСервере
Процедура Расш2_ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Создаем команду формы программно
НоваяКоманда = ЭтаФорма.Команды.Добавить("Расш2_ДействиеКнопки");
НоваяКоманда.Действие = "Расш2_ОбработчикНажатия"; // Имя процедуры на клиенте
// Привязываем команду к кнопке
ЭлементКнопка = Элементы.Добавить("Расш2_Кнопка", Тип("КнопкаФормы"), Элементы.КоманднаяПанель);
ЭлементКнопка.ИмяКоманды = "Расш2_ДействиеКнопки";
КонецПроцедуры
&НаКлиенте
Процедура Расш2_ОбработчикНажатия(Команда)
ПоказатьПредупреждение(,"Кнопка из второго расширения успешно сработала!");
КонецПроцедуры
Подведем итог: прямое визуальное заимствование объектов из одного расширения в другое невозможно. Однако мы можем эффективно работать в рамках этой задачи, используя:
Элементы.Добавить().&После или &Вместо.Наиболее стабильным решением для крупных проектов остается вынос структуры данных в основную конфигурацию, что позволяет использовать расширения по их прямому назначению — для адаптации, а не для создания сложных иерархий зависимых метаданных.