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