Как запретить удаление элемента справочника по условию в расширении 1С?

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

При разработке в среде 1С:Предприятие 8.3 программисты часто используют механизм расширений для добавления новой функциональности без изменения типовой конфигурации. Однако при работе с собственными объектами, созданными непосредственно внутри расширения, могут возникать неочевидные проблемы. Одна из типичных задач — запретить удаление или пометку на удаление определенных («избранных») элементов справочника по заданному условию. Давайте подробно проанализируем, почему стандартные подходы могут не работать, и разберем корректные способы реализации этой задачи.

Выясняем причины: почему не работал код у авторов темы?

Для начала проанализируем технические ошибки, с которыми столкнулись участники обсуждения в исходной теме. Это поможет нам избежать подобных проблем в будущем.

Ошибка 1. Использование аннотаций перехвата для собственных объектов.
В попытках решить проблему разработчики часто используют директивы &Перед, &После или &Вместо в модуле объекта (например, &Перед("ПриЗаписи")). Это в корне неверно для объектов, которые созданы внутри самого расширения. Указанные аннотации предназначены исключительно для перехвата методов и событий тех объектов, которые физически существуют в основной конфигурации. Если справочник создан «с нуля» внутри расширения, то модуль этого справочника является для него стандартным («родным»). В нем нужно писать обычные процедуры без каких-либо аннотаций перехвата.

Ошибка 2. Отсутствие привязки обработчика к событию в свойствах.
Простое объявление процедуры с правильным именем (например, Процедура ПередЗаписью(Отказ)) в модуле объекта не гарантирует её выполнение. Платформа 1С должна знать, что эта процедура является обработчиком конкретного события. Для этого необходимо открыть палитру свойств справочника, перейти на вкладку «События», найти нужное событие (например, ПередЗаписью или ПередУдалением) и нажать на лупу, чтобы платформа автоматически создала связь между событием и кодом в модуле.

Ошибка 3. Путаница между физическим удалением и установкой пометки на удаление.
В системе 1С пользователи практически никогда не удаляют данные физически одним нажатием клавиши. Кнопка «Удалить» или клавиша Del в интерфейсе запускает процесс логического удаления — установку пометки на удаление (изменение реквизита ПометкаУдаления в значение Истина и последующую запись объекта). Физическое же удаление происходит позже, при запуске регламентной обработки «Удаление помеченных объектов».

Следовательно:

---

Вариант 1. Защита на уровне базы данных (Модуль объекта собственного справочника)

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

Шаг 1. Перехват установки пометки на удаление

Откроем модуль объекта нашего справочника и напишем обработчик события ПередЗаписью. Нам нужно отследить момент, когда у объекта меняется статус пометки на удаление с «Ложь» на «Истина».


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

В этом коде конструкция Новый() Или Не Ссылка.ПометкаУдаления крайне важна. Она позволяет определить, что пометка на удаление устанавливается именно сейчас. Если элемент уже был помечен на удаление ранее, и мы просто перезаписываем другие его реквизиты, проверка не заблокирует запись.

Шаг 2. Предотвращение физического удаления

Если элемент все-таки был каким-то образом помечен на удаление (например, до внедрения нашей защиты), мы должны гарантировать, что стандартная обработка удаления помеченных объектов не сможет стереть его из базы данных. Для этого добавим обработчик события ПередУдалением:


Процедура ПередУдалением(Отказ)
    
    // Предотвращаем окончательное физическое удаление элемента из базы данных
    Если ЭтоИзбранныйЭлемент(Ссылка) Тогда
        Отказ = Истина;
        ВызватьИсключение "Данный элемент справочника защищен от физического удаления!";
    КонецЕсли;
    
КонецПроцедуры

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

---

Вариант 2. Использование Подписок на события в расширении

Проанализируем альтернативный и более гибкий архитектурный подход. Если вы не хотите писать код напрямую в модуле объекта справочника, в платформе 1С предусмотрен мощный механизм — Подписки на события. Мы можем создать подписку прямо внутри нашего расширения. Это особенно удобно, если логику проверки нужно будет в дальнейшем применить к нескольким справочникам.

Разберем процесс создания подписки по шагам:

  1. В дереве объектов вашего расширения найдите ветку Общие -> Подписки на события и добавьте новую подписку.
  2. Дайте ей понятное имя, например, Расш1_ЗапретитьУдалениеИзбранныхЭлементов.
  3. В свойстве Источник выберите объект вашего справочника: СправочникОбъект.ИмяВашегоСправочника.
  4. В свойстве Событие выберите значение: ПередЗаписью.
  5. В свойстве Обработчик укажите процедуру в общем модуле вашего расширения (общий модуль должен быть серверным, например, с установленным флагом «Сервер»).

Код обработчика в общем модуле расширения будет выглядеть следующим образом:


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

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

---

Вариант 3. Улучшение интерфейса (Запрет на форме списка)

Если мы ограничимся только проверками на уровне базы данных (Вариант 1 или 2), то при попытке пометить элемент на удаление в форме списка пользователь сначала увидит стандартное системное окно: «Объект будет помечен на удаление. Продолжить?». Пользователь нажмет «Да», и только после этого система попытается записать объект, наткнется на наш отказ в транзакции и выведет ошибку. Это создает лишние действия для пользователя.

Чтобы повысить юзабилити системы, мы можем перехватить действие удаления еще на форме списка справочника, до выдачи стандартных предупреждений системы.

Откроем форму списка нашего справочника в расширении и для таблицы Список добавим обработчик события ПередУдалением:


&НаКлиенте
Процедура СписокПередУдалением(Элемент, Отказ)
    
    ТекущиеДанные = Элементы.Список.ТекущиеДанные;
    Если ТекущиеДанные <> Неопределено Тогда
        
        // Проверяем условие «избранного» элемента на сервере
        Если ПроверитьЭлементНаСервере(ТекущиеДанные.Ссылка) Тогда 
            Отказ = Истина; // Блокируем вызов стандартного системного окна
            ПоказатьПредупреждение(, "Этот элемент справочника защищен от удаления на уровне интерфейса!");
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПроверитьЭлементНаСервере(СсылкаНаЭлемент)
    
    // Здесь вызывается ваша функция проверки условий
    Возврат ЭтоИзбранныйЭлемент(СсылкаНаЭлемент);
    
КонецФункции

Важное архитектурное правило разработки в 1С: Интерфейсный запрет (на форме) всегда должен выступать лишь как дополнение к серверному запрету (в модуле объекта или подписке). Пользователь может обойти форму списка и попытаться пометить элемент на удаление программным кодом, через обработку «Групповое изменение реквизитов» или посредством веб-сервисов. Поэтому надежная защита строится исключительно на сочетании Варианта 1 (или 2) и Варианта 3.

← На главную