Как в 1С 7.7 получить строковый идентификатор (ИД) из вспомогательного справочника по ссылке на элемент номенклатуры?

Программист 1С v7.7
← На главную

Работая с конфигурациями на платформе 1С 7.7, мы часто сталкиваемся с необходимостью извлечения данных из связанных справочников. В этой теме мы рассмотрим, как по ссылке на элемент стандартного справочника «Номенклатура» найти соответствующий строковый идентификатор (ИД) в дополнительном справочнике «ИД_Номенклатура». Эта задача требует специфических знаний особенностей работы с объектами и методами платформы 7.7.

Предположим, у нас есть следующая структура данных:

  1. Стандартный справочник Номенклатура, содержащий основные сведения о товарах.
  2. Вспомогательный справочник ИД_Номенклатура, в котором хранятся два ключевых реквизита:
    • Ссылка: реквизит типа «Справочник.Номенклатура», который указывает на соответствующий элемент из справочника Номенклатура.
    • ИД: реквизит типа «Строка», который содержит уникальный строковый идентификатор для каждой номенклатуры.

Наша цель — по известному элементу справочника Номенклатура найти связанный с ним элемент в справочнике ИД_Номенклатура и получить его строковый ИД.

Решение 1: Использование метода НайтиПоРеквизиту()

Наиболее прямым и эффективным способом решения этой задачи является использование метода НайтиПоРеквизиту() объекта «Справочник» в 1С 7.7. Этот метод позволяет искать элемент справочника по значению одного из его реквизитов.

Давайте подробно разберем его синтаксис и особенности применения:

  1. Синтаксис метода: Справочник.НайтиПоРеквизиту(ИмяРеквизита, Значение, Глобально)
    • ИмяРеквизита (Строка): Это строковое имя реквизита, по которому мы хотим выполнить поиск. В нашем случае, это будет "Ссылка", поскольку именно в этом реквизите справочника ИД_Номенклатура хранится ссылка на Номенклатуру. Мы обязательно указываем имя реквизита так, как оно задано в конфигураторе.
    • Значение (Произвольный): Это то значение, которое мы ищем. В нашем сценарии, это будет переменная, содержащая ссылку на конкретный элемент справочника Номенклатура.
    • Глобально (Число): Этот параметр определяет область поиска:
      • 0: Поиск будет осуществляться в пределах подчинения (если справочник подчиненный) или в пределах текущего родителя (для иерархических справочников).
      • 1: Поиск будет выполнен по всему справочнику, игнорируя иерархию и подчинение. В нашем случае, если справочник ИД_Номенклатура не подчиненный и не иерархический, или если мы хотим быть уверены, что поиск охватит все элементы, нам следует установить этот параметр в 1.
  2. Возвращаемое значение: Метод НайтиПоРеквизиту() возвращает число. Если элемент с заданным значением реквизита найден, метод возвращает 1. Если элемент не найден, возвращается 0.
  3. Критически важное замечание о реквизите "Сортировка": Мы должны помнить об одном важном нюансе платформы 1С 7.7, который часто становится источником ошибок. Метод НайтиПоРеквизиту() корректно работает только для тех реквизитов, у которых в конфигураторе, в свойствах реквизита (раздел "Дополнительные"), установлен признак "Сортировка". Если этот признак не установлен, платформа не сможет эффективно искать по этому реквизиту, и вы можете получить ошибку "Неверное имя реквизита" или, что еще хуже, метод просто не найдет нужный элемент, даже если он существует. Поэтому, прежде чем использовать этот метод, мы убеждаемся, что у реквизита "Ссылка" в справочнике ИД_Номенклатура установлен флажок "Сортировка". Это обеспечивает индексирование реквизита и значительно ускоряет поиск.

Пример кода с использованием НайтиПоРеквизиту()

Давайте посмотрим на практический пример, который был предложен и подтвержден на форуме:


// Предположим, у нас уже есть ссылка на искомую номенклатуру
// Например, мы получили ее из текущего элемента формы, документа или другого справочника
ИскомаяНоменклатура = Справочник.Номенклатура.НайтиПоКоду("00001"); // Пример получения ссылки

// Создаем объект для работы со справочником "ИД_Номенклатура"
СпрИДНоменклатура = СоздатьОбъект("Справочник.ИД_Номенклатура");

// Выполняем поиск элемента в справочнике "ИД_Номенклатура"
// по реквизиту "Ссылка", используя нашу ИскомаяНоменклатура
// Параметр 1 означает глобальный поиск по всему справочнику
Если СпрИДНоменклатура.НайтиПоРеквизиту("Ссылка", ИскомаяНоменклатура, 1) = 1 Тогда
    // Если элемент найден, объект СпрИДНоменклатура позиционируется на нем
    // Теперь мы можем напрямую обратиться к реквизиту "ИД" найденного элемента
    ИД = СпрИДНоменклатура.ИД;
    Сообщить("Найден ИД для номенклатуры " + ИскомаяНоменклатура.Наименование + ": " + ИД);
Иначе
    // Если элемент не найден
    Сообщить("ИД для номенклатуры " + ИскомаяНоменклатура.Наименование + " не найден в справочнике ИД_Номенклатура!");
КонецЕсли;

После успешного выполнения метода НайтиПоРеквизиту(), объект СпрИДНоменклатура автоматически "позиционируется" на найденном элементе. Это означает, что мы можем напрямую обращаться к его реквизитам, например, СпрИДНоменклатура.ИД, чтобы получить значение строкового идентификатора.

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

Решение 2: Перебор элементов справочника в цикле

В случае, если реквизит "Ссылка" не индексирован (то есть, не установлен флажок "Сортировка"), или если мы по какой-либо причине не хотим использовать НайтиПоРеквизиту(), мы всегда можем прибегнуть к полному перебору элементов справочника. Этот метод менее эффективен для больших справочников, но гарантированно работает независимо от настроек индексации.

Пример кода с использованием ВыбратьЭлементы() и ПолучитьЭлемент()


// Предположим, у нас уже есть ссылка на искомую номенклатуру
ИскомаяНоменклатура = Справочник.Номенклатура.НайтиПоКоду("00001"); // Пример получения ссылки

// Создаем объект для работы со справочником "ИД_Номенклатура"
СпрИДНоменклатура = СоздатьОбъект("Справочник.ИД_Номенклатура");
ИД_Найден = ""; // Переменная для хранения найденного ИД

// Открываем выборку элементов справочника.
Если СпрИДНоменклатура.ВыбратьЭлементы() = 1 Тогда
    // В цикле перебираем все элементы справочника
    Пока СпрИДНоменклатура.ПолучитьЭлемент() = 1 Цикл
        // Проверяем, соответствует ли реквизит "Ссылка" текущего элемента
        // нашей искомой номенклатуре
        Если СпрИДНоменклатура.Ссылка = ИскомаяНоменклатура Тогда
            ИД_Найден = СпрИДНоменклатура.ИД;
            Сообщить("Найден ИД для номенклатуры " + ИскомаяНоменклатура.Наименование + " (перебор): " + ИД_Найден);
            Прервать; // Выходим из цикла, как только найдем первый подходящий элемент
        КонецЕсли;
    КонецЦикла;
КонецЕсли;

Если ПустоеЗначение(ИД_Найден) = 1 Тогда
    Сообщить("ИД для номенклатуры " + ИскомаяНоменклатура.Наименование + " не найден (перебор)!");
КонецЕсли;

Этот подход работает следующим образом: сначала мы открываем выборку всех элементов справочника с помощью ВыбратьЭлементы(). Затем в цикле Пока СпрИДНоменклатура.ПолучитьЭлемент() = 1 мы последовательно получаем каждый элемент справочника. Для каждого элемента мы проверяем значение его реквизита Ссылка. Как только мы находим совпадение, мы извлекаем значение реквизита ИД и прерываем цикл, так как задача выполнена. Если цикл завершился без нахождения элемента, значит, соответствующего ИД нет.

Решение 3: Использование объекта "Запрос"

Для более сложных сценариев, когда требуется выполнить выборку по нескольким условиям, объединить данные из разных таблиц, или применить агрегатные функции, мы можем использовать объект "Запрос". В нашем случае, для простого поиска по одному реквизиту, запрос может быть избыточным, но он демонстрирует еще один мощный механизм работы с данными в 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. НайтиПоРеквизиту():
    • Плюсы: Это самый быстрый и эффективный способ для прямого поиска по одному индексированному реквизиту. Если реквизит "Ссылка" в справочнике ИД_Номенклатура имеет признак "Сортировка", этот метод будет работать очень быстро, даже на больших объемах данных. Он прост в написании и понимании.
    • Минусы: Требует, чтобы искомый реквизит был индексирован (имел признак "Сортировка"). Не подходит для поиска по нескольким реквизитам одновременно (без использования дополнительных условий в коде после нахождения), а также для сложных условий, соединений или агрегирования данных.
    • Рекомендация: Используйте этот метод как основной для вашей задачи, если реквизит "Ссылка" проиндексирован. Он идеально подходит для сценария "поиск по ключу".
  2. Перебор элементов в цикле (ВыбратьЭлементы() / ПолучитьЭлемент()):
    • Плюсы: Всегда работает, независимо от настроек индексации реквизитов. Простота реализации для несложных условий.
    • Минусы: Медленный для больших справочников, так как требует полного перебора всех элементов до нахождения нужного. Это может значительно нагрузить систему при частых вызовах.
    • Рекомендация: Применяйте этот метод, если искомый реквизит не проиндексирован, или если справочник гарантированно небольшой, и производительность не является критически важной.
  3. Использование объекта "Запрос":
    • Плюсы: Наиболее мощный и гибкий инструмент для работы с данными. Позволяет выполнять сложные выборки, объединения (JOIN), фильтрации по нескольким полям, группировки и агрегатные функции. Для сложных сценариев может быть значительно быстрее, чем перебор в цикле.
    • Минусы: Для простых задач, таких как наша, может быть избыточным и несколько сложнее в написании, чем НайтиПоРеквизиту(). Требует знания языка запросов 1С 7.7.
    • Рекомендация: Используйте запросы, когда ваша задача выходит за рамки простого поиска по одному ключу: например, если вам нужно найти ИД по комбинации номенклатуры и какой-то даты, или если вы хотите получить сразу несколько ИД для списка номенклатуры с определенным условием. В текущей задаче это рабочий, но не оптимальный вариант.

В контексте исходной задачи, где требуется найти ИД по конкретной Номенклатуре, использование метода НайтиПоРеквизиту("Ссылка", ИскомаяНоменклатура, 1) является наиболее прямым, чистым и рекомендованным решением при условии, что реквизит Ссылка проиндексирован. Этот подход был успешно реализован и подтвержден на практике.

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

← На главную