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