Почему при изменении измерений в периодическом регистре сведений двоятся записи и как это исправить?

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

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

В чем причина появления дублирующихся записей

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

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

  1. Изменение ресурса: набор измерений остается прежним. При добавлении новой записи с более свежей датой СрезПоследних берет наиболее актуальное значение ресурса для той же самой комбинации измерений, поэтому строка в таблице заменяется.
  2. Изменение измерения: когда мы меняем значение поля, являющегося измерением (например, переводим оборудование на новый порт или меняем адрес доступа), в базе данных создается совершенно новая комбинация измерений. Старая комбинация никуда не исчезает из базы. Для нее по-прежнему существует своя последняя запись, и виртуальная таблица возвращает обе строки: одну для старого набора измерений, вторую — для нового.

Вариант 1. Пересмотр архитектуры регистра (рекомендуемый подход)

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

  1. Измерения: оставляем только Оборудование (для него имеет смысл установить свойство Ведущее, чтобы при удалении оборудования автоматически очищались связанные записи).
  2. Ресурсы: переносим поля АдресДоступа, ВидДоступа, УчетнаяЗапись, Пароль, Протокол в состав ресурсов.
  3. Реквизиты: оставляем дополнительные служебные данные, не влияющие на уникальность среза, например Ответственный.

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

Вариант 2. Программное обновление через МенеджерЗаписи с контролем старых измерений

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

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

  1. Считываем существующую запись по исходным (старым) измерениям и периоду.
  2. Удаляем старую запись из базы данных, если требуется модификация ключа, либо перезаписываем её.
  3. Формируем и записываем менеджер с новыми значениями.

Посмотрим на пример кода с использованием менеджера записи:


Менеджер = РегистрыСведений.СведенияОДоступах.СоздатьМенеджерЗаписи();

// Заполняем старые ключевые поля для поиска существующей записи
Менеджер.Период = ИсходныйПериод;
Менеджер.Оборудование = ИсходноеОборудование;
Менеджер.АдресДоступа = ИсходныйАдресДоступа;
Менеджер.ВидДоступа = ИсходныйВидДоступа;

Менеджер.Прочитать();

Если Менеджер.Выбран() Тогда
    // Удаляем старую запись, чтобы не оставалось дубля в срезе последних
    Менеджер.Удалить();
КонецЕсли;

// Создаем новую запись с обновленными измерениями и ресурсами
НовыйМенеджер = РегистрыСведений.СведенияОДоступах.СоздатьМенеджерЗаписи();
НовыйМенеджер.Период = ТекущаяДата();
НовыйМенеджер.Оборудование = ЭтаФорма.Оборудование;
НовыйМенеджер.АдресДоступа = ЭтаФорма.АдресДоступа;
НовыйМенеджер.ВидДоступа = ЭтаФорма.ВидДоступа;
НовыйМенеджер.УчетнаяЗапись = ЭтаФорма.УчетнаяЗапись;
НовыйМенеджер.Пароль = ЭтаФорма.Пароль;
НовыйМенеджер.Протокол = ЭтаФорма.Протокол;
НовыйМенеджер.Ответственный = ЭтаФорма.Ответственный;

НовыйМенеджер.Записать(Истина);

Вариант 3. Особенности работы с НаборомЗаписей и параметром Замещать

Разберем типичную ошибку при использовании объекта НаборЗаписей. При вызове метода НаборЗаписей.Записать(Истина) выполняется замещение данных строго в рамках установленного отбора.

Если отбор установлен только по одному измерению Оборудование, установка режима замещения Истина приведет к тому, что платформа удалит всю накопленную историю по данному оборудованию за все даты и запишет только текущую строку набора:


НаборЗаписей = РегистрыСведений.СведенияОДоступах.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Оборудование.Установить(ЭтаФорма.Оборудование);

// Внимание: метод Прочитать() заполнит набор ВСЕМИ историческими записями по оборудованию
НаборЗаписей.Прочитать();

НовЗапись = НаборЗаписей.Добавить();
НовЗапись.Период = ТекущаяДата();
НовЗапись.Оборудование = ЭтаФорма.Оборудование;
НовЗапись.АдресДоступа = ЭтаФорма.АдресДоступа;
НовЗапись.ВидДоступа = ЭтаФорма.ВидДоступа;
НовЗапись.УчетнаяЗапись = ЭтаФорма.УчетнаяЗапись;
НовЗапись.Пароль = ЭтаФорма.Пароль;
НовЗапись.Протокол = ЭтаФорма.Протокол;
НовЗапись.Ответственный = ЭтаФорма.Ответственный;

// Запись с замещением перезапишет весь отбор целиком
НаборЗаписей.Записать(Истина);

Для сохранения истории при добавлении записей через набор без предварительного чтения набора метод вызывается с параметром Записать(Ложь). Однако если изменились измерения, добавление записи с Записать(Ложь) неизбежно создаст новую комбинацию измерений в базе.

Вариант 4. Использование стандартной формы записи и КлючаЗаписи

В управляемом приложении наиболее надежным и рекомендуемым способом редактирования записей является открытие штатной формы записи регистра с передачей объекта РегистрСведенийКлючЗаписи:


// Формируем структуру ключа записи по текущей строке таблицы
ПараметрыКлюча = Новый Структура;
ПараметрыКлюча.Вставить("Период", ТекущаяСтрока.Период);
ПараметрыКлюча.Вставить("Оборудование", ТекущаяСтрока.Оборудование);
ПараметрыКлюча.Вставить("АдресДоступа", ТекущаяСтрока.АдресДоступа);
ПараметрыКлюча.Вставить("ВидДоступа", ТекущаяСтрока.ВидДоступа);

КлючЗаписи = Новый("РегистрСведенийКлючЗаписи.СведенияОДоступах", Новый Массив, ПараметрыКлюча);

ПараметрыОткрытия = Новый Структура("Ключ", КлючЗаписи);
ОткрытьФорму("РегистрСведений.СведенияОДоступах.ФормаЗаписи", ПараметрыОткрытия, ЭтаФорма);

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

← На главную