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