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