В процессе разработки и сопровождения систем на базе платформы 1С:Предприятие часто возникает задача аудита действий пользователей. Одно из наиболее распространенных требований бизнеса — фиксировать, кто именно и когда в последний раз вносил изменения в документ. При этом обычные пользователи не должны иметь возможности вручную корректировать эти служебные реквизиты, так как это нарушит достоверность собираемых данных.
В этой статье мы подробно разберем, как решить эту задачу различными способами: от самых простых и быстрых решений «в лоб» до надежных и масштабируемых архитектурных подходов, а также современных системных механизмов платформы 1С.
Рассмотрим ситуацию, когда перед нами стоит задача реализовать хранение даты и автора изменений для одного конкретного документа в конфигурации. Разберем этот процесс по шагам.
Для начала нам необходимо открыть конфигуратор и добавить в структуру нашего документа два новых реквизита:
ДатаПоследнегоИзменения (тип: Дата, состав даты: Время и Дата)ПоследнийИзменивший (тип: СправочникСсылка.Пользователи)После создания реквизитов нам нужно сделать так, чтобы при любом сохранении документа система автоматически записывала в них актуальные данные. Для этого мы задействуем обработчик события модуля объекта. Перейдем в модуль объекта нашего документа и создадим процедуру ПередЗаписью. Посмотрим на пример программного кода, который решает эту задачу:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Записываем текущую дату сеанса
ЭтотОбъект.ДатаПоследнегоИзменения = ТекущаяДатаСеанса();
// Определяем текущего авторизованного пользователя
ЭтотОбъект.ПоследнийИзменивший = Пользователи.ТекущийПользователь();
КонецПроцедуры
Проанализируем ситуацию и выясним причину использования именно этих методов:
ТекущаяДатаСеанса() вместо стандартной ТекущаяДата(). Это важно, поскольку ТекущаяДатаСеанса() возвращает время с учетом часового пояса клиента, установленного в его сеансе, тогда как обычная дата вернет время сервера, которое может отличаться от локального времени пользователя.ПользователиКлиентСервер.ТекущийПользователь(). Обратите внимание, что в современных версиях Библиотеки Стандартных Подсистем (БСП) этот метод признан устаревшим. Настоятельно рекомендуется использовать метод Пользователи.ТекущийПользователь() для серверного кода или Пользователи.АвторизованныйПользователь().После реализации записи данных нам необходимо решить вторую часть задачи — закрыть пользователю возможность редактировать эти поля вручную. Разберем по шагам, как это можно сделать на уровне интерфейса.
Посмотрим на пример настройки управляемой формы документа. Перейдите в редактор формы вашего документа и перетащите реквизиты ДатаПоследнегоИзменения и ПоследнийИзменивший на форму в виде полей ввода.
Чтобы заблокировать их редактирование для пользователя, выполните следующие действия:
ДатаПоследнегоИзменения).ТолькоПросмотр в значение Истина.Если требуется управлять доступностью динамически (например, разрешать редактирование только Администраторам), мы можем прописать это программно в обработчике формы ПриСозданииНаСервере:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Проверяем роль текущего пользователя
Если Не РолиДоступны("ПолныеПрава") Тогда
Элементы.ДатаПоследнегоИзменения.ТолькоПросмотр = Истина;
Элементы.ПоследнийИзменивший.ТолькоПросмотр = Истина;
КонецЕсли;
КонецПроцедуры
Важно: Помните, что ограничение на уровне формы защищает данные только в интерфейсе этой конкретной формы. Продвинутые пользователи или программисты могут обойти этот запрет, изменив реквизиты через внешние обработки, групповое изменение реквизитов или через программный код интеграций. Чтобы защитить данные на 100%, используйте ограничение на уровне Ролей (прав доступа) в конфигураторе, убрав право Редактирование для обычных ролей и оставив им только право Просмотр.
Рассмотрим подробнее архитектурную проблему: что делать, если такое требование необходимо реализовать не для одного, а сразу для всех документов в базе данных? Создавать реквизиты в каждом документе вручную и дублировать код в каждом модуле — крайне неэффективный подход.
Выясним, как решить эту задачу изящно с помощью двух мощных механизмов платформы 1С: Общих реквизитов и Подписок на события.
Разберем этот процесс по шагам:
Шаг 1. Создание Общих реквизитов.
В ветке метаданных конфигуратора перейдите в раздел Общие -> Общие реквизиты. Добавьте два реквизита: ОбщийАвторИзменения и ОбщаяДатаИзменения. В свойствах каждого из них откройте параметр "Состав" и укажите список документов информационной системы, в которых они должны присутствовать, установив для них режим "Использовать".
Шаг 2. Создание подписки на событие.
Чтобы автоматизировать заполнение, перейдите в раздел Общие -> Подписки на события и добавьте новую подписку:
ЗаполнитьСлужебныеРеквизитыПередЗаписьюДокументОбъектПередЗаписьюПосмотрим на пример программного кода в общем модуле для автоматического заполнения общих реквизитов:
Процедура ЗаполнитьСлужебныеРеквизитыПередЗаписьюПередЗаписью(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
// Исключаем обработку при обмене данными
Если Источник.ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Заполняем общие реквизиты для любого записываемого документа
Источник.ОбщаяДатаИзменения = ТекущаяДатаСеанса();
Источник.ОбщийАвторИзменения = Пользователи.ТекущийПользователь();
КонецПроцедуры
Данный подход позволяет полностью избежать дублирования кода. Теперь при добавлении новых документов в систему вам достаточно будет просто включить их в состав общих реквизитов, и весь функционал заработает автоматически.
Проанализируем ситуацию с производительностью системы. Предложенные выше варианты обновляют дату и автора при любой перезаписи документа. Но в реальных базах данные часто перезаписываются программно без фактического изменения реквизитов документа (например, при групповом перепроведении, закрытии месяца или восстановлении последовательностей регламентными заданиями).
Если мы будем перезаписывать автора при каждом таком техническом перепроведении, мы потеряем ценную информацию о том, какой реальный пользователь последним редактировал документ вручную. Нам будет казаться, что все документы изменил "Администратор" или "Робот".
Рассмотрим подробнее пример кода, который решает эту проблему путем проверки фактического изменения ключевых полей перед обновлением истории:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если ЭтоНовый() Тогда
// Для нового документа всегда фиксируем автора создания
ДатаПоследнегоИзменения = ТекущаяДатаСеанса();
ПоследнийИзменивший = Пользователи.ТекущийПользователь();
Иначе
// Получаем текущую версию документа из базы данных для сравнения
ОбъектБД = Ссылка.ПолучитьОбъект();
// Сравниваем только значимые для бизнеса реквизиты
Если ЭтотОбъект.Контрагент <> ОбъектБД.Контрагент
Или ЭтотОбъект.СуммаДокумента <> ОбъектБД.СуммаДокумента Тогда
ДатаПоследнегоИзменения = ТекущаяДатаСеанса();
ПоследнийИзменивший = Пользователи.ТекущийПользователь();
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Такой подход защитит служебные реквизиты от перезаписи техническими транзакциями и сохранит реальную историю действий пользователей.
В современных версиях платформы 1С:Предприятие (начиная с 8.3.11) реализован мощный встроенный инструмент — История данных. Если перед вами стоит задача вести полноценный аудит изменений объектов (знать не просто последнего изменившего, а видеть всю историю изменений в разрезе версий, сравнивать их и иметь возможность откатиться к любой старой версии), то данный механизм является лучшим решением.
Выясним основные преимущества платформенной "Истории данных" перед самописными решениями и механизмами версионирования БСП на базе регистра ВерсииОбъектов:
Подводя итоги, выбор оптимального решения зависит от масштаба задачи. Для простых локальных доработок отлично подойдет обработчик ПередЗаписью в сочетании с ограничением прав на уровне ролей. Для сквозного аудита множества документов лучшим архитектурным решением станет использование общих реквизитов и подписок на события, а для создания детальной системы логирования версий целесообразно использовать платформенную "История данных".