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