Почему возникают ошибки уникальности в РИБ при загрузке непериодических регистров сведений и как это исправить?

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

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

Анализируем причину возникновения ошибки

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

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

  1. В независимом непериодическом регистре ключ записи состоит только из значений измерений.
  2. В периодическом регистре к ключу добавляется поле Период.
  3. В регистре, подчиненном регистратору (режим записи "По позиции регистратора"), в ключ неявно добавляются регистратор и момент времени.
  4. В непериодическом регистре, подчиненном регистратору, поле Регистратор является сервисным полем для связи, но не входит в ключ записи.

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

Проблема хронологии выгрузки в РИБ

Рассмотрим, как это влияет на обмен в РИБ. Выясним причину, по которой обмен «встает» при загрузке данных в периферийную базу. Когда мы регистрируем изменения для обмена, платформа фиксирует текущее состояние объектов. При формировании файла выгрузки (XML) платформа группирует объекты по типам метаданных и записывает их в пакет.

Проанализируем сценарий:

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

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

Демонстрация ошибки программным кодом

Для наглядности проанализируем пример кода, который моделирует поведение платформы при записи движений двумя документами. Рассмотрим, как неправильное управление набором записей приводит к ошибке уникальности:


Процедура КомандаЗаписатьНаСервере()
    // Пытаемся записать данные для Документа №1
    НаборЗаписей1 = РегистрыСведений.ТестовыйРегистр.СоздатьНаборЗаписей();
    Док1 = Документы.ТестовыйДокумент.НайтиПоНомеру("001");
    НаборЗаписей1.Отбор.Регистратор.Установить(Док1);
    
    НоваяЗапись1 = НаборЗаписей1.Добавить();
    НоваяЗапись1.ИзмерениеСтатус = Истина; // Устанавливаем значение измерения
    НаборЗаписей1.Записать(); // Здесь все пройдет успешно
    
    // Пытаемся записать данные для Документа №2 с тем же значением измерения
    НаборЗаписей2 = РегистрыСведений.ТестовыйРегистр.СоздатьНаборЗаписей();
    Док2 = Документы.ТестовыйДокумент.НайтиПоНомеру("002");
    НаборЗаписей2.Отбор.Регистратор.Установить(Док2);
    
    НоваяЗапись2 = НаборЗаписей2.Добавить();
    НоваяЗапись2.ИзмерениеСтатус = Истина; // Ключевое поле совпадает с записью Док1!
    НаборЗаписей2.Записать(); // ОШИБКА: Запись с такими ключевыми полями существует!
КонецПроцедуры

В данном примере мы видим, что даже если Регистратор у наборов разный, платформа блокирует запись из-за совпадения поля ИзмерениеСтатус. При обмене РИБ происходит ровно то же самое, но на уровне системных механизмов загрузки XML.

Способ решения №1: Использование периодичности

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

Важные нюансы:

Способ решения №2: Добавление Регистратора в Измерения

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

  1. Создаем в регистре сведений новое измерение, например, ДокументОснование с типом ДокументСсылка (или составным типом).
  2. В обработке проведения документа заполняем это измерение ссылкой на сам документ (ЭтотОбъект.Ссылка).

В этом случае поле Регистратор все еще остается системным, но наше новое измерение ДокументОснование входит в ключ уникальности. Теперь записи от разных документов всегда будут иметь разные ключи, и ошибка в РИБ исчезнет, так как при загрузке наборы перестанут "наступать" друг на друга.

Способ решения №3: Программное управление уникальностью

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

Вместо стандартного режима Движения.ИмяРегистра.Записывать = Истина в модуле документа, используйте явную очистку конфликтующих записей перед записью новых. Однако помните, что в РИБ это не спасет от ошибки при загрузке, так как там работает стандартный механизм. Поэтому для РИБ лучшим решением остается Изменение состава ключевых полей (измерений).

Выясним позицию фирмы «1С»

Как показывает практика и обсуждение на форумах, данное поведение платформы является "особенностью реализации". Платформа оптимизирована для передачи состояния объектов, а не истории транзакций. В версиях платформы 8.3.17 и выше механизм регистрации изменений постоянно дорабатывается, но фундаментальное правило уникальности ключа записи регистра сведений остается неизменным с версии 8.0.

Резюмируем наши выводы:

  1. Поле Регистратор в непериодическом регистре не обеспечивает уникальность записи.
  2. Ошибки в РИБ при загрузке регистров вызваны тем, что наборы записей разных документов могут содержать одинаковые ключи и загружаться в произвольном порядке.
  3. Для исправления ситуации следует либо включить периодичность "По позиции регистратора", либо добавить регистратор в состав явных измерений регистра.

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

← На главную