При работе с базами данных 1С периодически возникает необходимость массово скорректировать или вычистить служебные реквизиты в регистрах сведений. Распространенный сценарий — в базе по ошибке включили ведение характеристик номенклатуры, назначили штрихкоды, а затем характеристики отключили. При попытке программно очистить поле ХарактеристикаНоменклатуры в регистре сведений Штрихкоды платформа может выдать ошибку вида: «Штрихкод: 2000000011220 уже имеет владельца "Самокат 66р"».
Разберем подробнее, почему возникает эта ошибка, какие особенности структуры регистров сведений необходимо учитывать при обходе записей и как правильно реализовать программную обработку.
Рассмотрим стандартный пример кода, который часто используют для попытки массовой очистки характеристик:
НаборЗаписей = РегистрыСведений.Штрихкоды.СоздатьНаборЗаписей();
НаборЗаписей.Прочитать();
Для каждого Запись из НаборЗаписей Цикл
Если Запись.ХарактеристикаНоменклатуры <> Справочники.ХарактеристикиНоменклатуры.ПустаяСсылка() Тогда
Запись.ХарактеристикаНоменклатуры = Справочники.ХарактеристикиНоменклатуры.ПустаяСсылка();
КонецЕсли;
КонецЦикла;
НаборЗаписей.Записать();
На первый взгляд код выглядит корректным, однако при выполнении НаборЗаписей.Записать() возникает сбой. Проанализируем ключевые причины такого поведения платформы:
Штрихкод или комбинация Номенклатура + ХарактеристикаНоменклатуры) обеспечивают уникальность каждой строки. Если у одной позиции номенклатуры было привязано два разных штрихкода для двух разных характеристик, то после очистки характеристик обе строки будут относиться к одной номенклатуре с пустой характеристикой. С точки зрения платформы или прикладного решения создается дубль, что приводит к блокировке записи.Записать() стандартно срабатывают события модуля регистра (например, ПередЗаписью или ПриЗаписи). Прикладная логика типовых конфигураций проверяет, не привязан ли данный штрихкод к другому владельцу или другой комбинации измерений, и генерирует исключение.НаборЗаписей.Прочитать() без предварительной установки отбора считывает весь регистр сведений в оперативную память. При вызове Записать() платформа пытается заново перезаписать абсолютно все записи регистра, что увеличивает риск столкновения с проверками и вызывает высокие нагрузки на сервер СУБД.Для корректного изменения данных в регистре сведений выберем наиболее подходящий подход в зависимости от задачи.
Чтобы обойти внутренние прикладные проверки конфигурации в процедуре ПередЗаписью, можно использовать специальный флаг Загрузка объекта ОбменДанными. Это позволит отключить алгоритмы проверки бизнес-логики типового решения.
НаборЗаписей = РегистрыСведений.Штрихкоды.СоздатьНаборЗаписей();
// Устанавливаем точечный отбор по номенклатуре или штрихкоду
НаборЗаписей.Отбор.Номенклатура.Установить(СсылкаНаНоменклатуру);
НаборЗаписей.Прочитать();
// Отключаем обработчики событий модуля регистра
НаборЗаписей.ОбменДанными.Загрузка = Истина;
Для каждого Запись из НаборЗаписей Цикл
Запись.ХарактеристикаНоменклатуры = Справочники.ХарактеристикиНоменклатуры.ПустаяСсылка();
КонецЦикла;
НаборЗаписей.Записать();
Важная деталь: флаг ОбменДанными.Загрузка = Истина отключает только проверки в коде конфигурации. Он не может отменить контроль уникальности ключа записи со стороны самой платформы 1С. Если у товара было несколько штрихкодов с разными характеристиками, очистка характеристик все равно вызовет ошибку ключа записи СУБД.
Если к товару было привязано несколько штрихкодов под разные характеристики, а после отключения режимов характеристик требуется оставить только один уникальный штрихкод, лишние записи нужно удалять, а не очищать.
Проанализируем алгоритм правильной обработки таких записей:
Запрос находим все дублирующие штрихкоды для каждой номенклатуры.Записать().Когда требуется удалить или изменить единичные проблемные записи без загрузки всего набора, удобно применять МенеджерЗаписи:
МенеджерЗаписи = РегистрыСведений.Штрихкоды.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Штрихкод = "2000000011220";
МенеджерЗаписи.Прочитать();
Если МенеджерЗаписи.Выбран() Тогда
// Если нужно полностью удалить дублирующую запись:
МенеджерЗаписи.Удалить();
// Либо скорректировать и записать с отключением проверок:
// МенеджерЗаписи.ХарактеристикаНоменклатуры = Справочники.ХарактеристикиНоменклатуры.ПустаяСсылка();
// МенеджерЗаписи.ОбменДанными.Загрузка = Истина;
// МенеджерЗаписи.Записать();
КонецЕсли;
Посмотрим на основные правила, которые помогут избежать потери данных и ошибок блокировки при программной обработке регистров:
НаборЗаписей.Прочитать() без отбора на рабочих базах данных большого объема.