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