Почему при загрузке номенклатуры из Excel возникают дубли и как правильно организовать импорт данных

Программист 1С v8.3 (Обычные формы)
← На главную

При разработке механизмов импорта данных из внешних файлов, таких как Excel, программисты часто сталкиваются с проблемой появления дубликатов записей в справочниках. Ситуация усугубляется, когда код ведет себя непредсказуемо: поиск существующей номенклатуры не срабатывает, а счетчик обрабатываемых строк внезапно «сбрасывается» или начинает идти по второму кругу. Разберем подробно, почему это происходит и как выстроить надежную архитектуру загрузки.

Анализ основной ошибки: использование события ПриВыводеСтроки

Проанализируем представленный в теме код. Главная методическая ошибка заключается в том, что процедура создания элементов справочника Номенклатура помещена в обработчик события ДанныеЛистаПриВыводеСтроки. Рассмотрим подробнее, к чему это приводит:

  1. Множественное срабатывание: Событие ПриВыводеСтроки предназначено исключительно для визуального оформления табличного поля (например, изменения цвета текста или подстановки картинки в ячейку). Оно вызывается системой 1С каждый раз, когда строка отрисовывается на экране. Это происходит при прокрутке списка, при изменении размера окна, при сворачивании и даже если вы просто перекрыли окно 1С другим приложением и вернулись обратно.
  2. Рекурсивное обновление: В коде автора присутствует вызов метода ЭлементыФормы.ДанныеЛиста.ОбновитьСтроки(). Это провоцирует принудительную перерисовку всех видимых строк, что снова вызывает событие ПриВыводеСтроки. В итоге программа попадает в бесконечный цикл или выполняет одну и ту же операцию записи данных десятки раз для одной строки.
  3. Виртуализация данных: Табличное поле в 1С (особенно в обычных формах) не хранит в памяти состояние отрисовки всех строк сразу. Оно отрисовывает только те 30–40 строк, которые видны пользователю. Как только вы прокручиваете список, «старые» строки выгружаются из визуального интерфейса, а «новые» отрисовываются заново, повторно запуская ваш код загрузки. Именно поэтому номер строки в параметре ДанныеСтроки «прыгает» или возвращается к началу.

Важное правило: Никогда не используйте события отрисовки интерфейса (ПриВыводеСтроки, ПриПолученииДанных) для модификации данных в базе данных (создание, изменение, удаление объектов).

Проблема поиска дублей и невидимые символы

Часто программисты жалуются, что метод НайтиПоНаименованию() не находит элемент, хотя визуально строки в Excel и в 1С идентичны. Выясним причину этой ситуации. Текст, получаемый из ячеек ТабличногоДокумента, может содержать невидимые спецсимволы:

Для 1С строки "Товар" и "Товар " — это разные значения. Функция СокрЛП() удаляет только стандартные пробелы. Посмотрим на пример кода, который делает поиск более надежным:


// Очистка строки от специфических символов Excel
ТекНаименование = СокрЛП(Наименование);
ТекНаименование = СтрЗаменить(ТекНаименование, Символы.НПП, ""); // Убираем неразрывный пробел
ТекНаименование = СтрЗаменить(ТекНаименование, Символы.ПС, "");  // Убираем переносы строк

ИмеющеесяНаименование = Справочники.Номенклатура.НайтиПоНаименованию(ТекНаименование, Истина);
Если ИмеющеесяНаименование.Пустая() Тогда
    // Создаем новый элемент
Иначе
    // Элемент найден, пропускаем или обновляем
КонецЕсли;

Обратите внимание на второй параметр функции НайтиПоНаименованию. Использование Истина гарантирует поиск по полному соответствию наименования, что критично для предотвращения дублей.

Оптимизация работы с Табличным Документом

В исходном коде автора внутри цикла (события) каждый раз вызывается чтение файла с диска: ТабДок.Прочитать(ЭтотОбъект.ИмяФайла). Если в файле 1000 строк, и каждая отрисовывается по 5 раз, система прочитает файл 5000 раз. Это неизбежно приведет к зависанию системы.

Разберем правильный алгоритм загрузки:

  1. Создаем отдельную процедуру, которая запускается по кнопке «Загрузить».
  2. Один раз считываем файл Excel в объект ТабличныйДокумент или таблицу значений.
  3. Запускаем цикл Для каждого по строкам полученных данных.
  4. Внутри цикла выполняем поиск, создание объектов и запись картинок.

Правильная реализация загрузки (пример кода)

Рассмотрим, как должен выглядеть надежный код загрузки номенклатуры с картинками. Мы откажемся от событий интерфейса и перейдем к линейной обработке данных.


Процедура ВыполнитьЗагрузкуПоКнопке()
    
    ТабДок = Новый ТабличныйДокумент;
    Попытка
        ТабДок.Прочитать(Объект.ПутьКФайлу, СпособЧтенияЗначенийТабличногоДокумента.Значение);
    Исключение
        Сообщить("Ошибка чтения файла: " + ОписаниеОшибки());
        Возврат;
    КонецПопытки;
    
    // Определяем границы загрузки
    ВсегоСтрок = ТабДок.ВысотаТаблицы;
    Для НомерСтроки = НомерПервойСтроки По ВсегоСтрок Цикл
        
        НаименованиеИзФайла = ТабДок.ПолучитьОбласть("R" + Формат(НомерСтроки, "ЧГ=0") + "C" + НомерКолонкиНаименования).ТекущаяОбласть.Текст;
        НаименованиеЧистое = СокрЛП(НаименованиеИзФайла);
        
        // Поиск существующей номенклатуры
        СсылкаНоменклатура = Справочники.Номенклатура.НайтиПоНаименованию(НаименованиеЧистое, Истина);
        
        Если СсылкаНоменклатура.Пустая() Тогда
            НовыйОбъект = Справочники.Номенклатура.СоздатьЭлемент();
            НовыйОбъект.Наименование = НаименованиеЧистое;
            // Заполнение остальных реквизитов...
            НовыйОбъект.Записать();
            СсылкаНоменклатура = НовыйОбъект.Ссылка;
            Сообщить("Создан новый элемент: " + НаименованиеЧистое);
        КонецЕсли;
        
        // Обработка картинок
        ОбластьКартинки = ТабДок.Область(НомерСтроки, НомерКолонкиКартинки);
        Для Каждого Рисунок Из ТабДок.Рисунки Цикл
            // Проверяем, находится ли рисунок в границах нужной ячейки
            Если Рисунок.Расположение.Лево = ОбластьКартинки.Лево И Рисунок.Расположение.Верх = ОбластьКартинки.Верх Тогда
                ЗагрузитьКартинкуВНоменклатуру(СсылкаНоменклатура, Рисунок.Картинка);
                Прервать;
            КонецЕсли;
        КонецЦикла;
        
    КонецЦикла;
    
КонецПроцедуры

Работа с картинками и хранилищем значений

При загрузке изображений важно помнить, что в типовых конфигурациях (таких как УПП, КА 1.1 или старые версии БП) картинки хранятся в отдельном справочнике ХранилищеДополнительнойИнформации. Чтобы связать картинку с номенклатурой, нужно выполнить несколько шагов:

Проанализируем ситуацию с ОсновнымИзображением. Если вы записываете картинку при каждом «выводе строки», вы не только создаете дубли номенклатуры, но и забиваете базу данных гигабайтами одинаковых файлов изображений, так как для каждой отрисовки создается новый объект в справочнике хранилища.

Итоги и рекомендации

Подведем итог нашему разбору. Чтобы загрузка номенклатуры работала корректно и без дублей:

  1. Выносите логику из формы: Код загрузки должен находиться в модуле объекта обработки или в отдельной процедуре, запускаемой по команде пользователя, а не системой автоматически.
  2. Используйте точный поиск: Всегда применяйте НайтиПоНаименованию с параметром ТочноеСоответствие = Истина и предварительной очисткой строки от мусорных символов.
  3. Оптимизируйте чтение данных: Считывайте файл Excel один раз в начале процесса.
  4. Контролируйте уникальность: Если номенклатура ведется по артикулам, лучше выполнять поиск именно по артикулу (НайтиПоРеквизиту("Артикул", ...)), так как это более надежный идентификатор, чем наименование.

Соблюдение этих правил позволит вам создавать быстрые и надежные механизмы импорта, которые не портят данные в информационной базе.

← На главную