При переносе данных справочника номенклатуры из одной информационной базы 1С в другую с использованием текстового файла (CSV/TXT) начинающие разработчики часто сталкиваются со следующей проблемой: примитивные типы данных (код, наименование, артикул) переносятся корректно, а реквизиты ссылочного типа (единица измерения, вид номенклатуры, ставка НДС, группа) остаются пустыми.
В этой статье мы разберем причину возникновения такой ситуации, проанализируем разницу между типами данных в 1С, рассмотрим пошаговые примеры кода выгрузки и загрузки элементов, а также обсудим вопросы оптимизации при работе с большими объемами данных.
Для понимания логики работы алгоритмов загрузки важно четко разделять типы данных реквизитов объектов 1С:
Строка, Число, Булево, Дата) — хранятся непосредственно в поле таблицы объекта. При считывании текста из файла значение текстовой строки можно сразу напрямую присвоить реквизиту Код или Наименование.Справочники.БазовыеЕдиницыИзмерения, Справочники.ВидыНоменклатуры) или на значение перечисления (Перечисления.СтавкиНДС).Если мы попытаемся присвоить ссылочному реквизиту текстовую строку из файла (например, Спр.ВидНоменклатуры = "Товар"), платформа либо проигнорирует запись, либо выдаст ошибку несоответствия типов. Чтобы реквизит заполнялся корректно, нам необходимо сначала найти объект в базе по коду или наименованию (или создать новый), получить его ссылку и только потом записать ее в реквизит номенклатуры.
Рассмотрим стандартный механизм текстовой выгрузки элементов справочника номенклатуры. Формируем текстовый документ, где значение каждого реквизита разделяется специальным символом, например точка с запятой (;).
Пример кода выгрузки справочника в текстовый файл:
Процедура КнопкаВыгрузитьНажатие(Кнопка)
Текст = Новый ТекстовыйДокумент;
Выборка = Справочники.Номенклатура.Выбрать();
Пока Выборка.Следующий() Цикл
// Формируем строку с разделителями
СтрокаДанных = Выборка.Код + ";" +
Выборка.Наименование + ";" +
Выборка.Родитель + ";" +
Выборка.ВидНоменклатуры + ";" +
Выборка.БазоваяЕдиницаИзмерения + ";" +
Выборка.СтавкаНДС;
Текст.ДобавитьСтроку(СтрокаДанных);
КонецЦикла;
Текст.Записать("d:\test\test.txt");
КонецПроцедуры
При загрузке мы читаем файл построчно, разбиваем каждую строку в массив подстрок с помощью функции разбора и обрабатываем каждый реквизит в зависимости от его типа.
Разберем способы сопоставления ссылок для разных типов реквизитов:
НайтиПоНаименованию() или НайтиПоКоду().Перечисления.СтавкиНДС.Функция для определения значения перечисления ставки НДС:
Функция НайтиСтавкуНДСПоСтроке(ВходящаяСтрока)
ТекстСтроки = НРег(СокрЛП(ВходящаяСтрока));
Если Найти(ТекстСтроки, "без") > 0 Тогда
Возврат Перечисления.СтавкиНДС.БезНДС;
ИначеЕсли Найти(ТекстСтроки, "18") > 0 Тогда
Возврат Перечисления.СтавкиНДС.НДС18;
ИначеЕсли Найти(ТекстСтроки, "10") > 0 Тогда
Возврат Перечисления.СтавкиНДС.НДС10;
Иначе
Возврат Перечисления.СтавкиНДС.НДС0;
КонецЕсли;
КонецФункции
Теперь объединим эти шаги в итоговую процедуру загрузки номенклатуры:
Процедура ЗагрузитьНоменклатуруИзФайла()
Текст = Новый ТекстовыйДокумент;
Текст.Прочитать("d:\test\test.txt");
Для НомерСтроки = 1 По Текст.КоличествоСтрок() Цикл
Стр = Текст.ПолучитьСтроку(НомерСтроки);
МассивСтрок = РазложитьСтрокуВМассивПодстрок(Стр, ";");
Спр = Справочники.Номенклатура.СоздатьЭлемент();
// Заполнение примитивных реквизитов
Спр.Код = МассивСтрок[0];
Спр.Наименование = МассивСтрок[1];
// Поиск ссылок в соответствующих справочниках
Спр.ВидНоменклатуры = Справочники.ВидыНоменклатуры.НайтиПоНаименованию(МассивСтрок[3]);
Спр.ЕдиницаИзмерения = Справочники.БазовыеЕдиницыИзмерения.НайтиПоНаименованию(МассивСтрок[4]);
// Заполнение значения перечисления
Спр.СтавкаНДС = НайтиСтавкуНДСПоСтроке(МассивСтрок[5]);
Спр.Записать();
КонецЦикла;
КонецПроцедуры
Если в целевой базе необходимо сохранять иерархическую структуру (папки и группы), простая запись текстового имени группы в реквизит Родитель работать не будет. Требуется сначала найти или создать группу справочника.
Для работы с группами применяется отдельный метод создания — СоздатьГруппу():
Функция ПолучитьСсылкуНаГруппу(НаименованиеГруппы, ТекущийРодитель = Неопределено)
НайденнаяГруппа = Справочники.Номенклатура.НайтиПоНаименованию(НаименованиеГруппы, Истина);
Если НайденнаяГруппа.Пустая() Тогда
ОбъектГруппа = Справочники.Номенклатура.СоздатьГруппу();
ОбъектГруппа.Наименование = СокрЛП(НаименованиеГруппы);
Если ТекущийРодитель <> Неопределено Тогда
ОбъектГруппа.Родитель = ТекущийРодитель;
КонецЕсли;
ОбъектГруппа.Записать();
Возврат ОбъектГруппа.Ссылка;
Иначе
Возврат НайденнаяГруппа;
КонецЕсли;
КонецФункции
Если вам требуется загрузить масштабную базу (например, от 10 000 до 200 000 позиций), стандартный поэлементный подход вызовет существенное падение производительности. Для ускорения процесса рекомендуется соблюдать следующие правила:
НачатьТранзакцию() и ЗафиксироватьТранзакцию(). Это ускорит загрузку в десятки раз.Спр.ОбменДанными.Загрузка = Истина. Это отключает лишние тяжелые проверки модуля объекта и бизнес-логику конфигурации.НайтиПоНаименованию() на каждой итерации цикла отправляет запросы к СУБД. Рекомендуется один раз перед запуском цикла сопоставить виды номенклатуры и единицы измерения, сохранив ссылки в объект Соответствие или Структура.