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

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

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

Выясним причину ошибки уникальности записей

Проанализируем ситуацию, которая происходит в платформе по умолчанию. Для периодического регистра сведений с подчинением регистратору (периодичность День, Месяц или Секунда) ключ уникальности записи формируется из полей:

  1. Период (с округлением до выбранной периодичности).
  2. Все измерения регистра.

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

Способ 1. Установка периодичности «По позиции регистратора»

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

Рассмотрим подробнее, как меняется поведение платформы при таком режиме:

  1. В ключ уникальности теперь включается позиция документа-регистратора на временной оси (момент времени, состоящий из даты, времени и ссылки на документ).
  2. Система позволяет разным документам формировать движения с одинаковыми значениями измерений, даже если документы созданы в одну и ту же секунду.
  3. Поле Период заполняется платформой автоматически на основе момента времени регистратора. Попытки вручную присвоить значение свойству НоваяЗапись.Период в программном коде платформой игнорируются.

Особенности и подводные камни периодичности «По позиции регистратора»:

  1. Получение срезов в запросах: В виртуальные таблицы СрезПоследних и СрезПервых в качестве параметра среза нельзя просто передать значение типа Дата. Требуется передавать объект МоментВремени или Граница, иначе платформа не сможет однозначно определить срез относительно документов за этот день.
  2. Производительность: Для вычисления виртуальных срезов СУБД строит более сложные подзапросы с соединением по составному ключу (Дата + Ссылка), что при больших объемах данных создает дополнительную нагрузку на сервер.

Способ 2. Корректное оформление процедуры ОбработкаПроведения

Посмотрим на пример исходного кода из вопроса автора. В нем набор записей создается вручную через менеджер регистра:


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

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

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

Перепишем процедуру проведения по стандарту разработки 1С через коллекцию Движения:


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

Способ 3. Альтернативные архитектурные решения

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

  1. Переход на регистр накопления: Если в ресурсах регистра присутствуют суммовые или количественные показатели (как поле Количество в примере), правильнее использовать Регистр накопления (с видом Обороты или Остатки). В регистрах накопления разные документы могут беспрепятственно вносить записи с любыми одинаковыми аналитиками без конфликта уникальности, а агрегированные данные легко получаются через виртуальные таблицы оборотов.
  2. Использование независимого регистра сведений: Если требуется хранить исключительно актуальное («последнее») значение среза без привязки к документу-регистратору, регистр делают независимым и непериодическим. Запись новых значений осуществляется программно при проведении с предварительной очисткой или перезаписью среза по измерениям.
  3. Добавление разделяющего измерения: Если регистр должен оставаться подчиненным регистратору с периодичностью День или Секунда, в состав измерений можно добавить реквизит, гарантирующий уникальность (например, измерение ДокументОснование или номер строки). Это предотвратит дублирование ключа, но изменит состав группировки при получении срезов.
← На главную