Как исправить ошибку "Штрихкод уже имеет владельца" при программной очистке характеристик в 1С?

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

При работе с базами данных 1С периодически возникает необходимость массово скорректировать или вычистить служебные реквизиты в регистрах сведений. Распространенный сценарий — в базе по ошибке включили ведение характеристик номенклатуры, назначили штрихкоды, а затем характеристики отключили. При попытке программно очистить поле ХарактеристикаНоменклатуры в регистре сведений Штрихкоды платформа может выдать ошибку вида: «Штрихкод: 2000000011220 уже имеет владельца "Самокат 66р"».

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

Анализ исходного кода и причины возникновения ошибки

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


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

На первый взгляд код выглядит корректным, однако при выполнении НаборЗаписей.Записать() возникает сбой. Проанализируем ключевые причины такого поведения платформы:

  1. Конфликт уникальности измерений (дублирование ключа записи). В регистре сведений ключевые измерения (например, Штрихкод или комбинация Номенклатура + ХарактеристикаНоменклатуры) обеспечивают уникальность каждой строки. Если у одной позиции номенклатуры было привязано два разных штрихкода для двух разных характеристик, то после очистки характеристик обе строки будут относиться к одной номенклатуре с пустой характеристикой. С точки зрения платформы или прикладного решения создается дубль, что приводит к блокировке записи.
  2. Проверки в модуле регистра сведений. При вызове метода Записать() стандартно срабатывают события модуля регистра (например, ПередЗаписью или ПриЗаписи). Прикладная логика типовых конфигураций проверяет, не привязан ли данный штрихкод к другому владельцу или другой комбинации измерений, и генерирует исключение.
  3. Считывание регистра без отбора. Вызов НаборЗаписей.Прочитать() без предварительной установки отбора считывает весь регистр сведений в оперативную память. При вызове Записать() платформа пытается заново перезаписать абсолютно все записи регистра, что увеличивает риск столкновения с проверками и вызывает высокие нагрузки на сервер СУБД.

Способы решения проблемы

Для корректного изменения данных в регистре сведений выберем наиболее подходящий подход в зависимости от задачи.

Способ 1. Использование режима загрузки данных (ОбменДанными.Загрузка)

Чтобы обойти внутренние прикладные проверки конфигурации в процедуре ПередЗаписью, можно использовать специальный флаг Загрузка объекта ОбменДанными. Это позволит отключить алгоритмы проверки бизнес-логики типового решения.


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

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

// Отключаем обработчики событий модуля регистра
НаборЗаписей.ОбменДанными.Загрузка = Истина;

Для каждого Запись из НаборЗаписей Цикл
    Запись.ХарактеристикаНоменклатуры = Справочники.ХарактеристикиНоменклатуры.ПустаяСсылка();
КонецЦикла;

НаборЗаписей.Записать();

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

Способ 2. Удаление дублирующих записей вместо изменения

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

Проанализируем алгоритм правильной обработки таких записей:

  1. С помощью объекта Запрос находим все дублирующие штрихкоды для каждой номенклатуры.
  2. Считываем набор записей с установкой отбора по конкретному штрихкоду или номенклатуре.
  3. Оставляем одну актуальную запись с пустой характеристикой, а остальные дублирующие записи удаляем из набора.
  4. Вызываем метод Записать().

Способ 3. Использование Менеджера Записи для точечного удаления или корректировки

Когда требуется удалить или изменить единичные проблемные записи без загрузки всего набора, удобно применять МенеджерЗаписи:


МенеджерЗаписи = РегистрыСведений.Штрихкоды.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Штрихкод = "2000000011220";
МенеджерЗаписи.Прочитать();

Если МенеджерЗаписи.Выбран() Тогда
    // Если нужно полностью удалить дублирующую запись:
    МенеджерЗаписи.Удалить();
    
    // Либо скорректировать и записать с отключением проверок:
    // МенеджерЗаписи.ХарактеристикаНоменклатуры = Справочники.ХарактеристикиНоменклатуры.ПустаяСсылка();
    // МенеджерЗаписи.ОбменДанными.Загрузка = Истина;
    // МенеджерЗаписи.Записать();
КонецЕсли;

Рекомендации по безопасности при работе с регистрами

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

  1. Всегда создавайте резервную копию базы данных перед выполнением скриптов, изменяющих или очищающих ключевые регистры сведений.
  2. Используйте отборы в наборах записей. Никогда не считывайте весь регистр через НаборЗаписей.Прочитать() без отбора на рабочих базах данных большого объема.
  3. Проверяйте логику дублей. Прежде чем обнулять измерения регистра, убедитесь с помощью запроса, что после очистки измерения не возникнет совпадения ключей записей.
← На главную