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