Часто перед разработчиком стоит задача перенести данные из внешних файлов (Excel, CSV) в справочники 1С, имеющие сложную иерархическую структуру. Например, общероссийские классификаторы (ОКПД, ОКВЭД), где код элемента сам по себе диктует уровень вложенности. Просто загрузить строки в плоский список недостаточно — необходимо программно воссоздать «дерево» групп и подгрупп. В этой статье мы подробно разберем, как реализовать такой алгоритм, учитывая особенности платформы и структуры кодов. Для решения такой задачи без ручной разработки можно использовать универсальную обработку для импорта справочников из Excel.
Рассмотрим ситуацию, когда в Excel-файле содержатся коды вида А, 01, 01.1, 01.11, 01.11.11.110. Нам нужно, чтобы программа понимала: элемент с кодом А — это корневая группа, 01 — подгруппа внутри А, а длинный цифровой код — конечный элемент. Основная сложность заключается в том, что в файле данные идут подряд, и нам нужно на лету определять родителя для каждой новой записи.
Для решения задачи мы разделим процесс на три этапа: чтение файла, разбор кода (парсинг) для определения уровня иерархии и корректное создание объектов в базе данных 1С.
Для чтения файлов Excel наиболее стабильным и быстрым способом является использование встроенного объекта ТабличныйДокумент. Этот метод не требует наличия установленного Microsoft Office на сервере или компьютере пользователя. Проанализируем базовый код для чтения колонок:
ТабДок = Новый ТабличныйДокумент;
Попытка
ТабДок.Прочитать(ПутьКФайлу, СпособЧтенияЗначенийТабличногоДокумента.Значение);
Исключение
Сообщить("Не удалось прочитать файл!");
Возврат;
КонецПопытки;
КоличествоСтрок = ТабДок.ВысотаТаблицы;
// Цикл по строкам файла, начиная со второй (пропускаем заголовок)
Для НомерСтроки = 2 По КоличествоСтрок Цикл
КодИзФайла = СокрЛП(ТабДок.ПолучитьОбласть("R" + Формат(НомерСтроки, "ЧГ=0") + "C1").ТекущаяОбласть.Текст);
НаименованиеИзФайла = СокрЛП(ТабДок.ПолучитьОбласть("R" + Формат(НомерСтроки, "ЧГ=0") + "C2").ТекущаяОбласть.Текст);
// Далее следует логика обработки...
КонецЦикла;
Чтобы определить, является ли запись группой или элементом, и кто ее «родитель», необходимо разбить код на составляющие по разделителю (точке). В современных версиях платформы 8.3 для этого используется функция СтрРазделить. Однако, если вы работаете на более старых релизах, нам потребуется вспомогательная функция.
Рассмотрим универсальную функцию разложения строки в массив, которую можно использовать в любом модуле:
Функция РазложитьСтрокуВМассивПодстрок(Знач Строка, Знач Разделитель = ".") Экспорт
Результат = Новый Массив;
Позиция = Найти(Строка, Разделитель);
Пока Позиция > 0 Цикл
Подстрока = Лев(Строка, Позиция - 1);
Если Не ПустаяСтрока(Подстрока) Тогда
Результат.Добавить(Подстрока);
КонецЕсли;
Строка = Сред(Строка, Позиция + СтрДлина(Разделитель));
Позиция = Найти(Строка, Разделитель);
КонецЦикла;
Если Не ПустаяСтрока(Строка) Тогда
Результат.Добавить(Строка);
КонецЕсли;
Возврат Результат;
КонецФункции
Важный нюанс: В некоторых классификаторах иерархия определяется не только точками, но и длиной строки. Например, код 01 может быть группой, а 01.1 — ее подгруппой. Проанализируем логику: количество элементов в массиве после деления по точке подскажет нам уровень вложенности.
Теперь объединим чтение данных и создание объектов. Нам нужно проверять наличие родительской группы в базе. Чтобы не замедлять работу постоянным поиском через НайтиПоКоду, рекомендуется использовать кэширование или структуру соответствия, но для простоты разберем базовый надежный алгоритм.
Рассмотрим логику создания объекта:
// Внутри цикла по строкам Excel
МассивЧастей = РазложитьСтрокуВМассивПодстрок(КодИзФайла, ".");
УровеньВложенности = МассивЧастей.Количество();
// Определяем, группа это или конечный элемент
// Например, если точек в коде меньше 3 — это группы
ЭтоГруппа = ?(УровеньВложенности < 4, Истина, Ложь);
СправочникМенеджер = Справочники.СВ_КлассификаторПродукции;
// Ищем, не создан ли уже такой элемент
СуществующаяСсылка = СправочникМенеджер.НайтиПоКоду(КодИзФайла);
Если СуществующаяСсылка.Пустая() Тогда
Если ЭтоГруппа Тогда
НовыйОбъект = СправочникМенеджер.СоздатьГруппу();
Иначе
НовыйОбъект = СправочникМенеджер.СоздатьЭлемент();
КонецЕсли;
НовыйОбъект.Код = КодИзФайла;
Иначе
НовыйОбъект = СуществующаяСсылка.ПолучитьОбъект();
КонецЕсли;
НовыйОбъект.Наименование = НаименованиеИзФайла;
// Поиск родителя (усекаем код до предыдущего уровня)
Если УровеньВложенности > 1 Тогда
// Пытаемся найти родительский код.
// Если код "01.11.12", то родитель имеет код "01.11"
ПозицияПоследнейТочки = НайтиРеверс(КодИзФайла, "."); // Самописная функция или Лев() до точки
КодРодителя = Лев(КодИзФайла, СтрДлина(КодИзФайла) - (СтрДлина(МассивЧастей[УровеньВложенности-1]) + 1));
РодительСсылка = СправочникМенеджер.НайтиПоКоду(КодРодителя);
Если Не РодительСсылка.Пустая() Тогда
НовыйОбъект.Родитель = РодительСсылка;
КонецЕсли;
КонецЕсли;
НовыйОбъект.Записать();
Проанализируем несколько критически важных моментов, которые помогут сделать загрузку профессиональной:
НачатьТранзакцию() перед началом цикла и ЗафиксироватьТранзакцию() в конце. Это ускорит загрузку в десятки раз.СоздатьГруппу() вызовет ошибку, и все записи нужно будет создавать через СоздатьЭлемент().СокрЛП() для кодов и наименований. Лишний пробел в конце кода в Excel может привести к тому, что 1С не найдет родителя по «визуально похожему» коду.Разберем ситуацию с буквенными префиксами (например, код А.01). В этом случае логика разбиения по точке остается прежней, но первым элементом массива будет буква. Если ваша конфигурация подразумевает, что А — это корень, убедитесь, что в справочнике разрешены коды произвольной длины и типа «Строка».
Выясним причину возможных ошибок при записи: если система сообщает «Родитель не может быть элементом», значит, вы пытаетесь назначить родителем объект, который был создан методом СоздатьЭлемент(). Для иерархических справочников «Группы и элементы» родителем всегда должна выступать именно Группа.
Таким образом, мы построили надежный механизм, который анализирует структуру кода Excel-файла, определяет иерархическую соподчиненность и корректно переносит данные в 1С, сохраняя структуру классификатора. При необходимости можно воспользоваться готовой обработкой для загрузки данных в иерархические справочники 1С.