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