Работа с платформой 1С:Предприятие 7.7 имеет множество специфических особенностей, которые часто ставят в тупик даже опытных разработчиков, привыкших к более современным версиям платформы (таким как 1С 8.3). В этой статье мы подробно разберем две популярные технические проблемы, с которыми программисты сталкиваются при работе со справочниками в среде 1С 7.7:
НайтиПоРеквизиту().ИспользоватьВладельца() и ВыбратьЭлементыПоРеквизиту() для подчиненных справочников.Мы детально проанализируем причины возникновения этих ошибок, изучим требования платформы к конфигурации и напишем правильный, отказоустойчивый код для решения обеих задач.
---
Рассмотрим ситуацию: разработчику необходимо быстро найти элемент справочника по определенному реквизиту (например, договору по его номеру). Типичный код, который на первый взгляд кажется логичным, выглядит так:
ОбъектПоиска = СоздатьОбъект("Справочник.БП_ДоговорыСервисногоОбслуживания");
Если ОбъектПоиска.НайтиПоРеквизиту("НомерДоговора", "05149") = 1 Тогда
// Действия с найденным элементом
КонецЕсли;
При выполнении этого фрагмента система выдает ошибку рантайма: "Недостаточное число параметров передано при вызове функции/процедуры объекта". Выясним, почему так происходит.
В отличие от платформы 1С 8.х, где многие параметры методов являются необязательными и имеют значения по умолчанию, в 1С 7.7 метод НайтиПоРеквизиту() строго требует передачи трех параметров. Синтаксис метода выглядит следующим образом:
НайтиПоРеквизиту(<ИмяРеквизита>, <Значение>, <ФлагГлобальногоПоиска>)
Разберем назначение каждого параметра:
1 — искать глобально по всему справочнику, невзирая на установленные фильтры иерархии (родителя) или владельца.0 — искать только внутри установленного родителя (для иерархических справочников) или установленного владельца (для подчиненных справочников).Чтобы исправить ошибку, нам необходимо явно передать третий параметр. Исправленный и безопасный вариант кода выглядит так:
ОбъектПоиска = СоздатьОбъект("Справочник.БП_ДоговорыСервисногоОбслуживания");
// Ищем глобально по всему справочнику (третий параметр = 1)
Если ОбъектПоиска.НайтиПоРеквизиту("НомерДоговора", "05149", 1) = 1 Тогда
Предупреждение("Элемент успешно найден: " + ОбъектПоиска.ТекущийЭлемент());
Иначе
Предупреждение("Элемент с таким номером договора не найден!");
КонецЕсли;
Важный нюанс: Метод НайтиПоРеквизиту() является позиционирующим. Он находит только первый подходящий элемент в базе данных. Если в справочнике присутствуют дубликаты с одинаковыми номерами договоров, метод спозиционируется на тот, который физически расположен в таблице раньше (например, более старый элемент или даже помеченный на удаление). Если вам необходимо обработать все дубли, используйте выборку через ВыбратьЭлементыПоРеквизиту().
---
Теперь проанализируем вторую, более сложную проблему. Предположим, у нас есть подчиненный справочник (например, "БП_ОбъектыНаОбслуживании"), элементы которого принадлежат элементам справочника-владельца. Нам необходимо выбрать только те объекты обслуживания, которые:
Рассмотрим проблемный код, представленный в исходном обсуждении:
СправочникВидов = СоздатьОбъект("Справочник.БП_ВидыОбслуживаемыхОбъектов");
РеквизитПоиска = СправочникВидов.НайтиПоНаименованию("Банковское оборудование");
СпрОбъекты = СоздатьОбъект("Справочник.БП_ОбъектыНаОбслуживании");
СпрОбъекты.ВыбратьЭлементыПоРеквизиту("ВидОбъекта", СправочникВидов.ТекущийЭлемент(), 0, 0);
СпрОбъекты.ИспользоватьВладельца(ТекущийЭлемент());
Пока СпрОбъекты.ПолучитьЭлемент() = 1 Цикл
// Обработка элементов выборки...
КонецЦикла;
При выполнении этого кода отбор по владельцу полностью игнорируется, и система выбирает элементы по виду объекта для вообще всех владельцев базы данных. Давайте выясним причины такого поведения и разберем их по шагам.
В 1С 7.7 крайне важен порядок вызова методов. Установка фильтров (родителя или владельца) должна производиться строго до открытия выборки. Метод ИспользоватьВладельца() настраивает внутреннее состояние объекта справочника. Если вы сначала открыли выборку методом ВыбратьЭлементыПоРеквизиту(), а затем вызвали метод установки владельца, этот фильтр просто не применится к уже открытому набору данных.
Это самый неочевидный нюанс платформы 1С 7.7. Обратимся к описанию синтаксиса метода выборки по реквизиту:
ВыбратьЭлементыПоРеквизиту(<ИмяРеквизита>, <Значение>, <РежимИерархии>, <РежимГрупп>)
Обратите внимание на третий параметр — <РежимИерархии> (число):
1 (или параметр опущен, так как это значение по умолчанию), то выборка строится с учетом соподчиненности. В контексте подчиненных справочников "соподчиненность" означает именно подчинение владельцу! Выборка вернет только те элементы, которые принадлежат установленному владельцу.0, выборка выполняется по "плоскому" индексу всей таблицы базы данных. В этом случае система производит быстрый поиск по индексу реквизита и полностью игнорирует ранее установленный фильтр ИспользоватьВладельца()!В коде автора было написано: СпрОбъекты.ВыбратьЭлементыПоРеквизиту(..., ..., 0, 0);. Передав третьим параметром нуль, разработчик сам отключил фильтр по владельцу.
Если данный код выполняется при открытии формы элемента-владельца (например, в предопределенной процедуре ПриОткрытии()), необходимо помнить о статусе записи объекта:
ТекущийЭлемент() вернет пустое значение (пустую ссылку).ИспользоватьВладельца() приведет к тому, что выборка попытается найти подчиненные элементы с пустым владельцем. Поскольку у существующих элементов владелец всегда заполнен, выборка гарантированно окажется пустой.Для предотвращения этой ситуации необходимо обязательно проверять, записан ли элемент в базу данных, используя метод Выбран().
---
Объединим все наши выводы и напишем надежный алгоритм. Мы добавим проверку успешности поиска вида объекта (чтобы избежать "быдлокода", на который справедливо указали коллеги на форуме), добавим проверку существования текущего элемента владельца и правильно настроим параметры выборки.
Посмотрим на пример правильной реализации:
Процедура ЗаполнитьТаблицуОбъектов()
// 1. Инициализируем справочник видов обслуживания и ищем нужный элемент
СправочникВидов = СоздатьОбъект("Справочник.БП_ВидыОбслуживаемыхОбъектов");
Если СправочникВидов.НайтиПоНаименованию("Банковское оборудование", 0, 1) = 0 Тогда
Сообщить("Внимание: Вид обслуживаемых объектов 'Банковское оборудование' не найден в базе!", "!");
Возврат;
КонецЕсли;
// 2. Проверяем, записан ли текущий элемент (владелец) в базу данных
Если Выбран() = 0 Тогда
// Если это новый элемент, подчиненных объектов у него быть не может.
// Выходим из процедуры, чтобы не вызывать пустых выборок.
Возврат;
КонецЕсли;
// 3. Создаем объект подчиненного справочника
СпрОбъекты = СоздатьОбъект("Справочник.БП_ОбъектыНаОбслуживании");
// 4. Сначала устанавливаем фильтр по владельцу!
СпрОбъекты.ИспользоватьВладельца(ТекущийЭлемент());
// 5. Открываем выборку по реквизиту.
// Третий параметр ОБЯЗАТЕЛЬНО равен 1, чтобы сработал отбор по владельцу.
СпрОбъекты.ВыбратьЭлементыПоРеквизиту("ВидОбъекта", СправочникВидов.ТекущийЭлемент(), 1, 0);
// 6. Обходим результаты выборки в цикле
Пока СпрОбъекты.ПолучитьЭлемент() = 1 Цикл
// Дополнительная проверка периодического реквизита "НаОбслуживании" на текущую дату
Если СпрОбъекты.НаОбслуживании.Получить(РабочаяДата()) = 1 Тогда
// Заполняем строки таблицы на форме (например, ТЗ СпрОбъектыБанк)
СпрОбъектыБанк.НоваяСтрока();
СпрОбъектыБанк.Объект = СпрОбъекты.ТекущийЭлемент();
СпрОбъектыБанк.СерийныйНомер = СпрОбъекты.СерийныйНомер.ЗаводскойНомер;
СпрОбъектыБанк.Выбран = "-";
СпрОбъектыБанк.Механик = СпрОбъекты.Сотрудник;
СпрОбъектыБанк.ДатаРемонта = СпрОбъекты.ДатаРемонта.Получить(РабочаяДата());
СпрОбъектыБанк.Подразделение = СпрОбъекты.Подразделение;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Проанализировав данное решение, мы видим несколько важных преимуществ:
Выбран() предохраняет алгоритм от некорректного поведения при открытии формы создания нового элемента.1), выборка возвращает строго подчиненные текущему владельцу объекты.Используйте эти правила при разработке в среде 1С 7.7, обращайте внимание на полноту передачи параметров стандартных методов платформы и всегда проверяйте результаты позиционирования справочников перед их дальнейшим использованием.