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