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