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