При разработке и сопровождении конфигураций на платформе 1С:Предприятие 7.7 часто возникает задача пакетной обработки справочников: найти определенные папки (группы) внутри заданной ветки иерархии и автоматически создать в каждой из них новые элементы с заданными реквизитами.
Многие разработчики пробуют решить эту задачу с помощью штатного объекта Запрос, однако сталкиваются с неожиданным поведением: запрос по группам возвращает пустой результат. Выясним причину этой особенности и разберем корректные способы решения задачи как штатными средствами встроенного языка, так и через прямые SQL-запросы.
Проанализируем архитектуру встроенного языка запросов платформы 1С 7.7. Штатный объект Запрос предназначен исключительно для выборки детальных записей — конечных элементов справочника. Группировки в тексте запроса строятся поверх существующих элементов.
Из этого следуют два важных ограничения:
Запрос вообще не включит такую группу в результат выборки.Наименование) накладывается на элементы, а не на сами группы. При попытке отфильтровать выборку по имени группы условие отсечет все строки, и результат окажется пустым.Для гарантированного обхода папок следует использовать либо последовательную выборку методами самого справочника, либо прямой SQL-запрос к таблице базы данных.
Рассмотрим по шагам реализацию задачи без использования внешних библиотек и компонентов. Этот способ универсален и одинаково надежно работает как в файловом режиме (DBF), так и в клиент-серверном (SQL).
НайтиПоНаименованию с третьим параметром 2, который указывает платформе искать исключительно среди групп.ИспользоватьРодителя(). Метод ВключатьПодчиненные(1) позволит просматривать все вложенные уровни иерархии (если передать 0, выборка пойдет только по прямому первому уровню вложенности).Новый() на том же экземпляре объекта справочника, по которому выполняется цикл ПолучитьЭлемент(). Вызов Новый() сбросит указатель текущей выборки. Для добавления нового элемента всегда используем отдельный независимый экземпляр справочника.Посмотрим на пример программного кода:
Процедура СоздатьЭлементыВПапках(ИмяИскомойПапки, ИмяНовогоЭлемента)
Перем СпрРодитель, СпрВыборка, СпрСоздание;
// 1. Ищем базовую родительскую группу верхнего уровня "Дистрибутив"
СпрРодитель = СоздатьОбъект("Справочник.Программы");
// Третий параметр 2 — поиск только среди групп
Если СпрРодитель.НайтиПоНаименованию("Дистрибутив", 0, 2) = 0 Тогда
Предупреждение("Базовая группа 'Дистрибутив' не найдена!");
Возврат;
КонецЕсли;
// 2. Настраиваем выборку по иерархии найденного родителя
СпрВыборка = СоздатьОбъект("Справочник.Программы");
СпрВыборка.ИспользоватьРодителя(СпрРодитель.ТекущийЭлемент());
СпрВыборка.ВключатьПодчиненные(1); // Обход всех вложенных уровней
СпрВыборка.ВыбратьЭлементы();
// Отдельный объект для создания новых записей
СпрСоздание = СоздатьОбъект("Справочник.Программы");
// 3. Обходим структуру и ищем нужные папки
Пока СпрВыборка.ПолучитьЭлемент() = 1 Цикл
// Проверяем, что текущий объект — это группа, и ее имя совпадает с искомым
Если (СпрВыборка.ЭтоГруппа() = 1) И (СпрВыборка.Наименование = ИмяИскомойПапки) Тогда
// Создаем новый элемент внутри найденной папки
СпрСоздание.ИспользоватьРодителя(СпрВыборка.ТекущийЭлемент());
СпрСоздание.Новый();
СпрСоздание.Наименование = ИмяНовогоЭлемента;
СпрСоздание.Записать();
КонецЕсли;
КонецЦикла;
Предупреждение("Обработка успешно завершена!");
КонецПроцедуры
Если информационная база работает под управлением Microsoft SQL Server, а справочник содержит десятки тысяч элементов, штатный перебор может занять продолжительное время. В такой ситуации разберем более быстрое решение — использование внешней компоненты 1С++ и класса ODBCRecordSet.
В структуре таблиц 1С 7.7 признак группы хранится в поле ISFOLDER (значение 2 соответствует группе/папке, а 1 — конечному элементу). Поле наименования обозначается как DESCR.
Рассмотрим пример процедуры с выполнением прямого запроса:
Процедура СформироватьSQL()
Перем Запрос, ТекстЗапроса, ТЗ, СпрСоздание, ИмяИскомойПапки, ИмяНовогоЭлемента;
ИмяИскомойПапки = "Онлайн версия";
ИмяНовогоЭлемента = "Новый релиз";
Попытка
Запрос = СоздатьОбъект("ODBCRecordSet");
Исключение
Если ЗагрузитьВнешнююКомпоненту("1cpp.dll") = 0 Тогда
Предупреждение("Компонента 1cpp.dll не найдена в каталоге базы или в BIN!");
Возврат;
КонецЕсли;
Запрос = СоздатьОбъект("ODBCRecordSet");
КонецПопытки;
// Формируем прямой запрос к таблице справочника
ТекстЗапроса = "
|SELECT id [Элем $Справочник.Программы]
|FROM $Справочник.Программы
|WHERE isfolder = 2
| AND descr LIKE '%:Парам%'
|";
Запрос.УстановитьТекстовыйПараметр("Парам", ИмяИскомойПапки);
ТЗ = Запрос.ВыполнитьИнструкцию(ТекстЗапроса);
// Обходим полученную таблицу значений и создаем элементы в найденных группах
СпрСоздание = СоздатьОбъект("Справочник.Программы");
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл
СпрСоздание.ИспользоватьРодителя(ТЗ.Элем);
СпрСоздание.Новый();
СпрСоздание.Наименование = ИмяНовогоЭлемента;
СпрСоздание.Записать();
КонецЦикла;
Предупреждение("Элементы успешно созданы!");
КонецПроцедуры
Обратите внимание: если необходимо строго ограничить поиск только веткой родительской папки «Дистрибутив», в блок WHERE прямого SQL-запроса можно добавить условие по идентификатору родителя parentid = :ИД_Родителя или использовать конструкцию В ИЕРАРХИИ(:Родитель).
При работе с иерархией справочников в 1С 7.7 важно помнить:
Запрос не подходит для выборки пустых групп и фильтрации по наименованиям папок.НайтиПоНаименованию() с флагом поиска по группам, методы ИспользоватьРодителя(), ВключатьПодчиненные() и проверку ЭтоГруппа() = 1.