При разработке в среде 1С программисты часто сталкиваются с ситуацией, когда один документ-регистратор должен сформировать несколько записей в регистре сведений, где значения ключевых полей (измерений) совпадают. Попытка такой записи стандартными средствами приводит к ошибке: «Запись с такими ключевыми полями существует!». Рассмотрим подробно, почему возникает эта проблема и как ее правильно решить, сохранив целостность данных.
Для начала проанализируем, как платформа 1С определяет уникальность записи в регистре сведений. Если регистр является подчиненным регистратору, то уникальность каждой строки в базе данных проверяется по совокупности следующих полей:
Важно понимать: когда мы устанавливаем периодичность По позиции регистратора, это лишь означает, что поле Период будет автоматически заполняться датой и временем документа. Однако внутри одного и того же документа (одного регистратора) значения полей Регистратор и Период для всех создаваемых движений будут идентичными.
Следовательно, если в табличной части документа присутствуют две строки с одинаковой номенклатурой (или любым другим набором измерений), платформа воспримет их как попытку дважды записать данные в одну и ту же «ячейку» регистра. Поскольку регистр сведений — это не список операций, а хранилище состояний, он не допускает дублирования ключей.
Разберем самый надежный и распространенный способ решения проблемы — введение дополнительного измерения, которое будет гарантировать уникальность каждой строки в рамках одного документа. Чаще всего для этой цели используют номер строки из документа.
Пошаговый алгоритм реализации:
НомерСтрокиДокумента (или КлючСвязи).Число (длина 10, точность 0) или УникальныйИдентификатор.ОбработкаПроведения, добавим заполнение этого поля.Проанализируем пример кода для заполнения набора записей с использованием нового измерения:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// Очищаем старые записи регистратора
Движения.РегистрЦен.Записывать = Истина;
Движения.РегистрЦен.Очистить();
Для Каждого ТекСтрока Из ТабличнаяЧасть1 Цикл
НоваяЗапись = Движения.РегистрЦен.Добавить();
НоваяЗапись.Период = Дата;
НоваяЗапись.Регистратор = Ссылка;
// Заполняем ключевые измерения
НоваяЗапись.Номенклатура = ТекСтрока.Номенклатура;
НоваяЗапись.ТипЦен = ТекСтрока.ТипЦен;
// Обеспечиваем уникальность с помощью номера строки
НоваяЗапись.НомерСтрокиДокумента = ТекСтрока.НомерСтроки;
// Заполняем ресурсы
НоваяЗапись.Цена = ТекСтрока.Цена;
КонецЦикла;
КонецПроцедуры
В данном примере, даже если в документе выбрана одна и та же номенклатура дважды, значение НомерСтрокиДокумента будет разным (например, 1 и 5), что сделает записи уникальными для платформы.
Рассмотрим ситуацию, когда поле, вызывающее конфликт, на самом деле не должно быть ключевым. Иногда разработчики ошибочно включают в состав измерений параметры, по которым не требуется выполнять отборы или срезы данных.
Если нам не нужно строить индекс по конкретному полю и мы не используем его для поиска уникального состояния, мы можем перенести его в Ресурсы. Поля-ресурсы не входят в состав уникального ключа. Однако помните: если при этом остальные измерения совпадут, вторая запись просто «затрет» первую (в случае независимого регистра) или вызовет ту же ошибку при попытке записи набора. Поэтому данный метод эффективен только тогда, когда дублирующееся поле действительно является лишь описательным значением.
Если порядок строк в документе может меняться, а нам критически важно сохранять жесткую связь между конкретной строкой документа и записью в регистре, лучше использовать УникальныйИдентификатор.
Рассмотрим, как это работает:
В табличную часть документа добавляется скрытый реквизит КлючСвязи с типом УникальныйИдентификатор. При добавлении новой строки в документ генерируется новый GUID. В регистре сведений также создается измерение КлючСвязи с таким же типом. Это гарантирует 100% уникальность записей, даже если все остальные параметры (номенклатура, цена, дата) полностью совпадают.
Проанализируем работу системы в режиме подчинения регистратору. Когда мы выполняем проведение документа, 1С автоматически создает НаборЗаписей. Если в этом наборе данных перед вызовом метода Записать() окажутся две строки с одинаковыми измерениями, транзакция будет прервана.
Важные моменты, которые стоит учитывать:
Записывать = Истина.Свернуть(), чтобы объединить одинаковые строки, если бизнес-логика позволяет суммировать их значения.Если ваша задача подразумевает хранение неограниченного количества одинаковых записей (событий), которые не являются «срезами состояния», возможно, регистр сведений — не лучший выбор. Рассмотрим альтернативы:
Таким образом, для исправления ошибки в подчиненном регистре сведений наиболее правильным путем будет добавление измерения «Номер строки» или использование Ключа связи. Это позволит документу хранить любое количество строк с повторяющимися данными, не нарушая механизмов платформы 1С.