Как программно изменить штрихкод номенклатуры в 1С:Управление торговлей 11?

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

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

В современных версиях конфигурации 1С:Управление торговлей 11 (начиная с версии 11.4) архитектура регистра сведений ШтрихкодыНоменклатуры устроена таким образом, что измерение у регистра только одно — сам Штрихкод. Остальные важные аналитические разрезы, такие как номенклатура, характеристика и упаковка, являются ресурсами или реквизитами. Из-за этой особенности стандартная попытка просто перезаписать значение штрихкода в существующей записи регистра приводит к ошибке платформы: "Новая запись не соответствует отбору". В этой статье мы подробно разберем, почему возникает эта ошибка, как правильно построить алгоритм изменения ключевых измерений и как оптимизировать этот процесс для обработки больших объемов данных.

Выясняем причину: почему нельзя просто переписать штрихкод?

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


Набор = РегистрыСведений.ШтрихкодыНоменклатуры.СоздатьНаборЗаписей();
Набор.Отбор.Штрихкод.Установить(СтарыйШтрихкод);
Набор.Прочитать();

Если Набор.Количество() > 0 Тогда 
    Для Каждого Запись Из Набор Цикл
        // Попытка напрямую изменить измерение, по которому установлен отбор
        Запись.Штрихкод = НовыйШтрихкод; 
    КонецЦикла;
КонецЕсли;

Набор.Записать(); // Здесь платформа вызовет исключение!

Разберем ситуацию подробно. Независимый регистр сведений в 1С контролирует уникальность записей по набору своих измерений. В нашем случае измерением является Штрихкод. Когда мы устанавливаем отбор по конкретному штрихкоду (например, "2000000012345"), платформа ожидает, что в записываемом наборе будут находиться записи исключительно с этим штрихкодом. Изменяя внутри цикла значение Запись.Штрихкод на новое (например, "4600000098765"), мы нарушаем это правило. При вызове метода Записать() система сверяет новые значения измерений в записях с установленным отбором набора. Обнаружив несовпадение, платформа блокирует транзакцию и сообщает об ошибке.

Отсюда следует единственно верное концептуальное решение: чтобы изменить штрихкод, необходимо физически удалить старую запись из регистра и создать совершенно новую запись с измененным штрихкодом.

Анализ ошибок в коде на форуме

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


Набор = РегистрыСведений.ШтрихкодыНоменклатуры.СоздатьНаборЗаписей();
Набор.Отбор.Штрихкод.Установить(СокрЛП(СтарыйШтрихкод));
Набор.Прочитать();
Если Набор.Количество() > 0 Тогда 
    Для Каждого Запись Из Набор Цикл
        Номенклатура = Запись.Номенклатура;
        Характеристика = Запись.Характеристика;
        Единица = Запись.Упаковка;
    КонецЦикла;
    
    Набор.Удалить(Запись); // Ошибка 1: Переменная "Запись" вне цикла!
КонецЕсли;
// Ошибка 2: Нет вызова метода Набор.Записать() после удаления.

В этом коде присутствуют две критические ошибки:

  1. Область видимости переменной. Метод Набор.Удалить(Запись) вызывается за пределами цикла Для каждого. На уровне платформы это либо вызовет ошибку выполнения (так как переменная не определена), либо удалит только последнюю обработанную запись (если переменная сохранила значение после выхода из цикла).
  2. Отсутствие фиксации в базе данных. Метод Удалить() коллекции набора записей лишь модифицирует таблицу в оперативной памяти. Без вызова Набор.Записать() изменения не будут перенесены в физическую базу данных, и старый штрихкод останется на месте.

Теперь, когда мы понимаем все тонкости поведения платформы, составим правильный и максимально эффективный алгоритм.

Разбираем по шагам правильный алгоритм перезаписи штрихкода

Для реализации корректной логики нам потребуется выполнить три последовательных действия:

  1. Прочитать существующую запись по старому штрихкоду и выгрузить из нее сопутствующие данные (номенклатуру, характеристику, упаковку).
  2. Полностью очистить старую запись в базе данных.
  3. Создать новую запись с сохраненными реквизитами, но уже под новым штрихкодом.

Для удаления старой записи мы можем использовать два альтернативных варианта.

Вариант А. Использование пустого набора записей (наиболее надежный и быстрый)

Этот способ основан на базовом свойстве платформы 1С: если записать набор записей с установленным отбором, не заполняя сам набор строками, то все существующие в базе данных записи, подпадающие под этот отбор, будут удалены.


// 1. Создаем набор для чтения и удаления старого штрихкода
НаборСтарый = РегистрыСведений.ШтрихкодыНоменклатуры.СоздатьНаборЗаписей();
НаборСтарый.Отбор.Штрихкод.Установить(СтарыйШтрихкод);
НаборСтарый.Прочитать();

Если НаборСтарый.Количество() > 0 Тогда
    // Сохраняем связи штрихкода со справочниками
    ТекущаяЗапись = НаборСтарый[0]; // Так как штрихкод уникален, запись будет одна
    НоменклатураСсылка   = ТекущаяЗапись.Номенклатура;
    ХарактеристикаСсылка = ТекущаяЗапись.Характеристика;
    УпаковкаСсылка       = ТекущаяЗапись.Упаковка;
    
    // Удаляем старый штрихкод из базы данных методом записи пустого набора
    НаборСтарый.Очистить(); 
    НаборСтарый.Записать(Истина); // Параметр Истина замещает данные в БД пустым набором
    
    // 2. Создаем новую запись с новым штрихкодом
    НовыйМенеджер = РегистрыСведений.ШтрихкодыНоменклатуры.СоздатьМенеджерЗаписи();
    НовыйМенеджер.Штрихкод       = НовыйШтрихкод;
    НовыйМенеджер.Номенклатура   = НоменклатураСсылка;
    НовыйМенеджер.Характеристика = ХарактеристикаСсылка;
    НовыйМенеджер.Упаковка       = УпаковкаСсылка;
    НовыйМенеджер.Записать(Истина); // Записываем новую запись в базу данных
КонецЕсли;

Вариант Б. Использование Менеджера записи для удаления

Если вам привычнее работать с объектом МенеджерЗаписи, то операцию удаления можно выполнить через него. Этот способ выглядит лаконичнее, но при массовой обработке тысяч строк он может работать чуть медленнее, чем прямая запись пустого набора.


// Получаем данные перед удалением
НаборСтарый = РегистрыСведений.ШтрихкодыНоменклатуры.СоздатьНаборЗаписей();
НаборСтарый.Отбор.Штрихкод.Установить(СтарыйШтрихкод);
НаборСтарый.Прочитать();

Если НаборСтарый.Количество() > 0 Тогда
    ЗаписьДанных = НаборСтарый[0];
    НоменклатураСсылка   = ЗаписьДанных.Номенклатура;
    ХарактеристикаСсылка = ЗаписьДанных.Характеристика;
    УпаковкаСсылка       = ЗаписьДанных.Упаковка;
    
    // Удаляем старую запись через менеджер записи
    МенеджерУдаления = РегистрыСведений.ШтрихкодыНоменклатуры.СоздатьМенеджерЗаписи();
    МенеджерУдаления.Штрихкод = СтарыйШтрихкод;
    МенеджерУдаления.Удалить(); // Метод сразу обращается к СУБД и удаляет запись
    
    // Создаем новую запись через менеджер записи
    МенеджерНовый = РегистрыСведений.ШтрихкодыНоменклатуры.СоздатьМенеджерЗаписи();
    МенеджерНовый.Штрихкод       = НовыйШтрихкод;
    МенеджерНовый.Номенклатура   = НоменклатураСсылка;
    МенеджерНовый.Характеристика = ХарактеристикаСсылка;
    МенеджерНовый.Упаковка       = УпаковкаСсылка;
    МенеджерНовый.Записать();
КонецЕсли;

Оптимизация производительности: обработка 5 000 записей без зависаний

Рассмотрим важнейший аспект решения исходной задачи автора темы. Ему требуется обработать около 5 000 штрихкодов из внешнего файла Excel. Если мы запустим вышеописанный код в обычном цикле чтения строк Excel, система начнет катастрофически зависать. На обработку такого объема может уйти от 15 минут до нескольких часов!

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

Решение: Нам необходимо обернуть весь цикл обработки файла Excel в одну явную транзакцию с помощью операторов НачатьТранзакцию() и ЗафиксироватьТранзакцию(). В этом случае СУБД выполнит все изменения во временной памяти и запишет их на диск за один раз. Время выполнения сократится с десятков минут до нескольких секунд!

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


// Начинаем транзакцию для ускорения работы с СУБД в сотни раз
НачатьТранзакцию();

Попытка
    // Предположим, что объект Excel и номера строк уже инициализированы ранее
    ТекущаяСтрока = НачальнаяСтрока;
    
    Пока ТекущаяСтрока <= КонечнаяСтрока Цикл
        
        СтарыйШК = СокрЛП(Excel.Cells(ТекущаяСтрока, КолонкаШтрихкодПоиск).Text);
        НовыйШК  = СокрЛП(Excel.Cells(ТекущаяСтрока, КолонкаШтрихкодЗамена).Text);
        
        // Обязательно проверяем заполненность данных, чтобы избежать пустых записей
        Если ЗначениеЗаполнено(СтарыйШК) И ЗначениеЗаполнено(НовыйШК) Тогда
            
            // Читаем старые данные
            НаборУдаления = РегистрыСведений.ШтрихкодыНоменклатуры.СоздатьНаборЗаписей();
            НаборУдаления.Отбор.Штрихкод.Установить(СтарыйШК);
            НаборУдаления.Прочитать();
            
            Если НаборУдаления.Количество() > 0 Тогда
                // Запоминаем реквизиты существующей привязки
                ЗаписьРегистра = НаборУдаления[0];
                ТекущаяНоменклатура   = ЗаписьРегистра.Номенклатура;
                ТекущаяХарактеристика = ЗаписьРегистра.Характеристика;
                ТекущаяУпаковка       = ЗаписьРегистра.Упаковка;
                
                // Физически удаляем старый штрихкод из базы данных
                НаборУдаления.Очистить();
                НаборУдаления.Записать(Истина);
                
                // Создаем и записываем запись с новым штрихкодом
                НовыйМенеджер = РегистрыСведений.ШтрихкодыНоменклатуры.СоздатьМенеджерЗаписи();
                НовыйМенеджер.Штрихкод       = НовыйШК;
                НовыйМенеджер.Номенклатура   = ТекущаяНоменклатура;
                НовыйМенеджер.Характеристика = ТекущаяХарактеристика;
                НовыйМенеджер.Упаковка       = ТекущаяУпаковка;
                НовыйМенеджер.Записать(Истина);
                
            КонецЕсли;
            
        КонецЕсли;
        
        ТекущаяСтрока = ТекущаяСтрока + 1;
    КонецЦикла;
    
    // Если все прошло успешно, фиксируем транзакцию в базе данных
    ЗафиксироватьТранзакцию();
    Сообщить("Успешно обработано строк: " + (ТекущаяСтрока - НачальнаяСтрока));
    
Исключение
    // В случае любой непредвиденной ошибки откатываем абсолютно все изменения
    ОтменитьТранзакцию();
    ЗаписьЖурналаРегистрации("ПрограммноеИзменениеШтрихкодов", 
        УровеньЖурналаРегистрации.Ошибка, , , 
        "Ошибка при перезаписи штрихкодов: " + ОписаниеОшибки());
    Сообщить("Произошла критическая ошибка! Изменения не сохранены. Подробнее в Журнале регистрации.");
КонецПопытки;

Альтернативный вариант без программирования

Если перед вами стоит задача разового изменения штрихкодов и вы не хотите писать программный код, полезно знать, что в конфигурации 1С:Управление торговлей 11 встроен мощный инструмент загрузки данных из внешних файлов. Выполнить операцию можно следующим стандартным путем:

  1. Откройте раздел меню НСИ и администрирование, далее перейдите в подраздел Загрузка данных из файла.
  2. В качестве загружаемого объекта выберите регистр сведений Штрихкоды номенклатуры (или откройте список штрихкодов напрямую через Функции для технического специалиста и нажмите кнопку Еще -> Загрузить из файла).
  3. Скопируйте таблицу с сопоставленными данными из вашего Excel-файла напрямую в открывшееся окно загрузки платформы 1С.
  4. Настройте сопоставление колонок и запустите загрузку. Встроенный механизм платформы самостоятельно проанализирует зависимости, очистит неактуальные данные и создаст новые записи с учетом системных подписок на события контроля уникальности.

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

← На главную