В практике разработки на платформе 1С:Предприятие часто возникает задача: необходимо выполнить определенные действия (например, зарегистрировать изменения в Плане обмена) при записи движений документа в регистр накопления, но при этом проигнорировать эти действия, если движения очищаются вследствие физического удаления документа. Особую сложность представляет использование метода УдалитьОбъекты(МассивСсылок, Ложь), который выполняет непосредственное удаление записей из базы данных без вызова объектных событий документа.
В этой статье мы подробно разберем, почему стандартные механизмы не всегда срабатывают, и рассмотрим эффективные способы решения этой задачи, основываясь на опыте практикующих разработчиков.
При вызове метода УдалитьОбъекты с параметром ПроверятьСсылочнуюЦелостность = Ложь платформа выполняет прямое удаление записи документа из соответствующей таблицы БД. Однако для обеспечения логической целостности данных система должна очистить все связанные движения по регистрам. Платформа делает это автоматически: она записывает пустой набор записей для каждого регистратора, который подвергается удалению. Именно этот процесс инициирует выполнение подписки на событие ПриЗаписи для набора записей регистра накопления. Источником в данном случае выступает объект РегистрНакопленияНаборЗаписей.
Проблема заключается в том, что в этот момент мы не можем привычными способами понять контекст вызова. Объект самого документа уже недоступен, а дополнительные свойства в него передать невозможно, так как метод УдалитьОбъекты оперирует ссылками, а не объектами.
Разберем наиболее стабильный и универсальный метод, который подходит для серверного кода, например, при закрытии кассовой смены в типовых конфигурациях (УТ 10.3, Розница). Суть метода заключается в создании глобального «флага» на уровне сеанса, который будет виден внутри транзакции удаления.
Шаг 1. Создание параметра сеанса
В конфигураторе необходимо добавить новый ПараметрСеанса, например, с именем РежимУдаленияЧековККМ и типом Булево.
Шаг 2. Установка флага перед удалением
В модуле, где инициируется удаление объектов (например, в процедуре закрытия смены), необходимо установить этот параметр в значение Истина непосредственно перед вызовом процедуры удаления.
// Устанавливаем флаг режима удаления
ПараметрыСеанса.РежимУдаленияЧековККМ = Истина;
Попытка
// Выполняем физическое удаление массива ссылок
УдалитьОбъекты(МассивЧековККМ, Ложь);
Исключение
// Сбрасываем флаг в случае ошибки
ПараметрыСеанса.РежимУдаленияЧековККМ = Ложь;
ВызватьИсключение;
КонецПопытки;
// После успешного завершения возвращаем флаг в исходное состояние
ПараметрыСеанса.РежимУдаленияЧековККМ = Ложь;
Шаг 3. Проверка флага в подписке на событие
Теперь в обработчике подписки на событие ПриЗаписи регистра накопления мы можем проанализировать этот флаг. Если он установлен, мы понимаем, что идет процесс массового удаления, и можем пропустить регистрацию в планах обмена или другие тяжелые алгоритмы.
Процедура ПриЗаписиРегистраНакопленияДляОбмена(Источник, Отказ) Экспорт
// Если установлен флаг удаления, выходим из процедуры
Если ПараметрыСеанса.РежимУдаленияЧековККМ Тогда
Возврат;
КонецЕсли;
// Далее идет стандартная логика регистрации изменений
// ...
КонецПроцедуры
Если по каким-то причинам вы не хотите изменять состав метаданных (добавлять параметры сеанса), можно воспользоваться методом «упреждающей записи». Этот способ основан на том, что мы сами записываем пустой набор записей до того, как это сделает платформа при удалении, и передаем в этот набор нужные нам признаки.
Рассмотрим алгоритм действий:
ДополнительныеСвойства набора добавляем флаг, сообщающий подписке, что регистрация не требуется.УдалитьОбъекты.Пример реализации кода:
Для Каждого СсылкаИзМассива Из МассивСсылок Цикл
// Создаем набор записей по конкретному регистратору
Набор = РегистрыНакопления.Продажи.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(СсылкаИзМассива);
// Добавляем маркер для подписки
Набор.ДополнительныеСвойства.Вставить("НеРегистрироватьВПланеОбмена", Истина);
// Записываем пустой набор (очищаем движения вручную)
Набор.Записать(Истина);
КонецЦикла;
// Теперь вызываем физическое удаление - подписка больше не сработает
// (или сработает для пустого набора без флага, но данных уже нет)
УдалитьОбъекты(МассивСсылок, Ложь);
В обработчике подписки код будет выглядеть так:
Если Источник.ДополнительныеСвойства.Свойство("НеРегистрироватьВПланеОбмена") Тогда
Возврат;
КонецЕсли;
Важное примечание: Этот метод надежен, но может существенно замедлить работу системы при большом количестве удаляемых объектов, так как требует отдельной транзакции записи на каждый документ в цикле.
Проанализируем еще один технический нюанс. В момент срабатывания подписки ПриЗаписи при физическом удалении документа, сам документ в базе данных может уже отсутствовать или находиться в состоянии, когда ПолучитьОбъект() возвращает Неопределено. Мы можем попытаться использовать это свойство системы.
РегистраторСсылка = Источник.Отбор.Регистратор.Значение;
Если РегистраторСсылка.ПолучитьОбъект() = Неопределено Тогда
// Вероятно, объект удаляется методом УдалитьОбъекты()
// Внимание: метод ресурсоемкий и зависит от настроек транзакций
КонецЕсли;
Однако помните, что вызов ПолучитьОбъект() — это тяжелая операция для базы данных, особенно если она выполняется внутри подписки на массово записываемый регистр. Поэтому использование Параметров сеанса остается наиболее производительным и "чистым" способом реализации данной задачи.
Подведем итоги нашего анализа. Выбор метода зависит от архитектуры вашей конфигурации:
ОбменДанными.Загрузка. Хотя УдалитьОбъекты не устанавливает его автоматически, при программных манипуляциях это хороший тон — устанавливать этот флаг в Истина для обхода лишних проверок.Правильное управление контекстом удаления позволяет избежать "мусорных" записей в таблицах регистрации изменений и значительно повысить стабильность обменов данными в крупных информационных системах.