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

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

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

Проблема "двухэтапной" записи

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

  1. Удаление: Платформа удаляет все существующие записи в базе данных, которые соответствуют установленному фильтру (свойство Отбор набора записей).
  2. Запись: Платформа записывает новые данные, содержащиеся в коллекции набора.

Из-за этого свойства ДополнительныеСвойства или переменные модуля, которые вы пытаетесь инициализировать на этапе "удаления" или предварительных проверок, могут вести себя непредсказуемо, а сама попытка отловить момент "изменения" превращается в детектив. Давайте проанализируем основные способы решения этой задачи.

Способ 1. Сравнение данных в обработчике ПередЗаписью

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

Рассмотрим алгоритм действий:

  1. В обработчике ПередЗаписью проанализируем свойство Отбор.
  2. Сделаем запрос к регистру с этим отбором, чтобы получить текущие данные из БД (состояние "До").
  3. Сравним полученную таблицу с текущей коллекцией набора (состояние "После").
  4. Если в БД записи были, а в наборе их нет — это удаление.
  5. Если в БД записей не было, а в наборе они появились — это добавление.
  6. Если записи есть и там, и там — сравниваем ресурсы и реквизиты на предмет изменения.

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


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

    // 1. Получаем текущие данные из БД по отбору набора
    Запрос = Новый Запрос;
    ТекстЗапроса = "ВЫБРАТЬ * ИЗ РегистрСведений.ИмяВашегоРегистра ГДЕ ";
    
    ЛогикаОтбора = "";
    Для Каждого ЭлементОтбора Из Отбор Цикл
        Если ЭлементОтбора.Использование Тогда
            ИмяПоля = ЭлементОтбора.Имя;
            ТекстЗапроса = ТекстЗапроса + ИмяПоля + " = &" + ИмяПоля + " И ";
            Запрос.УстановитьПараметр(ИмяПоля, ЭлементОтбора.Значение);
        КонецЕсли;
    КонецЦикла;
    
    // Отрезаем лишний " И "
    ТекстЗапроса = Лев(ТекстЗапроса, СтрДлина(ТекстЗапроса) - 3);
    Запрос.Текст = ТекстЗапроса;
    
    ТаблицаДо = Запрос.Выполнить().Выгрузить();
    ТаблицаПосле = ЭтотОбъект.Выгрузить();

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

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

Способ 2. Использование механизма "История данных"

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

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

  1. Откройте свойства регистра в конфигураторе.
  2. Перейдите на закладку Данные.
  3. Установите свойство История данных в значение "Использовать".
  4. Для каждого измерения, ресурса или реквизита, который нужно отслеживать, также проверьте свойство "История данных".

Важный нюанс: Как отмечалось в обсуждении, иногда при включении истории данных может возникнуть ошибка, связанная с РазделениеДанных. Это происходит в облачных конфигурациях или при использовании механизмов разделения. Убедитесь, что ваш регистр корректно соотносится с общими реквизитами-разделителями. Главный плюс этого метода — наличие стандартных отчетов для просмотра истории изменений и программного интерфейса для получения версий.

Способ 3. Версионирование из БСП

Если ваша конфигурация построена на базе Библиотеки Стандартных Подсистем (БСП), рекомендуется использовать подсистему "Версионирование объектов".

Преимущества БСП:

Для независимых регистров БСП использует подписки на события или вызовы в модуле набора записей. При записи система сериализует текущий набор и сравнивает его с предыдущим сохраненным снимком. Если вы планируете использовать этот метод, проверьте, включен ли ваш регистр в состав определяемого типа ОбъектВерсионирования.

Способ 4. Определение удаления по модифицированности

Проанализируем интересный технический прием, упомянутый в сообщении 20. Если в обработчике ПередЗаписью вы обнаружили, что коллекция записей пуста (ЭтотОбъект.Количество() = 0), а метод ЭтотОбъект.Модифицированность() возвращает Истина, это с высокой долей вероятности означает, что происходит удаление данных (очистка набора по отбору).

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

Способ 5. Архитектурное решение "Машина времени"

В случаях, когда аудит критически важен (например, финансовые показатели), применяется подход, при котором записи в регистре никогда не удаляются физически. Рассмотрим, как это реализовать:

  1. В структуру регистра добавляется новое измерение НомерВерсии (тип Число) или ДатаАктуальности.
  2. При любой правке старая запись остается в базе, а записывается новая с увеличенным номером версии.
  3. Для получения актуальных данных всегда используется запрос с условием на максимальный номер версии или срез последних (если регистр периодический).

Этот метод позволяет "отмотать" состояние регистра на любую точку в прошлом без восстановления бэкапов, но требует переписывания всех запросов, работающих с этим регистром, так как теперь нужно учитывать версии. Для организации подобного хронологического хранения истории изменений можно использовать готовый механизм версионирования данных через регистры сведений.

Почему ДополнительныеСвойства могут быть пустыми?

Выясним причину проблемы, упомянутой автором темы в сообщении 4. ДополнительныеСвойства — это свойство конкретного экземпляра объекта. Если программный код создает новый объект набора записей (РегистрыСведений.Имя.СоздатьНаборЗаписей()), устанавливает отбор и записывает его, то это совершенно другой объект по сравнению с тем, который мог быть создан ранее. Данные между разными экземплярами объектов наборов записей автоматически не передаются. Если вам нужно передать информацию сквозь вызовы, используйте параметры сеанса или временные таблицы БД, хотя в большинстве случаев правильным решением будет перенос логики в сам модуль набора записей.

Резюме

Для надежного отслеживания изменений в независимом РС рекомендуется:

← На главную