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