Как выполнить НайтиПоНаименованию для реквизита с составным типом данных

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При разработке в среде 1С программисты часто сталкиваются с ситуацией, когда реквизит объекта имеет составной тип данных, включающий в себя несколько различных справочников. Типичная ошибка новичка — попытка вызвать метод поиска непосредственно у значения этого реквизита. Рассмотрим, почему возникает ошибка Метод объекта не обнаружен (НайтиПоНаименованию), и разберем правильные способы реализации этой задачи.

Анализ проблемы: Ссылка vs Менеджер

Разберем ситуацию, описанную автором темы. Попытка выполнить код вида ЭлементыФормы.Товары.ТекущиеДанные.Номенклатура.Скрипт.НайтиПоНаименованию(Скрипты) приводит к краху программы. Выясним причину такого поведения платформы.

В системе 1С:Предприятие существует четкое разделение между Менеджером справочника и Ссылкой:

  1. Менеджер (например, Справочники.Номенклатура) — это объект, который управляет всей таблицей в базе данных. Именно у него есть методы для создания новых элементов, поиска по коду или наименованию.
  2. Ссылка (значение реквизита) — это конкретная запись в таблице. У ссылки есть свойства (реквизиты), но у нее нет и не может быть метода НайтиПоНаименованию, так как она сама является результатом поиска.

Когда мы обращаемся к Номенклатура.Скрипт, мы получаем либо конкретную ссылку, либо Неопределено, либо ПустаяСсылка. Ни в одном из этих случаев метод поиска недоступен.

Решение 1: Динамическое обращение к менеджеру через метаданные

Проанализируем самый быстрый способ решения задачи в обычных формах. Если нам заранее неизвестно, какой именно справочник выбрал пользователь в поле со множественным типом, мы можем программно определить имя этого справочника и обратиться к соответствующему менеджеру через коллекцию Справочники.

Рассмотрим пример реализации:


// Получаем текущую строку данных
ТекДанные = ЭлементыФормы.Товары.ТекущиеДанные;

// Проверяем, заполнено ли значение, чтобы избежать ошибки при обращении к метаданным
Если ТекДанные <> Неопределено И ЗначениеЗаполнено(ТекДанные.Номенклатура.Скрипт) Тогда
    
    // Получаем имя метаданных текущего значения (например, "Договоры" или "Сотрудники")
    ИмяСправочника = ТекДанные.Номенклатура.Скрипт.Метаданные().Имя;
    
    // Обращаемся к менеджеру справочника динамически и выполняем поиск
    НайденнаяСсылка = Справочники[ИмяСправочника].НайтиПоНаименованию(ИскомоеИмя, Истина);
    
    Если Не НайденнаяСсылка.Пустая() Тогда
        // Обработка результата
    КонецЕсли;
    
КонецЕсли;

Важно помнить, что метод Метаданные() доступен на стороне клиента в обычных формах, но в управляемом приложении (тонкий клиент) такой подход может работать медленно или требовать серверного контекста. Кроме того, всегда указывайте второй параметр Истина в методе НайтиПоНаименованию, если вам нужно точное совпадение, иначе система вернет первый элемент, начинающийся с этой строки.

Решение 2: Использование запроса для универсального поиска

Рассмотрим более профессиональный и надежный метод. Если реквизит может содержать, к примеру, 7 разных типов справочников, а нам нужно найти элемент с определенным наименованием среди них, использование запроса будет гораздо эффективнее. Это позволяет избежать проблем с правами доступа и контекстными вызовами.

Проанализируем ситуацию: нам нужно найти объект по строке Скрипты, зная тип, который уже выбран в строке таблицы. Посмотрим на пример кода:


Функция НайтиОбъектСоставногоТипа(ТекущаяСсылка, ИмяДляПоиска)
    
    // Определяем тип текущего значения
    ТипОбъекта = ТипЗнч(ТекущаяСсылка);
    
    // Если ссылка пустая, поиск не имеет смысла в данном контексте
    Если ТекущаяСсылка.Пустая() Тогда
        Возврат Неопределено;
    КонецЕсли;
    
    // Создаем запрос
    Запрос = Новый Запрос;
    // Формируем текст запроса динамически на основании метаданных типа
    ИмяТаблицы = Метаданные.НайтиПоТипу(ТипОбъекта).ПолноеИмя();
    
    Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |   Т.Ссылка КАК Ссылка
    |ИЗ
    |   " + ИмяТаблицы + " КАК Т
    |ГДЕ
    |   Т.Наименование ПОДОБНО &Наименование";
    
    Запрос.УстановитьПараметр("Наименование", ИмяДляПоиска);
    
    Результат = Запрос.Выполнить();
    Если Не Результат.Пустой() Тогда
        Выборка = Результат.Выбрать();
        Выборка.Следующий();
        Возврат Выборка.Ссылка;
    КонецЕсли;
    
    Возврат Неопределено;
КонецФункции

Решение 3: Поиск во всех возможных справочниках (UNION ALL)

Иногда задача стоит иначе: пользователь еще ничего не выбрал, и нам нужно найти элемент с конкретным наименованием во всех справочниках, которые входят в составной тип реквизита Скрипт. В этом случае мы используем объединение запросов.

Разберем по шагам, как это реализовать:

  1. Создаем список имен таблиц, входящих в составной тип.
  2. Для каждой таблицы формируем блок ВЫБРАТЬ... ИЗ....
  3. Объединяем их через ОБЪЕДИНИТЬ ВСЕ.

Посмотрим на пример структуры такого запроса:


Запрос.Текст = 
"ВЫБРАТЬ Ссылка ИЗ Справочник.СкладскиеСкрипты ГДЕ Наименование = &Имя
|ОБЪЕДИНИТЬ ВСЕ
|ВЫБРАТЬ Ссылка ИЗ Справочник.ПользовательскиеСкрипты ГДЕ Наименование = &Имя
|ОБЪЕДИНИТЬ ВСЕ
|ВЫБРАТЬ Ссылка ИЗ Справочник.СистемныеСкрипты ГДЕ Наименование = &Имя";

Такой подход гарантирует, что вы найдете все вхождения в базе данных за один сеанс связи с сервером, что критически важно для производительности системы.

Почему решение через Справочники[ИмяСправочника] назвали шуткой?

В обсуждении на форуме одно из решений было названо "шуточным" (сообщение №8). Выясним причину такой иронии. Несмотря на то, что код Справочники[Имя].НайтиПоНаименованию() синтаксически верен, у него есть несколько "подводных камней":

Рекомендация: Для надежности всегда проверяйте тип значения перед поиском, используя функцию ТипЗнч(), и по возможности переносите логику поиска в серверные модули, используя объект Запрос.

← На главную