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

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

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

Особенности физического удаления записей из регистра сведений

Для понимания того, как перехватить удаление, проанализируем внутренний механизм работы платформы с регистрами сведений. В отличие от ссылочных типов, запись в регистр сведений всегда оперирует объектом РегистрСведенийНаборЗаписей. Любая модификация данных происходит по следующей схеме:

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

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

Способ 1. Использование подписки на событие «ПередЗаписью» набора записей

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

  1. В дереве метаданных добавим новую подписку на событие.
  2. В свойстве Источник выберем РегистрСведенийНаборЗаписей.<ИмяРегистра> (или укажем конкретный независимый регистр).
  3. В свойстве Событие укажем ПередЗаписью.
  4. Назначим обработчик в общем серверном модуле.

Важнейший нюанс при обменах данными: во время выполнения обмена наборы записей обычно записываются с установленным свойством ОбменДанными.Загрузка = Истина. Если в обработчике подписки разместить стандартное условие прерывания Если Источник.ОбменДанными.Загрузка Тогда Возврат; КонецЕсли;, код логирования при обмене попросту не сработает. Чтобы зафиксировать удаление в процессе синхронизации, это условие необходимо опустить.

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


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

Процедура ЗафиксироватьУдалениеВЖурнале(Набор, Причина)
    
    ОписаниеОтбора = "";
    Для Каждого ЭлементОтбора Из Набор.Отбор Цикл
        Если ЭлементОтбора.Использование Тогда
            ОписаниеОтбора = ОписаниеОтбора + ЭлементОтбора.Имя + " = " + Строка(ЭлементОтбора.Значение) + "; ";
        КонецЕсли;
    КонецЦикла;
    
    ТекстСообщения = СтрШаблон(
        "Зафиксировано удаление! Регистр: %1. Причина: %2. Отбор: [%3]. Стек вызовов: %4",
        Набор.Метаданные().Имя,
        Причина,
        ОписаниеОтбора,
        ПолучитьСтекВызовов()
    );
    
    ЗаписьЖурналаРегистрации(
        "КонтрольУдаленияРегистров",
        УровеньЖурналаРегистрации.Предупреждение,
        Набор.Метаданные(),
        ,
        ТекстСообщения
    );
    
КонецПроцедуры

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

Способ 2. Отслеживание через Технологический журнал (ТЖ)

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

Посмотрим на пример конфигурационного файла logcfg.xml:


<config xmlns="http://v8.1c.ru/v8/tech-log">
    <log location="C:\Logs\1C_Delete_Log" history="24">
        <event>
            <eq property="name" value="DBMSSQL"/>
            <like property="sql" value="%DELETE FROM _InfoRg%"/>
        </event>
        <property name="all"/>
        <property name="sql"/>
        <property name="Context"/>
        <property name="p:processName"/>
        <property name="Usr"/>
    </log>
</config>

Чтобы отфильтровать нужный регистр, определим имя его физической таблицы в СУБД с помощью вызова функции ПолучитьСтруктуруХраненияБазыДанных(). В лог технологического журнала попадут не только параметры удаленных строк, но и свойство Context (контекст встроенного языка), а также системное имя пользователя (Usr).

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

В современных версиях платформы 1С:Предприятие 8.3 для независимых регистров сведений предусмотрен встроенный механизм версионирования. Разберем порядок его включения:

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

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

← На главную