При разработке обработок загрузки, синхронизации или автоматической классификации файлов в «1С:Документооборот» разработчики нередко сталкиваются с необходимостью найти или перебрать подчиненные каталоги. На начальном этапе часто возникает путаница: термин «папка» воспринимается как директория на диске файловой системы сервера, тогда как в конфигурации речь идет об элементах и группах иерархического справочника (например, СправочникСсылка.ПапкиВнутреннихДокументов или СправочникСсылка.ПапкиФайлов).
Разберем подробно, почему стандартные файловые функции здесь не подходят, как правильно организовать обход подчиненных папок в иерархии справочника и как избежать типичных ошибок при сопоставлении наименований.
Посмотрим на ситуацию, с которой начинается большинство подобных ошибок. Если попытаться применить функцию НайтиФайлы() к реквизиту формы, содержащему ссылку на справочник, функция не даст результата либо завершится аварийно, так как ожидает строковый путь к физическому диску (например, "C:\Storage\Docs"):
// Ошибочный подход: попытка передать ссылку на справочник в файловую функцию
МассивКаталогов = НайтиФайлы(ПутьКПапке, "*.*", Истина);
Если нам действительно нужно работать с физическими каталогами на диске или сетевом ресурсе, метод НайтиФайлы() вызывается исключительно со строковыми путями:
// Корректная работа с файловой системой
МассивФайлов = НайтиФайлы("D:\Data\Documents", "*", Истина);
Для Каждого Файл Из МассивФайлов Цикл
Если Файл.ЭтоКаталог() Тогда
Сообщить("Найден каталог: " + Файл.ПолноеИмя);
КонецЕсли;
КонецЦикла;
Когда же переменная содержит ссылку на элемент прикладного объекта СправочникСсылка.ПапкиВнутреннихДокументов, задача переходит в плоскость работы с иерархическими справочниками 1С.
Для последовательного обхода всех подчиненных элементов и групп в платформе 1С предусмотрен метод менеджера справочника ВыбратьИерархически(). Проанализируем правильный порядок работы с этим методом:
ВыбратьИерархически(Родитель).Следующий() перемещает указатель на очередную запись. Важно помнить, что до первого вызова Следующий() выборка не позиционирована, и обращение к ее полям (например, Выборка.Наименование) приведет к ошибке или вернет пустое значение.СправочникВыборка). Чтобы сохранить ссылку на найденную папку, необходимо явно обращаться к свойству Выборка.Ссылка, а не присваивать саму выборку.Рассмотрим пример кода с исправлением логических ошибок поиска:
&НаСервере
Функция НайтиПодпапкуПоИмени(РодительскаяПапка, ИскомоеИмя)
НайденнаяПапка = Справочники.ПапкиВнутреннихДокументов.ПустаяСсылка();
ИмяДляПоиска = СокрЛП(ИскомоеИмя);
// Получаем иерархическую выборку с фильтром по родителю
Выборка = Справочники.ПапкиВнутреннихДокументов.ВыбратьИерархически(РодительскаяПапка);
Пока Выборка.Следующий() Цикл
// Пропускаем помеченные на удаление папки при необходимости
Если Выборка.ПометкаУдаления Тогда
Продолжить;
КонецЕсли;
// Очищаем строки от невидимых пробелов для корректного сравнения
Если СокрЛП(Выборка.Наименование) = ИмяДляПоиска Тогда
НайденнаяПапка = Выборка.Ссылка; // Получаем именно ссылку
Прервать; // Прерываем цикл сразу после нахождения
КонецЕсли;
КонецЦикла;
Возврат НайденнаяПапка;
КонецФункции
Обратите внимание на конструкцию Прервать. В исходных вариантах без прерывания цикла переменная состояния или сообщения об ошибке перезаписываются на каждой последующей итерации, из-за чего программа считает, что папка не найдена, даже если совпадение произошло в середине списка.
Хотя последовательный перебор через ВыбратьИерархически() нагляден, при большом количестве вложенных папок он работает медленно. Рассмотрим более производительные решения.
Если требуется найти конкретную папку по точному совпадению имени внутри известного родителя, платформа предоставляет встроенный инструмент:
// Параметры: Наименование, ТочноеСоответствие, Родитель
НайденнаяПапка = Справочники.ПапкиВнутреннихДокументов.НайтиПоНаименованию(
СокрЛП(ТекСтрока[ИндексКолонки]),
Истина,
ПутьКПапке
);
Если Не ЗначениеЗаполнено(НайденнаяПапка) Тогда
Сообщить("Папка не найдена в указанном каталоге!");
Иначе
Сообщить("Найдена папка: " + Строка(НайденнаяПапка));
КонецЕсли;
Запрос работает быстрее всего, позволяет сразу отсечь помеченные на удаление элементы и гибко настроить глубину поиска:
Родитель = &Родитель.Родитель В ИЕРАРХИИ(&Родитель).Посмотрим на реализацию серверной функции поиска через запрос:
&НаСервере
Функция НайтиПапкуЗапросом(РодительскаяПапка, ИмяПодпапки, ИскатьВоВсехВложенных = Ложь)
Запрос = Новый Запрос;
ТекстЗапроса =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Папки.Ссылка КАК Ссылка
|ИЗ
| Справочник.ПапкиВнутреннихДокументов КАК Папки
|ГДЕ
| НЕ Папки.ПометкаУдаления
| И Папки.Наименование = &Наименование";
Если ИскатьВоВсехВложенных Тогда
ТекстЗапроса = ТекстЗапроса + " И Папки.Родитель В ИЕРАРХИИ(&Родитель)";
Иначе
ТекстЗапроса = ТекстЗапроса + " И Папки.Родитель = &Родитель";
КонецЕсли;
Запрос.Текст = ТекстЗапроса;
Запрос.УстановитьПараметр("Наименование", СокрЛП(ИмяПодпапки));
Запрос.УстановитьПараметр("Родитель", РодительскаяПапка);
Результат = Запрос.Выполнить();
Если Не Результат.Пустой() Тогда
Выборка = Результат.Выбрать();
Выборка.Следующий();
Возврат Выборка.Ссылка;
КонецЕсли;
Возврат Справочники.ПапкиВнутреннихДокументов.ПустаяСсылка();
КонецФункции
При работе со структурой папок внутренних документов обязательно учитывайте следующие системные ограничения:
УстановитьПривилегированныйРежим(Истина).ВыбратьИерархически() всегда возвращает удаленные элементы. Если не проверять признак ПометкаУдаления, алгоритм может сопоставить данные со старой удаленной папкой-дублем.Символы.НПП) или пробелы на концах. Всегда оборачивайте сравниваемые переменные в СокрЛП() и при необходимости очищайте их от спецсимволов.