Как исправить ошибку «Значение поля Номер не уникально» при загрузке данных из Excel в 1С

Программист 1С v8.3 (Обычные формы) Управленческий учет IT и автоматизация бизнеса
← На главную

Загрузка данных из внешних файлов Excel — одна из самых частых задач в практике программиста 1С. Однако при написании кода импорта легко допустить логические ошибки, которые приводят к неожиданному поведению системы. Типичный пример такой проблемы — остановка импорта с ошибкой «Значение поля "Номер" не уникально», даже если в самом файле Excel все номера уникальны и дублей нет.

В этой статье мы подробно разберем, почему возникает эта ошибка, проанализируем уязвимости в кодовой базе, разберем правильный вариант работы с COM-объектами и рассмотрим современную, более быструю и кроссплатформенную альтернативу загрузки через встроенный ТабличныйДокумент.

Анализ проблемы: почему возникает ошибка уникальности?

Выясним первопричину ошибки на примере кода, предоставленного автором темы. Разработчик пытается заполнить массив структур данными из листа Excel, а затем в цикле создать документы ПриходныйКассовыйОрдер (ПКО). Но при записи второго документа система выдает ошибку уникальности номера.

Проанализируем структуру исходного кода и найдем критические ошибки логики:

  1. Вынос чтения данных за пределы цикла обхода строк.

    Обратите внимание на строки кода автора:

    
    Номер = СокрЛП(Лист.Cells(Сч2, 2).Value);
    ДатаДок = СокрЛП(Лист.Cells(Сч2, 3).Value);
    // ... и так далее для остальных реквизитов
    

    Этот блок находится вне цикла обхода строк Excel. Переменная Сч2 к моменту выполнения этого блока уже завершила свой внутренний цикл по колонкам и содержит фиксированное число (индекс последней колонки + 1). В результате программа считывает данные только один раз из одной случайной ячейки.

  2. Запись статических данных в цикле создания документов.

    Поскольку чтение произошло один раз, переменные Номер, ДатаДокумента, Сумма и другие получили фиксированные значения. Далее в цикле создания документов Для Каждого Элемент Из МассивДанных Цикл программа пытается создать множество документов ПКО, присваивая им одни и те же значения реквизитов. Как только код пытается записать второй документ с тем же номером, платформа 1С блокирует транзакцию из-за нарушения уникальности номера.

  3. Проблема преобразования индекса колонки в строку.

    В коде автора используется выражение "Колонка" + Строка(Сч2). В платформе 1С стандартная функция Строка(Число) при форматировании больших чисел (или при определенных региональных настройках системы) может добавлять неразрывные пробелы в качестве разделителей групп (например, превращая число 1000 в строку "1 000"). Это приводит к тому, что ключи структуры формируются некорректно, и при попытке обратиться к свойству Элемент.Колонка2 система возвращает пустые значения. В результате номер документа записывается как пустая строка, вызывая ошибку дублирования при создании следующего пустого документа.

Вариант 1. Исправление исходного кода на COM-объекте

Рассмотрим, как правильно перестроить логику чтения файла через COM-соединение, исправив все архитектурные ошибки. Мы объединим чтение ячеек и создание документов в один проход, обеспечим корректную обработку типов данных и добавим проверку на существующие в базе документы.

Выполним исправление по шагам:


Эксель = Новый COMОбъект("Excel.Application");
Попытка
    Книга = Эксель.WorkBooks.Open(ИмяФайла);
    Лист = Книга.WorkSheets(1);
Исключение
    Сообщить("Не удалось открыть файл Excel! " + ОписаниеОшибки());
    Эксель.Quit();
    Возврат;
КонецПопытки;

// Получаем координаты заполненной области листа
ВсегоСтрок = Лист.Cells(1,1).SpecialCells(11).Row;

// Запускаем цикл обхода строк со второй строки (пропуская шапку таблицы)
Для Сч = 2 по ВсегоСтрок Цикл
    
    // Считываем значения непосредственно внутри цикла для каждой строки
    НомерСтрока             = СокрЛП(Лист.Cells(Сч, 2).Value);
    ДатаИзExcel             = Лист.Cells(Сч, 3).Value;
    СуммаДок                = Число(Лист.Cells(Сч, 4).Value);
    КассаНаименование       = СокрЛП(Лист.Cells(Сч, 5).Value);
    ОрганизацияНаименование = СокрЛП(Лист.Cells(Сч, 9).Value);
    
    // Если номер строки пустой, пропускаем итерацию
    Если ПустаяСтрока(НомерСтрока) Тогда
        Продолжить;
    КонецЕсли;
    
    // Преобразуем дату. Если в Excel колонка имеет формат Даты, 1С получит ее корректно.
    // Если дата прочитана как строка, выполним безопасное преобразование.
    Если ТипЗнч(ДатаИзExcel) <> Тип("Дата") Тогда
        Попытка
            // Предполагаем стандартный формат даты ДД.ММ.ГГГГ
            ДатаДокумента = Дата(Прав(ДатаИзExcel, 4) + Сред(ДатаИзExcel, 4, 2) + Лев(ДатаИзExcel, 2));
        Исключение
            ДатаДокумента = ТекущаяДата(); // Дефолтное значение при ошибке парсинга
        КонецПопытки;
    Иначе
        ДатаДокумента = ДатаИзExcel;
    КонецЕсли;
    
    // Предотвращаем дублирование: ищем, нет ли уже такого документа в базе
    ПКОСсылка = Документы.ПриходныйКассовыйОрдер.НайтиПоНомеру(НомерСтрока, ДатаДокумента);
    
    Если ПКОСсылка.Пустая() Тогда
        ПКО = Документы.ПриходныйКассовыйОрдер.СоздатьДокумент();
        ПКО.Номер = НомерСтрока; // Заполняем номер только для новых документов
    Иначе
        // Если документ найден, мы можем обновить его или пропустить
        ПКО = ПКОСсылка.ПолучитьОбъект();
    КонецЕсли;
    
    ПКО.Дата = ДатаДокумента;
    ПКО.ХозяйственнаяОперация = Перечисления.ХозяйственныеОперации.ПоступлениеДенежныхСредствИзКассыККМ;
    
    // Поиск ссылочных данных в справочниках по наименованию
    ПКО.Касса = Справочники.Кассы.НайтиПоНаименованию(КассаНаименование);
    ПКО.КассаККМ = Справочники.КассыККМ.НайтиПоНаименованию(КассаНаименование);
    ПКО.Организация = Справочники.Организации.НайтиПоНаименованию(ОрганизацияНаименование);
    
    ПКО.СуммаДокумента = СуммаДок;
    
    // Безопасная запись документа
    Попытка
        ПКО.Записать(РежимЗаписиДокумента.Запись);
    Исключение
        Сообщить("Не удалось записать документ № " + НомерСтрока + " по причине: " + ОписаниеОшибки());
    КонецПопытки;
    
КонецЦикла;

// Обязательно закрываем процесс Excel в памяти
Книга.Close(Ложь);
Эксель.Quit();

Вариант 2. Современный и надежный способ через ТабличныйДокумент

Использование COM-объекта Excel.Application считается устаревшим подходом. Он имеет три критических недостатка:

Проанализируем современную альтернативу — чтение данных с помощью встроенного объекта 1С ТабличныйДокумент. Этот метод работает на любых операционных системах, не требует стороннего ПО и считывает файлы в десятки раз быстрее.

Рассмотрим пример реализации загрузки через ТабличныйДокумент:


ТабДок = Новый ТабличныйДокумент;

Попытка
    // Метод Прочитать автоматически определяет формат XLSX
    ТабДок.Прочитать(ИмяФайла, СпособЧтенияЗначенийТабличногоДокумента.Значение);
Исключение
    Сообщить("Ошибка чтения файла: " + ОписаниеОшибки());
    Возврат;
КонецПопытки;

КоличествоСтрок = ТабДок.ВысотаТаблицы;

Для Шаг = 2 По КоличествоСтрок Цикл
    
    // Считываем текстовые представления и значения ячеек напрямую
    НомерСтрока             = СокрЛП(ТабДок.Область(Шаг, 2).Текст);
    ЗначениеДаты            = ТабДок.Область(Шаг, 3).Значение;
    СуммаДок                = Число(ТабДок.Область(Шаг, 4).Значение);
    КассаНаименование       = СокрЛП(ТабДок.Область(Шаг, 5).Текст);
    ОрганизацияНаименование = СокрЛП(ТабДок.Область(Шаг, 9).Текст);
    
    Если ПустаяСтрока(НомерСтрока) Тогда
        Продолжить;
    КонецЕсли;
    
    // Если ячейка в Excel была отформатирована как дата, платформа считает ее как тип Дата
    Если ТипЗнч(ЗначениеДаты) = Тип("Дата") Тогда
        ДатаДокумента = ЗначениеДаты;
    Иначе
        // Попытка преобразовать строковое представление даты
        ДатаДокумента = ТекущаяДата();
    КонецЕсли;
    
    // Проверка уникальности номера перед созданием нового документа
    ПКОСсылка = Документы.ПриходныйКассовыйОрдер.НайтиПоНомеру(НомерСтрока, ДатаДокумента);
    
    Если ПКОСсылка.Пустая() Тогда
        ПКО = Документы.ПриходныйКассовыйОрдер.СоздатьДокумент();
        ПКО.Номер = НомерСтрока;
    Else
        ПКО = ПКОСсылка.ПолучитьОбъект();
    КонецЕсли;
    
    ПКО.Дата = ДатаДокумента;
    ПКО.ХозяйственнаяОперация = Перечисления.ХозяйственныеОперации.ПоступлениеДенежныхСредствИзКассыККМ;
    
    // Заполнение аналитики сопоставлением по наименованию
    ПКО.Касса = Справочники.Кассы.НайтиПоНаименованию(КассаНаименование);
    ПКО.КассаККМ = Справочники.КассыККМ.НайтиПоНаименованию(КассаНаименование);
    ПКО.Организация = Справочники.Организации.НайтиПоНаименованию(ОрганизацияНаименование);
    ПКО.СуммаДокумента = СуммаДок;
    
    Попытка
        ПКО.Записать(РежимЗаписиДокумента.Запись);
    Исключение
        Сообщить("Строка " + Шаг + ": Не удалось записать документ. Ошибка: " + ОписаниеОшибки());
    КонецПопытки;
    
КонецЦикла;

Методология работы с номерами документов в 1С

Для того чтобы избежать системных сбоев автонумерации при загрузке внешних данных, рекомендуем придерживаться следующих правил:

  1. Проверка существования документа перед записью. Метод НайтиПоНомеру() позволяет определить, загружался ли этот документ ранее. Это исключает дублирование записей при повторном импорте того же файла.
  2. Использование автоматической нумерации. Если сохранение оригинальных номеров из Excel не является строго обязательным требованием учета, лучше вообще не заполнять реквизит Номер вручную. Оставьте его пустым, и при записи документа платформа 1С автоматически сгенерирует правильный уникальный номер. Если требуется задать префикс информационной базы или организации, используйте метод:
    
    // Пример автоматической установки номера с префиксом организации
    ПКО.УстановитьНовыйНомер(ПКО.Организация.Префикс);
    
  3. Контроль за лидирующими нулями. Если вы присваиваете номера из Excel вручную, следите за форматом номеров. В 1С номера документов обычно текстовые и имеют фиксированную длину с лидирующими нулями (например, "0000-000125"). Если записать номер как "125", автоматический нумератор 1С в дальнейшем может "сломаться", так как не сможет корректно инкрементировать номер без лидирующих нулей.

Использование правильной архитектуры циклов, отказ от устаревшего COM-соединения в пользу кроссплатформенного ТабличногоДокумента и соблюдение правил автонумерации платформы гарантируют быструю, стабильную и безошибочную загрузку любых реестров документов в базы данных 1С.

← На главную