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