Как удалить дубли номенклатуры в 1С, если мешает ошибка «Запись в регистре сведений с такими ключевыми полями уже существует»?

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

Приветствуем вас, уважаемые коллеги! Каждый практикующий специалист в среде «1С:Предприятие» рано или поздно сталкивается с необходимостью наведения порядка в справочниках. Одной из наиболее частых и рутинных задач является слияние дублирующихся элементов номенклатуры. Казалось бы, в типовых конфигурациях (например, в «1С:Бухгалтерия предприятия 3.0») для этого предусмотрена удобная стандартная обработка «Поиск и удаление дублей». Однако на практике процесс часто прерывается неприятной ошибкой: «Запись в регистре сведений с такими ключевыми полями уже существует».

В рамках этой статьи мы подробно разберем, почему возникает данная коллизия на уровне платформы «1С:Предприятие 8», проанализируем архитектурные особенности регистров сведений и изучим пять практических способов решения этой проблемы — от интерактивного исправления до написания собственного программного кода.

Выясняем причину: почему платформа 1С блокирует удаление дублей?

Для того чтобы понять природу ошибки, обратимся к теории баз данных 1С. Любой регистр сведений (неважно, периодический он или нет, независимый или подчиненный регистратору) функционирует по строгому правилу уникальности ключевых полей. Ключом уникальности в регистре сведений выступает уникальная комбинация всех его измерений, а для периодических регистров — комбинация измерений и периода (даты и времени).

Давайте разберем конкретную ситуацию на примере регистра сведений ЦеныНоменклатуры или КалькуляцииСебестоимостиПродукции. Допустим, в нашей базе данных по ошибке были созданы два дублирующих друг друга элемента номенклатуры:

В регистре цен на определенную дату (например, 01.10.2023) были зарегистрированы цены для обоих этих элементов:

  1. Период: 01.10.2023 | Измерение Номенклатура: Номенклатура 1 | Ресурс Цена: 50 руб.
  2. Период: 01.10.2023 | Измерение Номенклатура: Номенклатура 2 | Ресурс Цена: 75 руб.

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

Период: 01.10.2023 | Измерение Номенклатура: Номенклатура 2 | Ресурс Цена: 50 руб.

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

Способ 1. Интерактивное исправление через анализ мест использования

Рассмотрим самый простой и безопасный для неискушенных пользователей метод. Стандартная обработка «Поиск и удаление дублей» из состава Библиотеки Стандартных Подсистем (БСП) не просто выдает ошибку, она также выводит подробный список документов и объектов, в которых найдены ссылки на удаляемый элемент.

Проанализируем наши действия по шагам:

  1. В правой части формы обработки удаления дублей найдем список мест использования проблемной номенклатуры.
  2. Обратим внимание на документы, которые делают движения по конфликтующему регистру (это могут быть документы УстановкаЦенНоменклатуры, калькуляции, спецификации или расчеты себестоимости).
  3. Дважды кликнем по строке с документом, чтобы открыть его форму.
  4. В табличной части открывшегося документа найдем строку, содержащую удаляемый дубль (Номенклатура 1).
  5. Заменим эту позицию вручную на оригинальную (Номенклатура 2). Если в документе уже есть строка с оригиналом, то дублирующуюся строку следует просто удалить, при необходимости просуммировав количество или скорректировав показатели, чтобы не исказить итоговые суммы документа.
  6. Проведем документ заново.
  7. Повторим процедуру для всех документов из списка и повторно запустим обработку схлопывания дублей.

Способ 2. Профессиональное решение с помощью «Инструментов разработчика»

Если документов-регистраторов сотни или тысячи, ручная работа превращается в затяжной процесс. В такой ситуации на помощь приходит мощнейший комплекс внешних обработок — «Инструменты разработчика» (широко известный как DevTool1C от автора tormozit).

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

  1. Запустим обработку «Поиск дублей и замена ссылок» из состава «Инструментов разработчика».
  2. Этот инструмент производит замену ссылок на более низком, физическом уровне и обладает гибкими настройками транзакций.
  3. При возникновении конфликта уникальности ключевых полей в регистре сведений данная обработка детально покажет структуру конфликта вплоть до конкретных полей СУБД.
  4. Мы можем настроить правила объединения записей или временно отключить контроль уникальности для определенных таблиц (если это технически допустимо), либо пропустить проблемные ссылки, обработав основную массу (99% ссылок) в автоматическом режиме, а оставшиеся единичные случаи исправить вручную.

Способ 3. Программный метод схлопывания независимых регистров сведений

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

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


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

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

Способ 4. Обработка подчиненных регистров сведений через документы-регистраторы

Если регистр сведений подчинен регистратору (как в случае с КалькуляцииСебестоимостиПродукции, где записи делаются только документами), напрямую редактировать записи регистра нельзя. Изменения нужно вносить исключительно в сами документы.

Рассмотрим стратегию автоматизации этого процесса с помощью программного кода:

  1. Выбираем запросом все документы, которые содержат в своих табличных частях удаляемый элемент Номенклатура 1.
  2. В цикле обходим эти документы, открываем их для изменения через метод ПолучитьОбъект().
  3. В каждой табличной части ищем строки с Номенклатура 1. Если в этой же табличной части на тех же условиях (например, по той же спецификации) уже присутствует Номенклатура 2, мы объединяем их количественные и суммовые показатели в одну строку, а строку с Номенклатура 1 удаляем из таблицы.
  4. Если строки с Номенклатура 2 нет, мы просто перезаписываем ячейку, меняя ссылку на правильную.
  5. Записываем и проводим документ. Так как проведение документов обновит движения в подчиненном регистре сведений автоматически, проблема с уникальностью ключей исчезнет сама собой!

Способ 5. Проверка служебных регистров обмена данными

Очень часто дубли номенклатуры появляются из-за ошибок синхронизации данных (например, при обмене между конфигурациями «Управление торговлей» и «Бухгалтерия предприятия»). Чтобы оперативно находить расхождения в учете после подобных сеансов обмена, можно использовать программу для автоматического сравнения проводок между базами 1С.

Проанализируем этот важный аспект:

Рекомендации по профилактике появления дублей

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

Надеемся, что предложенные нами методики помогут вам быстро и безболезненно навести порядок в вашей базе данных 1С! Для комплексного аудита и устранения повторяющихся элементов также подойдёт инструмент для поиска и безопасного удаления дублей с заменой ссылок.

← На главную