Как лучше сохранить предыдущие записи регистра при записи или перепроведении документа?

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

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

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

Почему событие при записи регистра срабатывает трижды?

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

  1. Очистка существующих записей: Система записывает пустой набор записей с установленным отбором по регистратору, чтобы «затереть» старые данные в базе.
  2. Подготовка новых данных: В процессе выполнения модуля документа формируются новые записи.
  3. Запись новых движений: Итоговый набор данных записывается в базу данных.

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

Метод 1: Использование коллекции ДополнительныеСвойства

Проанализируем наиболее профессиональный подход, который позволяет избежать многократного чтения данных из базы и конфликтов блокировок. Мы будем использовать механизм ДополнительныеСвойства, который есть практически у каждого объекта 1С.

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

  1. Создадим подписку на событие ПередЗаписью для объекта типа РегистрНакопленияНаборЗаписей (или регистра сведений).
  2. В обработчике проверим, не сохранены ли уже данные в дополнительных свойствах.
  3. Если данных нет — прочитаем их из базы данных и сохраним «внутри» самого объекта набора записей.

Рассмотрим пример кода для такой подписки:


Процедура ПередЗаписьюНабораЗаписей(Источник, Отказ, Замещение) Экспорт
    
    // Проверяем, что это не загрузка данных
    Если Источник.ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

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

КонецПроцедуры

Теперь в событии ПриЗаписи этого же регистра вы всегда сможете обратиться к Источник.ДополнительныеСвойства.СтарыеЗаписи и сравнить их с текущим набором записей.

Метод 2: Хранение истории в «скрытых» табличных частях документа

Иногда требуется сохранить историю движений не просто в лог-файл, а «привязать» её к самому документу для быстрого восстановления. Рассмотрим вариант, когда в документе создается специальная табличная часть (например, ИсторияДвижений).

Этот способ удобен тем, что данные всегда под рукой, но он существенно увеличивает размер таблиц документов. Логика работы следующая:

Метод 3: Использование вспомогательных «Теневых» регистров

Для высоконагруженных систем лучшим решением будет создание отдельного регистра сведений (например, ЛогДвиженийРегистров). Выясним преимущества этого подхода:

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

Для записи в такой регистр лучше всего использовать подписку на событие ПриЗаписи набора записей. Проанализируем пример структуры такого регистра:

Метод 4: Стандартный механизм «История данных» (версионирование)

Начиная с версии платформы 8.3.11, в 1С доступен механизм История данных. Разберем, как его задействовать:

Для регистров сведений достаточно включить свойство «История данных» в метаданных. Платформа сама будет создавать версии при каждом изменении записей. Однако для регистров накопления этот механизм напрямую не работает, так как они тесно связаны с регистратором. В случае регистров накопления версионировать нужно сам Документ, а движения восстанавливать программно или логировать их отдельно через события документа.

Практические рекомендации

Выбирая способ сохранения данных, следуйте этим советам:

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

← На главную