Механизм расширений конфигурации в платформе «1С:Предприятие 8.3» — это мощный инструмент, который позволяет кастомизировать систему без снятия типового решения с поддержки. Однако многие разработчики сталкиваются с крайне неприятной ситуацией: после очередного обновления типовой конфигурации (например, «Бухгалтерии предприятия 3.0» или «ERP») добавленные в расширение колонки табличных частей внезапно исчезают из интерфейса, а в конфигураторе загорается желтая полоса с предупреждением «Обновить форму». При этом сам программный код расширения остается нетронутым.
В этой статье мы подробно разберем причины возникновения этой проблемы, проанализируем внутреннее поведение платформы 1С и рассмотрим наиболее надежные методы борьбы с потерей элементов формы, главным из которых является программное создание элементов интерфейса.
Когда мы заимствуем форму из основной конфигурации в расширение для визуального редактирования, платформа создает внутри расширения две логические структуры:
В режиме «1С:Предприятие» платформа выполняет так называемое трехстороннее сравнение (3-way merge). Она сопоставляет текущую форму из обновившейся основной конфигурации поставщика, сохраненный слепок в расширении и наши доработки. Если поставщик в обновлении изменил структуру формы (например, переименовал родительскую группу, изменил состав реквизитов или тип данных), платформа сообщает о конфликте желтой полосой «Обновить форму».
При попытке обновить форму интерактивно в конфигураторе часто срабатывает давняя проблема платформы 1С: связи элементов формы с реквизитами затираются. Поля визуально остаются в дереве элементов расширения, но их свойство ПутьКДанным (DataPath) очищается. Как результат — элементы просто исчезают с экрана в пользовательском режиме, так как они больше не связаны с данными объекта.
---
Если проект небольшой, а форму нужно починить прямо сейчас, мы можем восстановить привязки визуально. Разберем этот процесс по шагам:
Форма — Обновить расширение формы).ПутьКДанным, скорее всего, окажется пустым.Объект.Товары.МойРеквизитКолонки).ПриИзменении). Если имя процедуры-обработчика стерлось, выберем ее заново из списка процедур модуля формы расширения.Важно: этот метод прост, но он не решает проблему глобально. При следующем обновлении конфигурации поставщика процедуру придется повторять заново. Для стабильной работы крупных баз такой подход категорически не рекомендуется.
---
Чтобы раз и навсегда обезопасить себя от «слетания» интерфейса при обновлениях, мы должны полностью отказаться от визуального заимствования форм. Сама форма в расширение не заимствуется (или заимствуется только ее модуль без элементов управления). Все новые колонки, поля ввода и кнопки мы будем создавать динамически с помощью встроенного языка 1С.
Рассмотрим подробнее, как реализовать этот подход на практике.
Если для новых колонок требуются новые реквизиты в табличной части, мы добавляем их непосредственно в объект метаданных (например, в табличную часть Товары документа ПоступлениеТоваровУслуг) внутри расширения. Саму форму при этом заимствовать визуально не нужно.
Заимствуем в расширение только модуль формы документа (без визуального заимствования самой формы) и используем аннотацию &После для перехвата стандартного обработчика события ПриСозданииНаСервере. Напишем код, который динамически добавит колонку в нужную таблицу.
Проанализируем пример кода:
&После("ПриСозданииНаСервере")
Процедура Ист_ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Шаг 1: Находим целевую таблицу на форме по ее имени в конфигурации поставщика
ТаблицаТовары = Элементы.Найти("Товары");
Если ТаблицаТовары <> Неопределено Тогда
ИмяНовойКолонки = "ТоварыМойДополнительныйРеквизит";
// Предотвращаем дублирование элемента при повторном вызове события
Если Элементы.Найти(ИмяНовойКолонки) = Неопределено Тогда
// Шаг 2: Создаем новый элемент формы типа "ПолеФормы" и помещаем его внутрь таблицы
НоваяКолонка = Элементы.Добавить(ИмяНовойКолонки, Тип("ПолеФормы"), ТаблицаТовары);
НоваяКолонка.Вид = ВидПоляФормы.ПолеВвода;
// Шаг 3: Связываем поле с реквизитом объекта
НоваяКолонка.ПутьКДанным = "Объект.Товары.МойДополнительныйРеквизит";
// Шаг 4: Настраиваем визуальные свойства элемента
НоваяКолонка.Заголовок = "Доп. Реквизит (Расш.)";
НоваяКолонка.РежимРедактирования = РежимРедактированияКолонки.ВХвостеКоридора;
НоваяКолонка.Ширина = 12;
// Шаг 5: Назначаем обработчик события при изменении значения в этой колонке
НоваяКолонка.УстановитьДействие("ПриИзменении", "Подключаемый_МойРеквизитПриИзменении");
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Теперь нам необходимо описать процедуру-обработчик Подключаемый_МойРеквизитПриИзменении, которую мы зарегистрировали для нашей колонки на предыдущем шаге. Эта процедура должна располагаться в модуле формы расширения с директивой компиляции &НаКлиенте.
Рассмотрим пример реализации обработчика:
&НаКлиенте
Процедура Подключаемый_МойРеквизитПриИзменении(Элемент)
// Получаем строку таблицы, в которой пользователь изменил значение
ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
Если ТекущаяСтрока <> Неопределено Тогда
// Выполняем требуемый алгоритм пересчета
Если ТекущаяСтрока.МойДополнительныйРеквизит > 0 Тогда
// Например, корректируем сумму или выполняем иные расчеты
ТекущаяСтрока.Сумма = ТекущаяСтрока.Количество * ТекущаяСтрока.Цена * ТекущаяСтрока.МойДополнительныйРеквизит;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
---
Если вы разрабатываете расширение для современной конфигурации, построенной на базе Библиотеки Стандартных Подсистем (БСП), например, «1С:ERP», «Комплексная автоматизация» или «Бухгалтерия предприятия 3.0», можно пойти еще дальше и полностью отказаться даже от заимствования модуля формы.
В БСП предусмотрены специальные переопределяемые модули для кастомизации интерфейса программными методами. Мы можем использовать общий модуль МодификацияКонфигурацииПереопределяемый.
Давайте разберем, как это сделать:
МодификацияКонфигурацииПереопределяемый.ПриСозданииНаСервере с аннотацией &Вместо или &После.Посмотрим на пример реализации в расширении общего модуля:
&После("ПриСозданииНаСервере")
Процедура Мой_ПриСозданииНаСервере(Форма, Отказ, СтандартнаяОбработка) Экспорт
// Проверяем, что открывается именно интересующая нас форма документа поступления
Если Форма.ИмяФормы = "Документ.ПоступлениеТоваровУслуг.Форма.ФормаДокумента" Тогда
ТаблицаТовары = Форма.Элементы.Найти("Товары");
Если ТаблицаТовары <> Неопределено Тогда
ИмяЭлемента = "ТоварыПрограммноеПоле";
Если Форма.Элементы.Найти(ИмяЭлемента) = Неопределено Тогда
// Создаем элемент, обращаясь к коллекции элементов переданной формы
НовоеПоле = Форма.Элементы.Добавить(ИмяЭлемента, Тип("ПолеФормы"), ТаблицаТовары);
НовоеПоле.Вид = ВидПоляФормы.ПолеВвода;
НовоеПоле.ПутьКДанным = "Объект.Товары.МойДополнительныйРеквизит";
НовоеПоле.Заголовок = "Программное поле";
// Для подключения действий в БСП используется перенаправление на общие клиентские модули
НовоеПоле.УстановитьДействие("ПриИзменении", "ОбщегоНазначенияБСПКлиент.Подключаемый_ВыполнитьПереопределяемуюКоманду");
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Такой подход делает ваше расширение абсолютно автономным. Вы не заимствуете форму, не отслеживаете ее изменения визуально, а значит, обновления конфигурации поставщика пройдут без единого сбоя со стороны вашего расширения.
---
Для создания отказоустойчивых расширений в среде 1С рекомендуем придерживаться следующих правил:
Элементы.Добавить() в обработчике ПриСозданииНаСервере гарантирует стабильность интерфейса при любых обновлениях.Элементы.Найти() убедитесь, что элемент с таким именем еще не создан. Это исключит критические ошибки инициализации.