Как реализовать обязательный ввод причины при пометке документа на удаление в 1С 8.3

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

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

Анализ проблемы и выбор стратегии реализации

Прежде чем приступать к кодингу, проанализируем поведение платформы. Основная сложность заключается в том, что в управляемом приложении код четко разделен на клиентскую и серверную части. Пометка удаления — это по сути запись объекта с установленным признаком ПометкаУдаления = Истина.

Разберем, почему стандартные события нам не всегда подходят:

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

Следовательно, нам необходимо комбинированное решение: запрет «неправильной» записи на сервере и организация удобного интерфейса на клиенте.

Шаг 1. Блокировка записи без причины на уровне объекта

Первым делом обеспечим безопасность на уровне бизнес-логики. Мы должны гарантировать, что документ не будет помечен на удаление никаким способом (через список, из формы или программно), если не передана причина. Для этого будем использовать механизм ДополнительныеСвойства.

Откроем модуль объекта документа и добавим обработчик события ПередЗаписью:


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

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

Шаг 2. Изменение интерфейса формы списка

Теперь перейдем к визуальной части. В форме списка документов нам нужно перехватить стандартное действие пользователя. Рассмотрим, как это сделать наиболее корректно.

  1. Откроем форму списка документа.
  2. В свойствах командной панели или самого динамического списка найдем стандартную команду Пометить на удаление / Снять пометку.
  3. Нам необходимо либо скрыть стандартную кнопку и добавить свою, либо (что более гибко) подменить действие команды.

Разберем вариант с созданием собственной команды КомандаПометитьНаУдаление. В обработчике этой команды на клиенте мы должны реализовать следующий алгоритм:


&НаКлиенте
Асинхрон Процедура КомандаПометитьНаУдаление(Команда)
	
	ТекущиеДанные = Элементы.Список.ТекущиеДанные;
	Если ТекущиеДанные = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	// Если пометка уже стоит, просто снимаем её (причина обычно не нужна)
	Если ТекущиеДанные.ПометкаУдаления Тогда
		УстановитьПометкуНаСервере(ТекущиеДанные.Ссылка, Ложь, "");
		Возврат;
	КонецЕсли;
	
	// Если пометки нет — запрашиваем ввод строки
	ОписаниеОповещения = Новый ОписаниеОповещения("ПослеВводаПричины", ЭтотОбъект);
	ПоказатьВводСтроки(ОписаниеОповещения, "", "Укажите причину удаления документа", 200);
	
КонецПроцедуры

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

Шаг 3. Обработка записи на сервере

Выясним, как правильно передать введенную причину с клиента на сервер, чтобы сработал наш предохранитель из Шага 1. Для этого создадим серверную процедуру в модуле формы:


&НаСервереБезКонтекста
Процедура УстановитьПометкуНаСервере(СсылкаНаОбъект, ЗначениеПометки, ТекстПричины)
	
	ДокументОбъект = СсылкаНаОбъект.ПолучитьОбъект();
	ДокументОбъект.ПометкаУдаления = ЗначениеПометки;
	
	// Заполняем ДополнительныеСвойства перед вызовом Записать()
	ДокументОбъект.ДополнительныеСвойства.Вставить("ПричинаУдаленияУказана", Истина);
	ДокументОбъект.ДополнительныеСвойства.Вставить("ПричинаУдаления", ТекстПричины);
	
	Попытка
		ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
	Исключение
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

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

Универсальный подход через расширение

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

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

Важный нюанс: При групповой пометке удаления в динамическом списке (когда пользователь выделил несколько строк) стандартный механизм платформы отработает без ваших диалогов. Чтобы этого избежать, в кастомной команде обязательно проверяйте массив Элементы.Список.ВыделенныеСтроки и организуйте цикл обработки каждого документа с выводом одного общего окна для ввода причины.

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

← На главную