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