При разработке в среде «1С:Предприятие» перед программистом часто возникают задачи, связанные с динамическим анализом структуры объектов. Например, нам может потребоваться выяснить: существует ли определенный реквизит в табличной части справочника или документа? И если существует, то заполнен ли он определенным образом в конкретном элементе базы данных?
В этой статье мы подробно разберем все аспекты этой задачи. Мы рассмотрим проверку метаданных (в том числе с использованием инструментов БСП), научимся эффективно искать нужные значения в строках табличной части, а также разберем особенности работы с элементами управляемых и обычных форм.
Прежде чем обращаться к данным табличной части, необходимо убедиться, что такой реквизит вообще описан в конфигураторе. Попытка обратиться к несуществующему свойству объекта вызовет критическую ошибку выполнения системы. Рассмотрим несколько способов такой проверки.
Способ А. Платформенный метод (без БСП)
Если мы пишем универсальный код, независимый от используемой конфигурации, мы можем обратиться напрямую к метаданным объекта. Разберем по шагам, как это сделать правильно, чтобы избежать ошибок при отсутствии самой табличной части:
// СсылкаНаОбъект - ссылка на элемент справочника или документа
МетаданныеОбъекта = СсылкаНаОбъект.Метаданные();
// Сначала проверяем, существует ли сама табличная часть
ТабличнаяЧасть = МетаданныеОбъекта.TabularSections.Find("КонтактнаяИнформация"); // Англоязычный синоним: TabularSections
Если ТабличнаяЧасть <> Неопределено Тогда
// Если ТЧ найдена, проверяем наличие реквизита в ней
ЕстьРеквизит = (ТабличнаяЧасть.Attributes.Find("ДействуетС") <> Неопределено); // Англоязычный синоним: Attributes
Если ЕстьРеквизит Тогда
// Безопасно выполняем логику
ПоказатьПредупреждение(,"Реквизит существует в метаданных!");
КонецЕсли;
КонецЕсли;
Способ Б. Использование Библиотеки стандартных подсистем (БСП)
В современных конфигурациях 1С (таких как ERP, УТ, КА, БП) встроена Библиотека стандартных подсистем. С её помощью код проверки становится гораздо лаконичнее. Мы можем использовать функции общего назначения:
// Вариант 1: Проверка через метаданные конкретной табличной части
МетаданныеТЧ = СсылкаНаОбъект.Метаданные().ТабличныеЧасти.КонтактнаяИнформация;
ЕстьРеквизит = ОбщегоНазначения.ЕстьРеквизитОбъекта("ДействуетС", МетаданныеТЧ);
// Вариант 2: Универсальная функция, которая проверяет наличие свойства у самого объекта данных
ЕстьРеквизитИлиСвойство = ОбщегоНазначения.ЕстьРеквизитИлиСвойствоОбъекта(СсылкаНаОбъект, "ИмяРеквизита");
Использование БСП предпочтительно, так как эти функции оптимизированы платформой и кэшируются для быстрого повторного доступа.
Допустим, мы убедились, что реквизит в метаданных существует. Теперь перед нами стоит задача: определить, содержит ли табличная часть конкретного элемента хотя бы одну строку, где этот реквизит имеет определенное значение (например, булево значение Истина).
Здесь важно разделять контексты работы: находится ли объект в оперативной памяти (например, на форме) или же мы работаем с базой данных на сервере через ссылку.
Сценарий А. Объект открыт на форме (в памяти)
Если пользователь уже работает с элементом, все данные табличной части находятся в оперативной памяти. В этом случае для поиска строки эффективнее всего использовать встроенный метод коллекции НайтиСтроки(). Проанализируем пример кода:
// Формируем структуру отбора для поиска
ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("ДействуетС", Истина);
// Выполняем поиск по табличной части "КонтактнаяИнформация"
НайденныеСтроки = Объект.КонтактнаяИнформация.НайтиСтроки(ПараметрыОтбора);
Если НайденныеСтроки.Count() > 0 Тогда
// Хотя бы одна строка со значением Истина найдена
ЗначениеВФормеСписка = Истина;
КонецЕсли;
Метод НайтиСтроки() работает очень быстро, так как поиск происходит в локальной памяти клиента или сервера приложений без обращения к СУБД.
Сценарий Б. Объект не загружен в память (работа со ссылкой)
Если вам нужно выполнить проверку в фоновом задании, подписке на события или просто в серверной процедуре обработки данных, где у вас есть только Ссылка на объект, ни в коем случае не используйте метод ПолучитьОбъект() ради обращения к ТЧ. Чтение всего объекта целиком из базы данных является ресурсоемкой операцией.
Вместо этого мы напишем быстрый точечный SQL-запрос. Выясним, как это выглядит на практике:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| 1 КАК Поле
|ИЗ
| Справочник.Организации.КонтактнаяИнформация КАК КонтактнаяИнформацияТЧ
|ГДЕ
| КонтактнаяИнформацияТЧ.Ссылка = &Ссылка
| И КонтактнаяИнформацияТЧ.ДействуетС = ИСТИНА";
Запрос.УстановитьПараметр("Ссылка", СсылкаНаОрганизацию);
РезультатЗапроса = Запрос.Выполнить();
Если НЕ РезультатЗапроса.Пустой() Тогда
// Запрос вернул строку, значит значение существует в базе
ЕстьНужноеЗначение = Истина;
КонецЕсли;
Такой запрос отрабатывает на сервере СУБД за миллисекунды и не нагружает оперативную память сервера приложений лишними данными.
Иногда требуется проверить не саму структуру данных, а интерфейс пользователя. Например, выведена ли колонка табличной части на форму для редактирования. Подходы для обычного и управляемого приложения принципиально различаются.
Для обычных форм (старшие версии платформ)
В обычных формах элементы табличного поля обходились через коллекцию колонок элемента формы:
ЕстьКолонка = Ложь;
Для каждого Колонка Из ЭлементыФормы.ТабличноеПолеКонтактнаяИнформация.Колонки Цикл
ПутьКДанным = "";
Если ЗначениеЗаполнено(Колонка.Данные) Тогда
ПутьКДанным = Колонка.Данные;
ИначеЕсли ЗначениеЗаполнено(Колонка.ДанныеФлажка) Тогда
ПутьКДанным = Колонка.ДанныеФлажка;
КонецЕсли;
Если ПутьКДанным = "ДействуетС" Тогда
ЕстьКолонка = Истина;
Прервать;
КонецЕсли;
КонецЦикла;
Для управляемых форм (современные конфигурации)
В управляемом приложении все элементы на форме представляют собой дерево элементов. Если нам нужно проверить видимость или наличие колонки реквизита на клиенте, мы можем обратиться к коллекции Элементы:
// Имя элемента колонки обычно формируется как ИмяТаблицыФормы + ИмяРеквизита
ИмяЭлементаКолонки = "КонтактнаяИнформацияДействуетС";
ЭлементКолонка = Элементы.Find(ИмяЭлементаКолонки);
Если ЭлементКолонка <> Неопределено Тогда
// Колонка присутствует на форме. Можем управлять её свойствами
ЭлементКолонка.Видимость = Истина;
КонецЕсли;
В процессе обсуждения вопросов работы с табличными частями часто всплывает тема поиска дубликатов записей (например, полей со схожими наименованиями в справочниках). Начинающие разработчики часто сомневаются, что использовать для этой задачи: выборку с отбором или запрос?
Однозначный ответ — только Запрос! Выясним причины, почему это так:
СГРУППИРОВАТЬ ПО и конструкции ИМЕЮЩИЕ КОЛИЧЕСТВО(Ссылка) > 1.Посмотрим на пример запроса для поиска дубликатов контрагентов по ИНН:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.ИНН КАК ИНН,
| КОЛИЧЕСТВО(Контрагенты.Ссылка) КАК КоличествоДублей
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.ИНН <> """"
|СГРУППИРОВАТЬ ПО
| Контрагенты.ИНН
|ИМЕЮЩИЕ
| КОЛИЧЕСТВО(Контрагенты.Ссылка) > 1";
Результат = Запрос.Выполнить().Выгрузить();
// Получили готовую таблицу только с дублирующимися ИНН
Используйте этот подход в своей повседневной практике для обеспечения стабильной и быстрой работы ваших решений на платформе «1С:Предприятие».