Как изменить период записей в модуле набора записей периодического регистра сведений в 1С?

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

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

Разберем по шагам, почему возникает эта ошибка, как устроена внутренняя механика записи регистров и как правильно реализовать изменение периода.

Почему платформа не разрешает менять период строки напрямую

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

Если мы попытаемся просто переопределить поле строки через СтрокаНабора.Период = НовыйПериод, возникнет системное исключение, поскольку значение в строке перестанет совпадать с установленным отбором набора.

Особенности работы объекта «МенеджерЗаписи» и двухфазная запись

При интерактивном редактировании записи в форме или при программной работе через РегистрСведенийМенеджерЗаписи платформа не выполняет операцию обновления (UPDATE) по первичному ключу. Вместо этого в рамках единой транзакции выполняются две последовательные фазы:

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

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

Корректное изменение периода в обработчике «ПередЗаписью»

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

  1. Проверяем количество строк в наборе через ЭтотОбъект.Количество(). Если строк 0, значит, выполняется системное удаление старой записи — в этом случае отбор модифицировать нельзя.
  2. Если количество строк больше 0, мы находимся на фазе записи новых данных. Здесь необходимо одновременно изменить и значение отбора, и значение поля Период во всех строках набора.

Посмотрим на пример реализации в модуле набора записей:


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

Программное изменение периода через чтение и перезапись

Если задача выполняется во внешней обработке, общем модуле или процедуре менеджера, наиболее надежный и прозрачный способ — прочитать существующую запись, удалить ее и записать новую с требуемым периодом. Рассмотрим пример с использованием объекта РегистрСведенийМенеджерЗаписи:


Менеджер = РегистрыСведений.КурсыВалют.СоздатьМенеджерЗаписи();
Менеджер.Период = СтарыйПериод;
Менеджер.Валюта = ВыбраннаяВалюта;

// Обязательно считываем существующую запись
Менеджер.Прочитать();

Если Менеджер.Выбран() Тогда
    // Удаляем старую запись из базы
    Менеджер.Удалить();
    
    // Формируем новую запись с измененным периодом
    Менеджер.Период = НовыйПериод;
    Менеджер.Записать();
КонецЕсли;

Предотвращение бесконечной рекурсии

Если внутри обработчиков модуля набора записей требуется создать вспомогательный набор или менеджер записи для того же регистра сведений, существует риск попадания в бесконечную рекурсию. Для ее предотвращения используем свойство ДополнительныеСвойства:


// В вызывающем коде устанавливаем флаг
Набор.ДополнительныеСвойства.Вставить("КорректировкаПериодаВыполнена", Истина);

// В начале процедуры ПередЗаписью модуля набора проверяем наличие флага
Если ЭтотОбъект.ДополнительныеСвойства.Свойство("КорректировкаПериодаВыполнена") Тогда
    Возврат;
КонецЕсли;

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

← На главную