Загрузка данных из внешних файлов (Excel, CSV, XML) — это одна из самых частых задач, с которыми сталкивается разработчик 1С. Однако начинающие программисты часто совершают ошибки в логике поиска существующих объектов, что приводит к появлению «мусора» в базе данных или пропуску важных позиций. Рассмотрим типичную проблему: при загрузке товаров со схожими названиями (например, «Телевизор» и «Телевизор Samsung») программа ошибочно считает их одним и тем же объектом. Проанализируем причины этой ситуации и разберем правильные способы реализации поиска и создания номенклатуры.
В исходном коде автора для проверки наличия товара в базе использовался следующий запрос:
Запрос.Текст = "ВЫБРАТЬ
| Номенклатура.Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Наименование ПОДОБНО &Номенклатура";
Запрос.УстановитьПараметр("Номенклатура","%" + ПроверитьНоменклатуры + "%");
Разберем, почему такая конструкция работает некорректно для задачи точного поиска. Оператор ПОДОБНО (LIKE в SQL) предназначен для поиска по маске. Использование спецсимволов % (процент) означает, что внутри названия может быть любая последовательность символов.
Рассмотрим пример: в файле Excel у нас есть строка «Телевизор». В базе уже существует товар «Телевизор Samsung». Когда выполняется запрос с условием ПОДОБНО %Телевизор%, система находит «Телевизор Samsung», так как это наименование содержит искомое слово. В результате логика программы Если НЕ Выборка.Следующий() Тогда срабатывает так, будто товар уже есть в базе, и новый элемент «Телевизор» не создается.
Для решения этой проблемы необходимо использовать оператор строгого равенства = и убрать лишние символы процентов. Это гарантирует, что система найдет только тот товар, название которого символ в символ совпадает с данными из Excel.
Оптимальный вариант запроса для проверки существования номенклатуры выглядит следующим образом:
ПроверитьНоменклатуры = СокрЛП(ExcelПриложение.Cells(Строка, НоменклатураКол).Value);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ССЫЛКА
|ИЗ Справочник.Номенклатура
|ГДЕ Наименование = &Наименование";
Запрос.УстановитьПараметр("Наименование", ПроверитьНоменклатуры);
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
// Здесь пишем код создания новой номенклатуры
НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
// ... заполнение реквизитов
НовыйЭлемент.Записать();
ТоварСсылка = НовыйЭлемент.Ссылка;
Иначе
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
ТоварСсылка = Выборка.Ссылка;
КонецЕсли;
Обратите внимание, что мы используем функцию СокрЛП(). Это критически важно, так как лишние пробелы в начале или конце строки из Excel (которые часто не видны глазу) сделают строки «Телевизор » и «Телевизор» разными для базы данных.
В тексте обработки автора также встречается метод НайтиПоНаименованию(). У этого метода есть важный второй параметр — Истина (ПоискПоПолномуСовпадению). Если его не указать или установить в Ложь, 1С может вернуть первый попавшийся элемент, начинающийся с искомой строки.
Проанализируем правильный вызов метода:
СсылкаНаТовар = Справочники.Номенклатура.НайтиПоНаименованию(НаименованиеИзФайла, Истина);
Параметр Истина гарантирует, что поиск будет вестись по полному совпадению всей строки, а не по начальным символам.
Рассмотрим ситуацию с точки зрения производительности. Выполнение запроса внутри цикла Для Строка = 2 По ExcelПоследняяСтрока — это серьезная архитектурная ошибка. Если в файле 5000 строк, программа сделает 5000 обращений к базе данных, что крайне медленно.
Выясним, как сделать загрузку быстрее. Правильный алгоритм действий должен быть таким:
Посмотрим на пример реализации такого подхода:
// 1. Читаем Excel в ТаблицуЗначений "ДанныеФайла"
// ... (код загрузки в ТЗ)
// 2. Получаем массив уникальных наименований
МассивНаименований = ДанныеФайла.ВыгрузитьКолонку("Наименование");
ОбщегоНазначения.УдалитьПовторяющиесяЭлементыМассива(МассивНаименований);
// 3. Ищем существующие в базе товары одним запросом
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ Наименование, Ссылка
|ИЗ Справочник.Номенклатура
|ГДЕ Наименование В (&МассивНаименований)";
Запрос.УстановитьПараметр("МассивНаименований", МассивНаименований);
// Сохраняем результат в Соответствие для быстрого поиска в памяти
НайденныеТовары = Новый Соответствие;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
НайденныеТовары.Вставить(Выборка.Наименование, Выборка.Ссылка);
КонецЦикла;
// 4. Основной цикл создания/обработки
Для Каждого СтрДанных Из ДанныеФайла Цикл
ТоварСсылка = НайденныеТовары.Получить(СтрДанных.Наименование);
Если ТоварСсылка = Неопределено Тогда
// Создаем новый элемент, так как в базе его нет
// После записи добавляем его в Соответствие, чтобы не создать дубль,
// если в Excel товар встретится повторно
// ...
КонецЕсли;
КонецЦикла;
При работе с конфигурацией «Управление торговлей 10.3» (на обычных формах) важно помнить, что Справочник.Номенклатура имеет ряд специфических особенностей. Простого заполнения наименования недостаточно для полноценного учета.
Разберем по шагам, что нужно заполнить обязательно:
ЕдиницыИзмерения. После записи новой номенклатуры нужно программно создать хотя бы одну единицу измерения и записать её в этот реквизит.Пример корректного создания элемента с единицами измерения:
НовыйТовар = Справочники.Номенклатура.СоздатьЭлемент();
НовыйТовар.Наименование = "Телевизор";
НовыйТовар.ВидНоменклатуры = Справочники.ВидыНоменклатуры.НайтиПоНаименованию("Товар");
НовыйТовар.БазоваяЕдиницаИзмерения = Справочники.КлассификаторЕдиницИзмерения.НайтиПоКоду("796"); // Штуки
НовыйТовар.Записать(); // Сначала записываем, чтобы получить ссылку
// Теперь создаем единицу хранения остатков
НоваяЕдиница = Справочники.ЕдиницыИзмерения.СоздатьЭлемент();
НоваяЕдиница.Владелец = НовыйТовар.Ссылка;
НоваяЕдиница.Наименование = "шт";
НоваяЕдиница.ЕдиницаПоКлассификатору = НовыйТовар.БазоваяЕдиницаИзмерения;
НоваяЕдиница.Коэффициент = 1;
НоваяЕдиница.Записать();
// Указываем единицу в карточке товара
НовыйТоварОбъект = НовыйТовар.Ссылка.ПолучитьОбъект();
НовыйТоварОбъект.ЕдиницаХраненияОстатков = НоваяЕдиница.Ссылка;
НовыйТоварОбъект.Записать();
Проанализируем техническую сторону работы с COM-объектом. Программисты часто забывают корректно закрывать процесс Excel, что приводит к «зависанию» памяти на сервере или компьютере пользователя.
Всегда используйте блок Попытка...Исключение и в блоке завершения или после цикла обязательно вызывайте ExcelПриложение.Quit(). Также рекомендуется обнулять переменную: ExcelПриложение = Неопределено;, чтобы сборщик мусора 1С быстрее освободил ресурсы. Для повышения надежности можно использовать чтение через ADO или сохранение файла в формат CSV/XML, что работает значительно быстрее и стабильнее, чем через COM.
Резюме: Чтобы загрузка работала корректно, используйте строгое сравнение в запросах (= вместо ПОДОБНО), всегда применяйте СокрЛП(), не забывайте про обязательные реквизиты в УТ 10.3 и старайтесь избегать выполнения запросов внутри циклов для обеспечения высокой скорости работы программы.