В этой статье мы рассмотрим актуальную проблему для разработчиков 1С: как добавить новые реквизиты на типовые формы конфигурации с использованием расширений, избегая при этом серьезных конфликтов и трудозатрат при дальнейших обновлениях. Часто возникает ситуация, когда типовая конфигурация не предусматривает необходимых полей для ведения учета или отображения информации. Желание доработать систему, не снимая ее с поддержки, приводит нас к использованию расширений. Однако, простой захват формы в расширение может создать немало сложностей. Разберем, стоит ли "заморачиваться" с программным добавлением реквизитов и какие подходы существуют.
Существует два основных подхода к добавлению реквизитов на формы через расширения, каждый из которых имеет свои преимущества и недостатки. Мы подробно рассмотрим каждый из них, чтобы вы могли выбрать наиболее подходящий для вашей задачи.
Этот подход является наиболее предпочтительным, поскольку он существенно минимизирует проблемы при обновлении типовой конфигурации. Идея заключается в том, чтобы не захватывать всю форму в расширение, а динамически добавлять необходимые реквизиты и элементы управления на нее с помощью кода. Как упоминалось на форуме (сообщение 3), мы можем "впиливать" вызовы в общие модули БСП или использовать стандартные обработчики форм, чтобы не захватывать форму целиком.
Использование процедуры ПриСозданииНаСервере.
Наиболее распространенный способ — это вызов соответствующего кода в процедуре ПриСозданииНаСервере модуля формы. Если форма не захвачена, мы можем добавить подписку на событие ПриСозданииНаСервере для объекта Форма или использовать вызовы в общих модулях, предоставляемых БСП (например, УправлениеСвойствами). В этой процедуре мы динамически создаем поля и связываем их с реквизитами объекта, которые также должны быть добавлены в расширение.
Метод ИзменитьРеквизиты().
Для программного добавления новых реквизитов непосредственно в управляемую форму используется метод ИзменитьРеквизиты(). Мы передаем ему массив добавляемых реквизитов. Этот метод всегда выполняется на сервере. Давайте посмотрим на пример создания и добавления реквизита, а также соответствующего элемента управления на форму:
// В модуле формы расширения (или в общем модуле, если используется внедрение)
// В процедуре-обработчике события ПриСозданииНаСервере
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Проверяем, был ли код расширения уже выполнен для этой формы
Если НЕ Параметры.Свойство("РасширениеИнициализировано_МойРеквизит") Тогда
// 1. Создаем новый реквизит формы
// Указываем имя, тип, путьКДанным (если реквизит объекта), заголовок
НовыйРеквизит = Новый РеквизитФормы("МойНовыйРеквизит", Новый ОписаниеТипов("Строка", , , "255"), , "Дополнительный реквизит");
МассивРеквизитов = Новый Массив;
МассивРеквизитов.Добавить(НовыйРеквизит);
// 2. Добавляем реквизит в форму.
// Этот метод изменяет описание формы на сервере, добавляя новый реквизит.
ИзменитьРеквизиты(МассивРеквизитов);
// 3. Создаем элемент формы для отображения этого реквизита
// Найдем существующую группу, куда хотим добавить элемент,
// или создадим новую, если необходимо.
// Например, добавим в основную группу (предполагаем ее наличие)
Если Элементы.Найти("ГруппаОсновная") = Неопределено Тогда
Элементы.Добавить("ГруппаОсновная", Тип("ГруппаФормы"));
Элементы.ГруппаОсновная.Вид = ВидГруппыФормы.ОбычнаяГруппа;
Элементы.ГруппаОсновная.Заголовок = "Основная группа";
КонецЕсли;
Элементы.Добавить("ГруппаМоегоРеквизита", Тип("ГруппаФормы"), Элементы.ГруппаОсновная);
Элементы.ГруппаМоегоРеквизита.Вид = ВидГруппыФормы.ОбычнаяГруппа;
Элементы.ГруппаМоегоРеквизита.Заголовок = "Мои дополнительные поля";
Элементы.ГруппаМоегоРеквизита.Порядок = 100; // Устанавливаем порядок отображения
НовыйЭлемент = Элементы.Добавить("МойНовыйРеквизитЭлемент", Тип("ПолеФормы"), Элементы.ГруппаМоегоРеквизита);
НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода;
НовыйЭлемент.ПутьКДанным = "МойНовыйРеквизит"; // Связываем элемент с созданным реквизитом
НовыйЭлемент.Заголовок = "Мой новый реквизит";
НовыйЭлемент.УстановитьДействие("ПриИзменении", "МойНовыйРеквизитПриИзменении"); // Связываем событие с процедурой
// Запоминаем, что инициализация прошла, чтобы не повторять при каждом открытии формы
Параметры.Вставить("РасширениеИнициализировано_МойРеквизит", Истина);
КонецЕсли;
КонецПроцедуры
// Процедура-обработчик события ПриИзменении для динамически добавленного реквизита.
// Эта процедура должна находиться в модуле формы расширения,
// если форма захвачена, или быть вызвана через сложный механизм,
// если форма не захвачена.
&НаКлиенте
Процедура МойНовыйРеквизитПриИзменении(Элемент)
Сообщить("Значение МоегоНовогоРеквизита изменилось: " + МойНовыйРеквизит);
// Здесь может быть дополнительная клиентская или серверная логика
КонецПроцедуры
Описание нового реквизита.
Объект Новый РеквизитФормы() используется для создания описания будущего реквизита формы. Мы указываем такие параметры, как имя, тип, путь (если реквизит относится к табличной части объекта конфигурации, а не просто к форме), заголовок и признак сохраняемости данных. Важно понимать, что реквизиты, добавленные через ИзменитьРеквизиты(), существуют только в контексте открытой формы. Если нам нужно хранить эти данные, соответствующие реквизиты должны быть добавлены в объект конфигурации (например, документ, справочник) через расширение.
Обработка событий динамически добавленных элементов.
Это ключевой момент, который вызывает вопросы на форуме (сообщение 7). Если нам нужны обработчики событий для динамически добавленных элементов, мы можем указать имя процедуры-обработчика в свойстве УстановитьДействие() элемента формы. Сами процедуры-обработчики (например, МойНовыйРеквизитПриИзменении) в идеале должны находиться в модуле формы, который заимствован расширением, либо мы должны использовать механизмы внедрения кода или подписки на события в общих модулях, чтобы наша логика могла быть вызвана. Если форма не захвачена, добавление клиентских процедур-обработчиков непосредственно в ее модуль невозможно, и приходится использовать более сложные механизмы (например, подписки на события на общие события формы или вызовы функций общих модулей с передачей контекста формы).
Этот подход проще в реализации на начальном этапе. Мы просто добавляем нужную типовую форму в расширение, а затем непосредственно в редакторе формы расширения добавляем новые реквизиты объекта и элементы управления. Как упоминалось в сообщении 5, при изменении типовой формы нам достаточно нажать кнопку "Обновить" в расширении. Однако, этот подход имеет свои недостатки:
Потенциальные проблемы при обновлении: Хотя платформа 1С постоянно улучшается, в некоторых старых версиях или при сложных изменениях в типовой конфигурации, захваченные формы могли вызывать проблемы. Могли теряться пути к данным, требовалась ручная расстановка элементов после обновления (сообщения 6, 8, 9). В новых версиях платформы (согласно данным поиска) эта ошибка была исправлена, но все равно рекомендуется быть осторожными.
Высокий риск конфликтов: Если типовая форма сильно изменится (например, добавятся новые группы, перегруппируются элементы), то "слияние" форм может потребовать значительных ручных доработок. Система 1С объединяет изменения, но изменения расширения имеют приоритет, и некорректное управление этим процессом может привести к "странному" отображению форм. Это означает, что несмотря на кнопку "Обновить", вам может потребоваться время на адаптацию формы.
Сложность контроля: Захват формы означает, что вы полностью берете на себя ответственность за ее внешний вид и функциональность в части, заимствованной расширением. Любые изменения типовой формы могут повлиять на вашу заимствованную форму, требуя от вас постоянного контроля и корректировки.
Ключевой момент: Если мы выбираем этот путь, то для добавления обработчиков событий для новых реквизитов мы просто создаем необходимые процедуры в модуле формы нашего расширения, как мы это делаем в обычной разработке. Это значительно упрощает работу с клиентскими событиями.
Если требуется добавить новые реквизиты в динамический список формы (например, в журнал документов или справочник), мы не можем просто добавить их через ИзменитьРеквизиты(). Вместо этого, мы модифицируем текст запроса динамического списка, чтобы включить в него дополнительные поля из базовых таблиц данных (или из регистров/справочников, куда мы добавили свои реквизиты).
Например, если у нас есть динамический список, основанный на документе "РеализацияТоваровУслуг", и мы хотим вывести дополнительное поле "ИНН" из справочника "Контрагенты", мы можем модифицировать запрос следующим образом:
// Пример модификации запроса динамического списка в расширении
// В модуле формы расширения, в обработчике ПриСозданииНаСервере
// или в подписке на событие "ПриКомпоновкеРезультата" для динамического списка (если доступно)
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Предполагаем, что у нас есть динамический список с именем "Список"
// и мы хотим добавить поле "ИНН" контрагента из справочника "Контрагенты"
// Получаем текущий объект динамического списка
ДинамическийСписок = Элементы.Найти("Список");
Если ДинамическийСписок = Неопределено Тогда
Возврат;
КонецЕсли;
// Получаем текущий текст запроса динамического списка
ТекущийТекстЗапроса = ДинамическийСписок.ПолучитьПараметрыНастройки().ТекстЗапроса;
// Проверяем, не был ли наш запрос уже модифицирован
Если СтрНайти(ТекущийТекстЗапроса, "КонтрагентИНН") = 0 Тогда
// Добавляем к нему необходимое поле через соединение
// Важно: Этот пример упрощенный и требует аккуратности.
// В реальных условиях нужно анализировать структуру запроса и место для вставки.
// Ищем место, куда можно добавить поле SELECT и JOIN
НовыйТекстЗапроса = ТекущийТекстЗапроса;
// В простейшем случае, если это один объект, можно добавить JOIN
// Например, если запрос начинается с ВЫБРАТЬ ... ИЗ Документ.РеализацияТоваровУслуг
Если СтрНайти(НовыйТекстЗапроса, "ИЗ Документ.РеализацияТоваровУслуг") <> 0 Тогда
// Добавляем поле в SELECT часть
НовыйТекстЗапроса = СтрЗаменить(НовыйТекстЗапроса, "ВЫБРАТЬ", "ВЫБРАТЬ
| Контрагенты.ИНН КАК КонтрагентИНН,
|");
// Добавляем соединение в FROM часть
НовыйТекстЗапроса = СтрЗаменить(НовыйТекстЗапроса,
"ИЗ Документ.РеализацияТоваровУслуг КАК Список",
"ИЗ Документ.РеализацияТоваровУслуг КАК Список
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
| ПО Список.Контрагент = Контрагенты.Ссылка");
Иначе
// Если структура запроса сложнее, требуется более детальный парсинг или подписка на событие
// ПриПолученииТекстаЗапросаДинамическогоСписка
Сообщить("ВНИМАНИЕ: Запрос динамического списка имеет сложную структуру, требуется ручная модификация.", СтатусСообщения.Важное);
Возврат;
КонецЕсли;
// Устанавливаем измененный текст запроса
ДинамическийСписок.УстановитьТекстЗапроса(НовыйТекстЗапроса);
// Добавляем новый элемент поле в форму для отображения
НовоеПолеСписка = Элементы.Добавить("СписокКонтрагентИНН", Тип("ПолеФормы"), ДинамическийСписок);
НовоеПолеСписка.Вид = ВидПоляФормы.ПолеНадпись;
НовоеПолеСписка.ПутьКДанным = "Список.КонтрагентИНН"; // Связываем с полем из запроса
НовоеПолеСписка.Заголовок = "ИНН Контрагента";
НовоеПолеСписка.УстановитьДействие("Нажатие", "СписокКонтрагентИНННажатие"); // Пример обработчика
НовоеПолеСписка.Порядок = 1; // Отобразить в начале списка
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура СписокКонтрагентИНННажатие(Элемент)
Сообщить("Нажат ИНН контрагента: " + Элемент.ТекущиеДанные.КонтрагентИНН);
// Здесь может быть дополнительная клиентская или серверная логика
КонецПроцедуры
Важно: Модификация запроса требует глубокого понимания его структуры и может быть хрупкой при значительных изменениях в типовой конфигурации. Рекомендуется использовать механизмы подписки на событие ПриКомпоновкеПараметровНаСервере или ПриПолученииТекстаЗапросаДинамическогоСписка, если они предусмотрены, для более контролируемой модификации.
Обновление расширений формы: В некоторых старых версиях платформы 1С нажатие кнопки "Обновить расширение формы" могло привести к потере путей к данным у элементов формы. Отмечается, что в более новых версиях эта ошибка была исправлена. Тем не менее, всегда рекомендуется быть осторожными и делать резервные копии.
Влияние нескольких расширений: При использовании нескольких расширений, которые модифицируют одну и ту же форму, мы должны учитывать порядок их применения. Несогласованные изменения могут привести к тому, что одно расширение "отменит" изменения другого или вызовет ошибки. Важно координировать изменения между расширениями.
Префиксы расширений: Все новые объекты, созданные в расширении (отчеты, обработки, подсистемы, обработчики событий), автоматически получают префикс расширения, что помогает избежать конфликтов имен с объектами основной конфигурации.
Удаление реквизитов формы: Программно удалить можно только те реквизиты, которые были также созданы программно. Реквизиты, добавленные через редактор формы, удаляются там же.
Особенности работы с реквизитами объектов в расширении: В новых версиях платформы 1С при добавлении формы в расширение связанные реквизиты объекта конфигурации не всегда "заимствуются" в расширение напрямую. Они могут отображаться как отражение основной конфигурации, и изменения таких реквизитов производятся через редактор формы в расширении. Это сделано для повышения устойчивости расширения к изменениям в основной конфигурации.
Мы выяснили, что программное добавление реквизитов на формы в расширениях является более устойчивым и предпочтительным подходом, особенно для часто изменяющихся форм. Оно требует более глубокого понимания механизмов платформы 1С и работы с динамическими элементами, но значительно снижает риски при обновлениях типовых конфигураций. Если же вы выбираете путь захвата формы, будьте готовы к возможным ручным доработкам после обновлений и внимательно отслеживайте изменения в типовой конфигурации. В любом случае, использование расширений — это мощный инструмент для кастомизации 1С без снятия конфигурации с поддержки, позволяющий нам гибко адаптировать систему под уникальные бизнес-процессы.