При заполнении новой базы данных или реорганизации текущей структуры справочника Номенклатура часто возникает необходимость быстро создать десятки или сотни новых папок (групп). Делать это вручную — рутинный и трудоемкий процесс. Гораздо эффективнее использовать автоматическую загрузку из подготовленного текстового файла формата TXT или CSV. Рассмотрим, как правильно реализовать этот механизм в 1С:Предприятии 8, разберем типичные ошибки новичков и предложим оптимальный и безопасный готовый код.
На форумах часто предлагают простые решения, которые при детальном рассмотрении содержат критические ошибки, способные нарушить структуру данных. Проанализируем популярный вариант кода, предложенный в обсуждении (Сообщение 8), и выясним, почему его нельзя использовать в исходном виде.
В предложенном коде строка файла считывается и преобразуется следующим образом:
Стр = СтрЗаменить(файл.ПолучитьСтроку(Сч), "|", Символы.ПС);
НаименованиеНоменклатуры = СокрЛП(СтрПолучитьСтроку(Стр, 1));
НаименованиеРодителя = СокрЛП(СтрПолучитьСтроку(Стр, 2));
После этого разработчик пытается записать наименование новой группы:
НоваяГруппа.Наименование = СокрЛП(Стр);
В чем заключается ошибка? Переменная Стр — это вся исходная строка файла, в которой символ разделителя | был заменен на перевод строки (Символы.ПС). Записывая в наименование группы переменную Стр, мы получаем в справочнике папки с многострочными именами вида:
"НаименованиеГруппы
НаименованиеРодителя"
Вместо этого свойству Наименование необходимо присваивать очищенную переменную НаименованиеНоменклатуры.
Вторая проблема — риск путаницы элементов и групп при поиске. Функция НайтиПоНаименованию ищет совпадения по всему справочнику. Если в системе уже заведен обычный товар (элемент) с точно таким же названием, какое мы хотим дать новой группе, 1С вернет ссылку на этот товар. Попытка записать его как группу или назначить родителем вызовет критическую ошибку выполнения. При поиске всегда нужно проверять свойство объекта ЭтоГруппа.
Третья проблема — дублирование при наличии одинаковых имен в разных ветках. Если папка "Расходные материалы" должна быть создана внутри группы "Офис" и внутри группы "Производство", то простой поиск по наименованию без указания родителя вернет первую попавшуюся папку. Поиск подгрупп должен выполняться строго с ограничением по родительской ветке.
При работе в управляемом приложении (Тонкий клиент) мы сталкиваемся с системным ограничением: на клиенте невозможно использовать метод СоздатьГруппу() менеджера справочника. Все действия по модификации данных должны выполняться на стороне сервера (в процедурах с директивой &НаСервере).
Однако, если база данных работает в клиент-серверном режиме (на SQL), сервер 1С физически запущен на другом компьютере. Попытка прочитать файл по локальному пути вроде "C:\ТекстСНовымиГруппами.txt" на сервере завершится ошибкой, так как на сервере этого файла попросту нет.
Для правильной работы алгоритма мы должны:
Рассмотрим готовое решение, полностью готовое к интеграции в любую обработку на управляемых формах. Этот код лишен недостатков прошлых версий, использует безопасные методы поиска и современную функцию СтрРазделить вместо устаревших манипуляций со строками.
Создадим на форме обработки команду и привяжем к ней следующий код:
&НаКлиенте
Процедура ЗагрузитьГруппы(Команда)
// Настраиваем диалог выбора файла на клиенте
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Диалог.Фильтр = "Текстовые файлы (*.txt)|*.txt";
Диалог.Заголовок = "Выберите текстовый файл со структурой групп";
Если Диалог.Выбрать() Тогда
ОписаниеФайла = Новый Файл(Диалог.ПолноеИмяФайла);
Если ОписаниеФайла.Существует() Тогда
// Читаем локальный файл в двоичные данные
ДвоичныеДанные = Новый ДвоичныеДанные(Диалог.ПолноеИмяФайла);
// Помещаем во временное хранилище для передачи на сервер
АдресХранилища = ПоместитьВоВременноеХранилище(ДвоичныеДанные, ЭтаФорма.УникальныйИдентификатор);
// Вызываем серверный обработчик загрузки
ЗагрузитьГруппыНаСервере(АдресХранилища);
ПоказатьПредупреждение(, "Процесс загрузки групп успешно завершен!");
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Теперь реализуем серверную часть логики, которая выполнит чтение данных, проверку на дубликаты и безопасное создание иерархии справочника:
&НаСервере
Процедура ЗагрузитьГруппыНаСервере(АдресХранилища)
// Получаем данные из хранилища и сохраняем во временный файл на сервере
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресХранилища);
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("txt");
ДвоичныеДанные.Записать(ИмяВременногоФайла);
Файл = Новый ТекстовыйДокумент;
Попытка
Файл.Прочитать(ИмяВременногоФайла, КодировкаТекста.UTF8);
Исключение
УдалитьФайлы(ИмяВременногоФайла);
ВызватьИсключение "Не удалось прочитать переданный файл на сервере!";
КонецПопытки;
КоличествоСтрок = Файл.КоличествоСтрок();
Для Сч = 1 По КоличествоСтрок Цикл
ОбработкаПрерыванияПользователя();
СтрокаФайла = Файл.ПолучитьСтроку(Сч);
// Пропускаем пустые строки
Если ПустаяСтрока(СокрЛП(СтрокаФайла)) Тогда
Продолжить;
КонецЕсли;
// Разбиваем строку по разделителю "|"
МассивКолонок = СтрРазделить(СтрокаФайла, "|", Ложь);
Если МассивКолонок.Количество() = 0 Тогда
Продолжить;
КонецЕсли;
ИмяГруппы = СокрЛП(МассивКолонок[0]);
// Проверяем наличие колонки с родителем
ИмяРодителя = "";
Если МассивКолонок.Количество() > 1 Тогда
ИмяРодителя = СокрЛП(МассивКолонок[1]);
КонецЕсли;
СсылкаРодитель = Справочники.Номенклатура.ПустаяСсылка();
// 1. Поиск или создание родительской папки верхнего уровня
Если Не ПустаяСтрока(ИмяРодителя) Тогда
НайденныйРодитель = Справочники.Номенклатура.НайтиПоНаименованию(ИмяРодителя, Истина);
Если ЗначениеЗаполнено(НайденныйРодитель) Тогда
// Убедимся, что найденный объект является именно ГРУППОЙ, а не товаром
Если НайденныйРодитель.ЭтоГруппа Тогда
СсылкаРодитель = НайденныйРодитель;
КонецЕсли;
Иначе
// Если родитель не найден в базе, создаем его на самом верхнем уровне
НовыйРодитель = Справочники.Номенклатура.СоздатьГруппу();
НовыйРодитель.Наименование = ИмяРодителя;
НовыйРодитель.УстановитьНовыйКод();
Попытка
НовыйРодитель.Записать();
СсылкаРодитель = НовыйРодитель.Ссылка;
Исключение
ЗаписьЖурналаРегистрации("ИмпортНоменклатуры", УровеньЖурналаРегистрации.Ошибка,,,
"Не удалось записать группу-родителя: " + ИмяРодителя + ". Описание: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецЕсли;
// 2. Поиск создаваемой группы внутри определенного родителя во избежание дублей
ЭлементПоиска = Справочники.Номенклатура.НайтиПоНаименованию(ИмяГруппы, Истина, СсылкаРодитель);
// Если группа с таким именем уже существует внутри этого родителя — пропускаем создание
Если ЗначениеЗаполнено(ЭлементПоиска) И ЭлементПоиска.ЭтоГруппа Тогда
Продолжить;
КонецЕсли;
// 3. Создание новой группы
НоваяГруппа = Справочники.Номенклатура.СоздатьГруппу();
НоваяГруппа.Наименование = ИмяГруппы;
НоваяГруппа.Родитель = СсылкаРодитель;
НоваяГруппа.УстановитьНовыйКод();
Попытка
НоваяГруппа.Записать();
Исключение
ЗаписьЖурналаРегистрации("ИмпортНоменклатуры", УровеньЖурналаРегистрации.Ошибка,,,
"Не удалось записать группу: " + ИмяГруппы + ". Описание: " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
// Обязательно удаляем временный файл с диска сервера после завершения операции
Попытка
УдалитьФайлы(ИмяВременногоФайла);
Исключение
// Ошибка удаления временного файла не должна блокировать работу пользователя
КонецПопытки;
КонецПроцедуры
Для корректной работы данного скрипта ваш текстовый файл C:\ТекстСНовымиГруппами.txt должен содержать строки следующего формата:
Обувь|Одежда и обувь
Строительные материалы|Хозтовары
Офисная мебель|Мебель
В этом примере первая часть строки до знака | — это создаваемая папка, а вторая часть — имя родительской папки верхнего уровня, в которую она должна быть помещена.
Проанализируем ситуацию: если вам нужно выполнить разовую загрузку структуры из 100 папок, писать собственный программный код вовсе не обязательно. Современные типовые конфигурации (1С:Бухгалтерия 3.0, 1С:Управление торговлей 11, 1С:УНФ) предоставляют мощный встроенный инструмент импорта данных из файлов Excel и табличных документов.
Выполним загрузку по шагам с помощью типового функционала:
Для старых конфигураций на обычных формах (таких как Управление торговлей 10.3 или УПП 1.3) аналогичную задачу решает универсальная внешняя обработка "Загрузка данных из табличного документа" (поставляется фирмой 1С на дисках ИТС). Она позволяет сопоставлять поля справочника с колонками Excel, создавать группы по уровням иерархии и настраивать режим поиска дубликатов по наименованию или коду.