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

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

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

Способ 1. Обработка событий в модуле набора записей (ПередЗаписью и ПриЗаписи)

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

Проанализируем базовый вариант проверки факта удаления в процедуре ПередЗаписью модуля набора записей:


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

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

Однако здесь важно учитывать важную особенность: при интерактивном редактировании или при работе через МенеджерЗаписи платформа может сначала записать пустой набор для очистки старой строки, а затем вторым шагом записать набор с новыми данными. Чтобы точно отличить окончательное удаление от промежуточной перезаписи или избежать дублирования действий, разберем связку событий ПередЗаписью и ПриЗаписи с передачей флага через ДополнительныеСвойства:


Процедура ПередЗаписью(Отказ, Замещение)

    Если Замещение И ЭтотОбъект.Количество() = 0 Тогда
        // Запоминаем намерение удалить данные
        ЭтотОбъект.ДополнительныеСвойства.Вставить("ВыполняетсяУдаление", Истина);
        
        // Получаем данные удаляемых записей до их физического удаления из базы
        // Накладываем фильтр по установленным отборам набора
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ
            |   Т.ПрикрепленныйФайл КАК ПрикрепленныйФайл
            |ИЗ
            |   РегистрСведений.Сообщения КАК Т
            |ГДЕ
            |   Т.Идентификатор = &Идентификатор";
            
        Если ЭтотОбъект.Отбор.Идентификатор.Использование Тогда
            Запрос.УстановитьПараметр("Идентификатор", ЭтотОбъект.Отбор.Идентификатор.Значение);
            Результат = Запрос.Выполнить().Выгрузить();
            ЭтотОбъект.ДополнительныеСвойства.Вставить("УдаляемыеФайлы", Результат);
        КонецЕсли;
    КонецЕсли;

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

Процедура ПриЗаписи(Замещение)

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

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

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

Способ 2. Перехват интерактивного удаления в форме списка регистра

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

Разберем реализацию этого подхода по шагам:

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

Посмотрим на пример программного кода формы списка:


&НаКлиенте
Перем МассивФайловКУдалению;

&НаКлиенте
Процедура СписокПередУдалением(Элемент, Отказ)
    
    МассивСтр = Элемент.ВыделенныеСтроки;
    МассивФайловКУдалению = СписокПередУдалениемНаСервере(МассивСтр);
    
КонецПроцедуры

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

&НаКлиенте
Процедура СписокПослеУдаления(Элемент)
    
    Если МассивФайловКУдалению <> Неопределено И МассивФайловКУдалению.Количество() > 0 Тогда
        СписокПослеУдаленияНаСервере(МассивФайловКУдалению);
        МассивФайловКУдалению = Неопределено;
    КонецЕсли;
    
КонецПроцедуры

&НаСервереБезКонтекста
Процедура СписокПослеУдаленияНаСервере(МассивФайлов)
    
    Для Каждого СсылкаНаФайл Из МассивФайлов Цикл
        Попытка
            ОбъектФайла = СсылкаНаФайл.ПолучитьОбъект();
            Если ОбъектФайла <> Неопределено Тогда
                ОбъектФайла.Удалить();
            КонецЕсли;
        Исключение
            // Обработка ошибок удаления связанного объекта
        КонецПопытки;
    КонецЦикла;
    
КонецПроцедуры

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

Важные архитектурные нюансы и ограничения

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

  1. Транзакционность и внешние действия: Обработчики ПередЗаписью и ПриЗаписи модуля набора записей выполняются внутри общей транзакции СУБД. Если внутри этих событий инициируется необратимое внешнее действие (удаление физического файла с диска, отправка HTTP-запроса), при последующей ошибке или отказе записи транзакция базы данных откатится, а внешнее действие вернуть не удастся. Для критичных внешних операций надежнее использовать отложенную очистку по расписанию через регламентные задания.
  2. Групповые отборы: Запись пустого набора может производиться без установки значений по всем измерениям сразу (например, очистка среза данных или полная очистка регистра). Алгоритм поиска удаляемых данных должен проверять свойство Использование у каждого элемента отбора, чтобы не вызвать исключение при формировании запроса.
  3. Ведущие измерения: Если измерение регистра имеет признак «Ведущее», платформа автоматически очищает записи при удалении ссылочного объекта. При каскадном удалении помеченных объектов значение ведущего измерения в наборе может указывать на уже удаленный объект (битую ссылку). В таких сценариях сопутствующие действия надежнее размещать в обработчике ПередУдалением самого объекта-владельца через объектные подписки на события.
  4. Подписки на события: Чтобы не вносить изменения в типовой модуль регистра сведений, отслеживание удаления пустого набора записей можно вынести в объект метаданных ПодпискаНаСобытие с источником РегистрСведенийНаборЗаписей.<ИмяРегистра>.
← На главную