Как заменить старые штрихкоды номенклатуры на новые из Excel в 1С?

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

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

Проблема заключается в структуре самого регистра. В непериодическом регистре сведений уникальность каждой записи определяется набором ее измерений. В стандартном регистре ШтрихкодыНоменклатуры измерением является поле Штрихкод, а поле Номенклатура — это ресурс. Когда вы пытаетесь записать новую связь "Номенклатура - Штрихкод", система проверяет уникальность по полю Штрихкод. Если такой штрихкод уже есть, возникнет ошибка. Если его нет, система создаст новую запись, даже если для указанной номенклатуры уже была другая запись со старым штрихкодом. Таким образом, простая запись не "обновляет" штрихкод у товара, а добавляет еще один.

Чтобы корректно заменить старые штрихкоды на новые, необходимо сначала удалить существующие записи для нужной номенклатуры, а затем создать новые. Рассмотрим несколько способов решения этой задачи.

Решение 1: Последовательное удаление и запись

Это самый простой для понимания и логичный подход, который был предложен на форуме. Он состоит из двух основных шагов: сначала полная очистка старых данных для обновляемой номенклатуры, а затем — загрузка новых.

Порядок действий:

  1. Подготовка данных. Сначала подготовим данные для загрузки. Обычно это таблица значений, полученная из файла Excel, с колонками, содержащими номенклатуру (ссылку или артикул для поиска) и ее новый штрихкод.
  2. Шаг 1: Удаление старых штрихкодов. Необходимо обойти список номенклатуры, для которой мы обновляем штрихкоды, и для каждой позиции удалить все существующие записи из регистра ШтрихкодыНоменклатуры. Чтобы не делать это в цикле по одной записи (что очень медленно), эффективнее использовать объект НаборЗаписей с установленным отбором.
  3. Шаг 2: Запись новых штрихкодов. После того как старые записи удалены, можно приступать к записи новых. Обходим нашу таблицу с данными и для каждой строки создаем новую запись в регистре с помощью МенеджерЗаписи.

Посмотрим на примерный код, который реализует этот алгоритм.


// Предполагается, что ТЗЗагрузки - это ТаблицаЗначений
// с колонками "Номенклатура" (тип СправочникСсылка.Номенклатура) 
// и "НовыйШтрихкод" (тип Строка).

НачатьТранзакцию();
Попытка
    // ШАГ 1: Удаление старых штрихкодов для всей номенклатуры из файла
    // Собираем номенклатуру в массив, чтобы установить отбор по списку
    МассивНоменклатуры = ТЗЗагрузки.ВыгрузитьКолонку("Номенклатура");
    
    НаборДляУдаления = РегистрыСведений.ШтрихкодыНоменклатуры.СоздатьНаборЗаписей();
    НаборДляУдаления.Отбор.Номенклатура.ВидСравнения = ВидСравнения.ВСписке;
    НаборДляУдаления.Отбор.Номенклатура.Установить(МассивНоменклатуры);
    
    // Запись пустого набора с отбором равносильна удалению всех записей, 
    // удовлетворяющих этому отбору. Это быстрая операция.
    НаборДляУдаления.Записать();
    
    // ШАГ 2: Запись новых штрихкодов
    Для Каждого СтрокаТЗ Из ТЗЗагрузки Цикл
        МенеджерЗаписи = РегистрыСведений.ШтрихкодыНоменклатуры.СоздатьМенеджерЗаписи();
        МенеджерЗаписи.Штрихкод = СокрЛП(СтрокаТЗ.НовыйШтрихкод);
        МенеджерЗаписи.Номенклатура = СтрокаТЗ.Номенклатура;
        
        // Попытка записи нужна для отлова ошибок, например, если
        // новый штрихкод уже назначен другому товару.
        Попытка
            МенеджерЗаписи.Записать();
        Исключение
            Сообщить("Не удалось записать штрихкод '" + СтрокаТЗ.НовыйШтрихкод + "' для номенклатуры '" + СтрокаТЗ.Номенклатура + "'. " + ОписаниеОшибки());
            // Прерываем транзакцию в случае ошибки
            ОтменитьТранзакцию();
            Возврат; // Прерываем выполнение процедуры
        КонецПопытки;
        
    КонецЦикла;
    
    ЗафиксироватьТранзакцию();
    Сообщить("Обновление штрихкодов успешно завершено!");
    
Исключение
    ОтменитьТранзакцию();
    Сообщить("Произошла ошибка при обновлении штрихкодов! Все изменения отменены. " + ОписаниеОшибки());
КонецПопытки;

Этот метод надежен и понятен, но он выполняет две большие операции последовательно: одно массовое удаление и затем серию записей.

Решение 2: Оптимальное обновление через НаборЗаписей

Более производительным и идеологически верным способом для массовых операций с регистрами является использование объекта НаборЗаписей. Он позволяет выполнить удаление старых и добавление новых записей в рамках одной транзакции для каждой группы данных.

Метод НаборЗаписей.Записать() имеет важный параметр Замещение, который по умолчанию равен Истина. Это означает, что при записи набора с установленным отбором все существующие в базе записи, удовлетворяющие этому отбору, будут полностью заменены записями из нашего набора. Это именно то, что нам нужно.

Порядок действий:

  1. Группировка данных. Необходимо сгруппировать данные из Excel по номенклатуре. Например, используя соответствие, где ключ — это ссылка на номенклатуру, а значение — массив ее новых штрихкодов.
  2. Обработка в цикле. Для каждой номенклатуры из сгруппированных данных выполняем следующие действия:
    • Создаем новый объект НаборЗаписей.
    • Устанавливаем отбор по текущей номенклатуре.
    • Добавляем в этот набор все новые штрихкоды для этой номенклатуры.
    • Вызываем метод НаборЗаписей.Записать(). Система автоматически удалит старые записи по отбору и вставит новые.

Посмотрим на пример кода, реализующий этот подход.


// Предположим, что ТЗЗагрузки уже подготовлена и содержит
// колонки "Номенклатура" и "НовыйШтрихкод"

// Шаг 1: Группируем штрихкоды по номенклатуре
СоответствиеНоменклатураШК = Новый Соответствие;
Для Каждого СтрокаТЗ Из ТЗЗагрузки Цикл
    Если СоответствиеНоменклатураШК[СтрокаТЗ.Номенклатура] = Неопределено Тогда
        СоответствиеНоменклатураШК.Вставить(СтрокаТЗ.Номенклатура, Новый Массив);
    КонецЕсли;
    СоответствиеНоменклатураШК[СтрокаТЗ.Номенклатура].Добавить(СтрокаТЗ.НовыйШтрихкод);
КонецЦикла;

// Шаг 2: Обрабатываем каждую номенклатуру отдельно
НачатьТранзакцию();
Попытка
    Для Каждого КлючЗначение Из СоответствиеНоменклатураШК Цикл
        СсылкаНаНоменклатуру = КлючЗначение.Ключ;
        МассивНовыхШтрихкодов = КлючЗначение.Значение;
        
        НаборЗаписей = РегистрыСведений.ШтрихкодыНоменклатуры.СоздатьНаборЗаписей();
        // Устанавливаем отбор по номенклатуре. Все существующие записи, 
        // связанные с этой номенклатурой, будут целью для замещения.
        НаборЗаписей.Отбор.Номенклатура.Установить(СсылкаНаНоменклатуру);
        
        // Добавляем новые записи в набор
        Для Каждого НовыйШтрихкод Из МассивНовыхШтрихкодов Цикл
            НоваяЗапись = НаборЗаписей.Добавить();
            НоваяЗапись.Штрихкод = СокрЛП(НовыйШтрихкод);
            НоваяЗапись.Номенклатура = СсылкаНаНоменклатуру;
        КонецЦикла;
        
        // Записываем набор. Параметр "Замещать" (по умолчанию Истина)
        // заставит платформу удалить старые записи по отбору и записать новые.
        НаборЗаписей.Записать();
    КонецЦикла;
    
    ЗафиксироватьТранзакцию();
    Сообщить("Обновление штрихкодов успешно завершено!");
    
Исключение
    ОтменитьТранзакцию();
    Сообщить("Произошла ошибка при обновлении штрихкодов! Все изменения отменены. " + ОписаниеОшибки());
КонецПопытки;

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

Важные моменты и возможные риски

Перед тем как запускать массовое обновление, крайне важно учесть несколько моментов, чтобы не нарушить целостность данных.

  1. Анализ данных. Как справедливо было замечено в обсуждении на форуме, вы должны быть на 100% уверены, что удаляемые штрихкоды действительно не нужны. Задайте себе вопрос: "А что, если существующие штрихкоды относятся к упаковкам, а новые, из файла, — к штукам?". В таком случае старые записи удалять нельзя, и задача кардинально меняется. Всегда анализируйте бизнес-смысл данных перед их удалением.
  2. Резервное копирование. Это золотое правило. Перед любыми массовыми изменениями данных обязательно сделайте резервную копию базы данных.
  3. Использование транзакций. Как показано в примерах кода, все операции изменения данных следует оборачивать в транзакцию (НачатьТранзакцию() / ЗафиксироватьТранзакцию()). Если в процессе возникнет ошибка, вы сможете отменить все сделанные изменения с помощью ОтменитьТранзакцию(), и база данных останется в исходном состоянии.
  4. Готовые инструменты. Не стоит забывать о существовании типовых обработок, таких как "Загрузка данных из табличного документа". Они часто имеют встроенные механизмы для поиска и обновления записей. Однако для нашей специфической задачи (обновление по ресурсу, а не по измерению) может потребоваться предварительная очистка данных, как и при ручном написании кода.
  5. Тестирование. Любую обработку по массовому изменению данных сначала протестируйте на копии базы, чтобы убедиться в корректности ее работы и оценить время выполнения.
← На главную