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