Механизм дополнительных реквизитов и сведений — это мощный инструмент Библиотеки стандартных подсистем (БСП), который позволяет пользователям расширять состав свойств объектов без изменения конфигурации. Однако перед разработчиками часто возникает задача: как программно найти элемент справочника (например, контрагента, номенклатуру или договор), зная лишь значение этого дополнительного реквизита? В этой статье мы подробно разберем, как правильно составить поисковый запрос, избежать типичных ошибок производительности и учесть особенности архитектуры 1С. При миграции данных сопоставление объектов по произвольным полям поиска вместо ссылок выполняет инструмент переноса данных между похожими конфигурациями 1С.
Рассмотрим типичную ошибку, с которой часто сталкиваются начинающие разработчики при попытке написать поисковый запрос к табличной части дополнительных реквизитов. Посмотрим на исходный нерабочий вариант кода:
Функция НайдемКонтрагента(КодКонтрагента)
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| КонтрагентыДополнительныеРеквизиты.Ссылка КАК Контрагент
|ИЗ
| Справочник.Контрагенты.ДополнительныеРеквизиты КАК КонтрагентыДополнительныеРеквизиты
|ГДЕ
| КонтрагентыДополнительныеРеквизиты.Свойство = &Свойство
| И КонтрагентыДополнительныеРеквизиты.Ссылка = &Ссылка";
Свойство = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("КодУт", Истина);
Запрос.УстановитьПараметр("Ссылка", КодКонтрагента);
Результат = Запрос.Выполнить().Выбрать();
Пока Результат.Следующий() цикл
БКонтрагент = Результат.Контрагент;
КонецЦикла;
Возврат БКонтрагент;
КонецФункции
Проанализируем этот код и выясним причины, по которым он возвращает пустое значение:
КонтрагентыДополнительныеРеквизиты.Ссылка = &Ссылка. Но в параметр &Ссылка передается код контрагента (строка или число), а не ссылка на сам элемент справочника. Поиск должен выполняться по полю Значение табличной части.&Свойство, но метод Запрос.УстановитьПараметр("Свойство", ...) вообще не вызывается. Это приведет к ошибке выполнения запроса.НайтиПоНаименованию("КодУт") ненадежен. Пользователи могут легко переименовать синоним свойства в режиме предприятия (например, изменить "КодУТ" на "Код в старой базе"), после чего программный код перестанет работать.Теперь рассмотрим, как решить эти проблемы и построить надежный, быстродействующий алгоритм.
Чтобы защитить код от случайного переименования реквизита пользователями, в БСП предусмотрено специальное поле — Имя (имя для разработчиков). Оно уникально и не меняется при редактировании видимого наименования свойства.
Разберем рабочий вариант функции, выполняющей поиск контрагента по его коду из внешней системы (в нашем примере — "КодУТ"):
&НаСервере
Функция НайтиКонтрагентаПоДопРеквизиту(ЗначениеПоиска) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| ДопРеквизиты.Ссылка КАК Контрагент
|ИЗ
| Справочник.Контрагенты.ДополнительныеРеквизиты КАК ДопРеквизиты
|ГДЕ
| ДопРеквизиты.Значение = &Значение
| И ДопРеквизиты.Свойство.Имя = &ИмяСвойства";
// Передаем системное имя дополнительного реквизита и искомое значение
Запрос.УстановитьПараметр("ИмяСвойства", "КодУТ_7fa830250b37400a8c11df50f9bc7e62");
Запрос.УстановитьПараметр("Значение", ЗначениеПоиска);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.Контрагент;
КонецЕсли;
Возврат Справочники.Контрагенты.ПустаяСсылка();
КонецФункции
Этот код успешно решает задачу, однако он имеет один скрытый недостаток, который может существенно снизить скорость работы системы при росте объема базы данных. Разберем этот нюанс подробнее.
В предыдущем примере используется условие ДопРеквизиты.Свойство.Имя = &ИмяСвойства. Обращение через точку к реквизитам ссылочного поля (Свойство.Имя) внутри условий запроса заставляет СУБД выполнять неявное левое соединение (LEFT JOIN) таблицы дополнительных реквизитов с таблицей плана видов характеристик (ПВХ) ДополнительныеРеквизитыИСведения. На базах данных с миллионами записей такие неявные соединения приводят к полному сканированию таблиц и серьезным задержкам. Для автоматического выявления подобных неявных соединений и разыменований в коде подойдёт обработка для проверки запросов на разыменование полей составного типа.
Правильный подход (Best Practice): сначала найти саму ссылку на свойство в плане видов характеристик, а затем передать готовую ссылку в параметрах запроса. Это позволит СУБД использовать индексы напрямую.
Рассмотрим оптимизированный вариант кода:
&НаСервере
Функция НайтиКонтрагентаПоДопРеквизитуОптимизированный(ЗначениеПоиска) Экспорт
// 1. Находим ссылку на дополнительный реквизит по его уникальному имени для разработчиков
СвойствоСсылка = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя", "КодУТ_7fa830250b37400a8c11df50f9bc7e62");
Если Не ЗначениеЗаполнено(СвойствоСсылка) Тогда
ЗаписатьЛогОшибок("Не найдено свойство 'КодУТ' в плане видов характеристик!");
Возврат Справочники.Контрагенты.ПустаяСсылка();
КонецЕсли;
// 2. Выполняем быстрый поиск по индексу
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ДопРеквизиты.Ссылка КАК Контрагент
|ИЗ
| Справочник.Контрагенты.ДополнительныеРеквизиты КАК ДопРеквизиты
|ГДЕ
| ДопРеквизиты.Свойство = &СвойствоСсылка
| И ДопРеквизиты.Значение = &Значение";
Запрос.УстановитьПараметр("СвойствоСсылка", СвойствоСсылка);
Запрос.УстановитьПараметр("Значение", ЗначениеПоиска);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.Контрагент;
КонецЕсли;
Возврат Справочники.Контрагенты.ПустаяСсылка();
КонецФункции
При поиске данных важно помнить, что в системе могут существовать дублирующиеся коды или устаревшие элементы, помеченные на удаление. Если просто выбрать первую попавшуюся запись, программа может вернуть неактивного контрагента.
Для решения этой проблемы нам необходимо соединить табличную часть дополнительных реквизитов с основной таблицей справочника Контрагенты. Это позволит нам наложить фильтр на пометку удаления и настроить сортировку результатов.
Разберем пример надежного запроса с проверкой активности объекта:
&НаСервере
Функция НайтиАктивногоКонтрагента(ЗначениеПоиска) Экспорт
СвойствоСсылка = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя", "КодУТ_7fa830250b37400a8c11df50f9bc7e62");
Если Не ЗначениеЗаполнено(СвойствоСсылка) Тогда
Возврат Справочники.Контрагенты.ПустаяСсылка();
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Контрагенты.Ссылка КАК Контрагент
|ИЗ
| Справочник.Контрагенты.ДополнительныеРеквизиты КАК ДопРеквизиты
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
| ПО ДопРеквизиты.Ссылка = Контрагенты.Ссылка
|ГДЕ
| ДопРеквизиты.Свойство = &СвойствоСсылка
| И ДопРеквизиты.Значение = &Значение
| И Контрагенты.ПометкаУдаления = ЛОЖЬ
|УПОРЯДОЧИТЬ ПО
| Контрагенты.Предопределенный ОБРАТНО,
| Контрагенты.Код";
Запрос.УстановитьПараметр("СвойствоСсылка", СвойствоСсылка);
Запрос.УстановитьПараметр("Значение", ЗначениеПоиска);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.Контрагент;
КонецЕсли;
Возврат Справочники.Контрагенты.ПустаяСсылка();
КонецФункции
Использование конструкции ВНУТРЕННЕЕ СОЕДИНЕНИЕ гарантирует, что мы найдем только реально существующего контрагента, а условие Контрагенты.ПометкаУдаления = ЛОЖЬ отсекает удаленные дубли. Сортировка по полю Предопределенный ОБРАТНО выводит предопределенные элементы (если они есть) в приоритет.
В архитектуре БСП свойства делятся на два типа, и механизмы их хранения принципиально отличаются:
РегистрСведений.ДополнительныеСведения. Это позволяет не блокировать сам объект при изменении его свойств.Если администратор системы решит перевести свойство "КодУТ" из разряда реквизитов в категорию дополнительных сведений, предыдущие варианты кода перестанут работать. Для поиска по дополнительным сведениям нам потребуется написать запрос к регистру сведений:
&НаСервере
Функция НайтиКонтрагентаПоДопСведению(ЗначениеПоиска) Экспорт
СвойствоСсылка = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоРеквизиту("Имя", "КодУТ_7fa830250b37400a8c11df50f9bc7e62");
Если Не ЗначениеЗаполнено(СвойствоСсылка) Тогда
Возврат Справочники.Контрагенты.ПустаяСсылка();
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ДопСведения.Объект КАК Контрагент
|ИЗ
| РегистрСведений.ДополнительныеСведения КАК ДопСведения
|ГДЕ
| ДопСведения.Свойство = &СвойствоСсылка
| И ДопСведения.Значение = &Значение";
Запрос.УстановитьПараметр("СвойствоСсылка", СвойствоСсылка);
Запрос.УстановитьПараметр("Значение", ЗначениеПоиска);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
// Проверяем тип найденного объекта (поскольку измерение 'Объект' имеет составной тип)
Если ТипЗнч(Выборка.Контрагент) = Тип("СправочникСсылка.Контрагенты") Тогда
Возврат Выборка.Контрагент;
КонецЕсли;
КонецЕсли;
Возврат Справочники.Контрагенты.ПустаяСсылка();
КонецФункции
Если ваша конфигурация разработана на основе БСП (а это практически все современные типовые решения от 1С: УТ, ERP, КА, УНФ, БП), то для чтения и записи дополнительных реквизитов не обязательно писать запросы вручную. Рекомендуется использовать встроенный программный интерфейс общего модуля УправлениеСвойствами.
Рассмотрим полезные методы этого модуля, которые значительно упрощают разработку:
Если у вас уже есть ссылка на контрагента и вам нужно быстро прочитать значение его дополнительного реквизита, используйте функцию:
Значение = УправлениеСвойствами.ЗначениеСвойства(СсылкаНаОбъект, СвойствоСсылка);
Для получения структуры всех заполненных свойств объекта можно использовать метод:
СтруктураСвойств = УправлениеСвойствами.ЗначенияСвойств(СсылкаНаОбъект);
Чтобы программно изменить значение дополнительного реквизита (или сведения), не нужно вручную открывать объект на чтение/запись и перезаписывать табличную часть. БСП сделает это автоматически в рамках одной транзакции:
УправлениеСвойствами.ЗаписатьСвойстваУОбъекта(СсылкаНаОбъект, ТаблицаСвойств);
В данном случае метод сам определит, куда именно нужно записать данные — в табличную часть объекта или в регистр сведений.
Использование стандартных функций БСП делает ваш код более лаконичным, снижает вероятность возникновения ошибок при обновлении конфигурации и обеспечивает строгое соблюдение установленных прав доступа и бизнес-правил платформы. Для быстрого поиска готовых примеров вызова процедур и функций подсистем можно использовать справочник по методам БСП.