Как в 1С 8.3 отследить отказ пользователя от сохранения или удаления документа

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

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

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

Решение 1: Отслеживание отказа при удалении в списках

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

Для полного контроля над ситуацией проанализируем подход с подменой стандартной команды удаления. Разберем по шагам, как это реализовать:

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

Посмотрим на пример кода, который позволяет зафиксировать ответ пользователя:


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

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

Этот метод позволяет нам четко разграничить состояния: фактическое удаление и осознанный отказ пользователя от действия.

Решение 2: Отслеживание отказа от сохранения изменений

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

Для реализации проверки проанализируем состояние формы в событии ПередЗакрытием. Основным индикатором здесь выступает свойство Модифицированность. Если оно равно Истина, значит, данные в форме были изменены относительно тех, что хранятся в базе.

Рассмотрим алгоритм действий в модуле управляемой формы:


&НаКлиенте
Процедура ПередЗакрытием(Отказ, ЗавершениеРаботы, ТекстПредупреждения, СтандартнаяОбработка)
    
    Если Модифицированность И Не ЭтаФорма.Записывается Тогда
        // Данные изменены, но запись не инициирована (пользователь нажал "Закресть" или "Esc")
        СтандартнаяОбработка = Ложь;
        Отказ = Истина; // Прерываем стандартное закрытие, чтобы задать свой вопрос
        
        Оповещение = Новый ОписаниеОповещения("ПослеВопросаОЗакрытии", ЭтотОбъект);
        ТекстВопроса = "Данные были изменены. Сохранить изменения?";
        ПоказатьВопрос(Оповещение, ТекстВопроса, РежимДиалогаВопрос.ДаНетОтмена);
    КонецЕсли;
    
КонецПроцедуры

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

Важно помнить: если пользователь просто открыл документ и закрыл его, не меняя ни одного поля, свойство Модифицированность останется Ложь, и событие не будет расценено как отказ от обновления.

Решение 3: Передача статуса между формами (Callback)

Часто требуется, чтобы форма списка «узнала», чем закончилось редактирование в форме документа. Например, если форма документа была открыта программно методом ОткрытьФорму. Для этого в 1С используется механизм Описания оповещения.

Проанализируем ситуацию: в форме списка мы вызываем создание нового документа и хотим получить отклик. В форме списка пишем:


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

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

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


&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
    РезультатЗакрытия = Новый Структура("Статус", "УспешноЗаписано");
    ОповеститьОЗакрытии(РезультатЗакрытия);
КонецПроцедуры

Решение 4: Создание собственного журнала аудита

Поскольку штатный журнал регистрации не подходит для хранения «намерений», рекомендуется создать непериодический Регистр сведений (например, ЛогДействийПользователей). В него следует записывать следующие данные:

Запись в такой регистр должна выполняться в привилегированном модуле, чтобы даже при отсутствии прав на сам регистр у пользователя, система могла зафиксировать его действия. Рассмотрим пример функции для записи:


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

Нюансы при копировании документов

Разберем ситуацию с копированием. Когда пользователь нажимает «Скопировать», в модуле объекта срабатывает событие ПриКопировании. Объект в памяти уже создан, но в базе данных его еще нет. Если пользователь закроет форму этого нового документа, не нажав «Записать», он фактически откажется от создания копии.

Чтобы отловить этот момент, в событии ПередЗакрытием формы нового документа проверьте:

  1. Является ли объект новым (Объект.Ссылка.Пустая()).
  2. Был ли он создан на основании копирования (можно передать параметр при открытии или проверить заполнение специфических реквизитов).

Важный момент: Помните о производительности. Избыточное логгирование каждого «чиха» пользователя может существенно замедлить работу системы и раздуть размер базы данных. Используйте эти методы только для критически важных справочников и документов, где риск злоупотреблений наиболее высок.

← На главную