Как избежать ошибки дублирования записей при проведении документа в регистр сведений?

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

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

Выясним причину возникновения ошибки

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

Проанализируем типичные ошибки при попытке обойти эту ситуацию:

  1. Запрос к регистру сведений при проведении. Попытка сделать запрос к самому регистру сведений во время проведения нового документа ничего не даст — выборка будет пустой, так как физически в базу данных движения еще не записаны. При перепроведении документа платформа по умолчанию (в свойстве УдалениеДвижений = Удалять автоматически) сначала очищает старые движения, поэтому запрос к регистру снова вернет пустой результат. Проверять и фильтровать нужно не данные в регистре, а строки самого документа.
  2. Использование метода Получить() менеджера регистра. Метод Получить() возвращает структуру только с ресурсами регистра на определенный срез. Измерения в эту структуру не входят, так как они сами передаются в качестве параметров отбора. Кроме того, этот метод считывает зафиксированные в базе данные и не видит строки документа в текущей транзакции.
  3. Периодичность регистра. Если периодичность регистра установлена не По позиции регистратора, а, к примеру, В пределах дня, то конфликт уникальности может возникнуть даже между двумя разными документами за одно и то же число с одинаковой моделью.

Рассмотрим надежные и правильные варианты решения проблемы дублирования данных внутри одного документа.

Способ 1. Свертка копии табличной части перед записью

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

Посмотрим на пример реализации в процедуре ОбработкаПроведения:


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

КонецПроцедуры

Метод Свернуть() оставляет только уникальные комбинации колонок, переданных в первом параметре, а числовые поля из второго параметра суммирует. Если суммирование цены не подходит, рассмотрим другие способы группировки.

Способ 2. Получение уникальных строк через запрос с группировкой

Более гибкий подход — использование запроса непосредственно к табличной части документа. Этот способ позволяет точно определить, какую цену брать при наличии дублей моделей (например, максимальную, минимальную или среднюю).

Разберем по шагам реализацию через объект Запрос:


Процедура ОбработкаПроведения(Отказ, Режим)
    
    Движения.ЦеныНаРаботы.Записывать = Истина;
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	ДокументТабЧасть.Модель КАК Модель,
        |	МАКСИМУМ(ДокументТабЧасть.Цена) КАК Цена
        |ИЗ
        |	Документ.УстановкаЦенНаРаботы.ТабличнаяЧасть КАК ДокументТабЧасть
        |ГДЕ
        |	ДокументТабЧасть.Ссылка = &Ссылка
        |СГРУППИРОВАТЬ ПО
        |	ДокументТабЧасть.Модель";
        
    Запрос.УстановитьПараметр("Ссылка", Ссылка);
    
    РезультатЗапроса = Запрос.Выполнить();
    Выборка = РезультатЗапроса.Выбрать();
    
    Пока Выборка.Следующий() Цикл
        Движение = Движения.ЦеныНаРаботы.Добавить();
        Движение.Период = Дата;
        Движение.Модель = Выборка.Модель;
        Движение.Цена   = Выборка.Цена;
    КонецЦикла;

КонецПроцедуры

Конструкция СГРУППИРОВАТЬ ПО гарантирует, что каждая Модель встретится в выборке ровно один раз, что полностью исключает ошибку дублирования записей в регистре.

Способ 3. Фильтрация дублей в цикле с помощью коллекции Соответствие

Если бизнес-логика требует записать только первое вхождение модели, а все последующие повторы в документе просто проигнорировать, удобно использовать объект Соответствие для фиксации уже обработанных позиций.

Посмотрим на пример работы:


Процедура ОбработкаПроведения(Отказ, Режим)
    
    Движения.ЦеныНаРаботы.Записывать = Истина;
    ОбработанныеМодели = Новый Соответствие;
    
    Для Каждого СтрокаТЧ Из ТабличнаяЧасть Цикл
        
        // Если модель уже была добавлена ранее в этом цикле - пропускаем строку
        Если ОбработанныеМодели[СтрокаТЧ.Модель] <> Неопределено Тогда
            Продолжить;
        КонецЕсли;
        
        // Регистрируем модель как обработанную
        ОбработанныеМодели.Вставить(СтрокаТЧ.Модель, Истина);
        
        Движение = Движения.ЦеныНаРаботы.Добавить();
        Движение.Период = Дата;
        Движение.Модель = СтрокаТЧ.Модель;
        Движение.Цена   = СтрокаТЧ.Цена;
        
    КонецЦикла;

КонецПроцедуры

Способ 4. Запрет проведения при наличии повторяющихся строк

В большинстве прикладных задач дублирование позиций в одном документе установки цен является ошибкой оператора. В таком случае правильнее не маскировать проблему автопропуском строк, а предупредить пользователя и отменить проведение. Для этого используем событие ОбработкаПроверкиЗаполнения:


Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
    
    ТаблицаПроверки = ТабличнаяЧасть.Выгрузить();
    ТаблицаПроверки.Свернуть("Модель");
    
    Если ТаблицаПроверки.Количество() <> ТабличнаяЧасть.Количество() Тогда
        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = "В табличной части обнаружены повторяющиеся модели! Устраните дубли.";
        Сообщение.Поле = "ТабличнаяЧасть";
        Сообщение.УстановитьДанные(ЭтотОбъект);
        Сообщение.Сообщить();
        
        Отказ = Истина;
    КонецЕсли;
    
КонецПроцедуры

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

← На главную