Как вернуть старое значение реквизита формы при отмене записи элемента справочника

Программист 1С v8.3 (Управляемые формы)
← На главную

В процессе разработки на платформе 1С часто возникает ситуация, когда необходимо запретить редактирование критически важных реквизитов (например, базовой единицы измерения в справочнике Номенклатура), если по объекту уже есть движения в регистрах. Стандартный подход — проверка в модуле объекта в процедуре ПередЗаписью с установкой параметра Отказ = Истина. Однако при интерактивной работе пользователь сталкивается с проблемой: запись не произошла, появилось сообщение об ошибке, но в поле на форме осталось то «новое» значение, которое он пытался ввести. Это вводит в заблуждение и требует ручного обновления формы.

В этой статье мы подробно разберем, как автоматизировать возврат старого значения реквизита из базы данных в интерфейс формы после отмены записи.

Почему проверка только в модуле объекта недостаточна для UI

Проанализируем ситуацию: когда мы пишем код в модуле объекта, мы обеспечиваем целостность данных на уровне базы. Это критически важно, так как объект может записываться не только из формы, но и программно (обработками, через COM-соединение или веб-сервисы). Однако модуль объекта «ничего не знает» о форме. Когда выполнение кода доходит до Отказ = Истина, транзакция записи прерывается, но данные в реквизитах формы (которые связаны с объектом) уже изменены пользователем. Платформа не откатывает состояние элементов управления автоматически до состояния «как было в базе».

Решение 1: Использование метода Прочитать() в модуле формы

Самый простой и эффективный способ вернуть все данные в исходное состояние — это метод Прочитать(). Он принудительно считывает состояние объекта из базы данных и обновляет связанные с ним реквизиты формы. Разберем по шагам, как это реализовать.

Проблема заключается в том, что если мы установим Отказ = Истина в серверном событии ПередЗаписьюНаСервере, мы не сможем просто так вызвать Прочитать(), так как управление должно вернуться на клиент. Рассмотрим правильную последовательность действий в модуле формы:


&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
    
    // Вызываем проверку на сервере
    Если Не ПроверитьВозможностьИзмененияНаСервере() Тогда
        Отказ = Истина;
        
        // Сообщаем пользователю
        ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Нельзя менять базовую единицу, так как есть движения!");
        
        // Вот здесь мы перечитываем данные
        ЭтаФорма.Прочитать();
    КонецЕсли;
    
КонецПроцедуры

Метод Прочитать() полностью обновляет объект на форме. Важно помнить, что при этом сбрасываются и все остальные несохраненные изменения, которые пользователь мог внести в другие поля.

Решение 2: Двойная проверка (Валидация на форме и в объекте)

Рассмотрим ситуацию, которую часто называют «паттерном валидации». Это считается хорошим тоном в профессиональной разработке. Суть заключается в том, что проверка дублируется:

  1. В модуле объекта: для обеспечения безопасности данных при любом способе записи.
  2. В модуле формы: для обеспечения комфортной работы пользователя (UX).

Проанализируем пример кода в модуле объекта:


Процедура ПередЗаписью(Отказ)
    
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;
    
    Если Не ЭтоНовый() Тогда
        // Получаем старое значение напрямую из ссылки в базе
        СтараяЕдиница = Ссылка.БазоваяЕдиница;
        
        Если БазоваяЕдиница <> СтараяЕдиница И ИмеютсяДвиженияПоОбъекту() Тогда
            Отказ = Истина;
            ТекстСообщения = "Изменение базовой единицы запрещено!";
            // В 8.3 лучше использовать СообщениеПользователю
        КонецЕсли;
    КонецЕсли;
    
КонецПроцедуры

Теперь, чтобы форма корректно реагировала на этот отказ, в модуле формы мы можем перехватить событие ПриЗаписиНаСервере или ПослеЗаписиНаСервере. Однако, если Отказ установлен в модуле объекта, событие ПослеЗаписи на форме не вызовется. Поэтому наиболее надежным местом для «отката» значения остается клиентское событие ПередЗаписью или использование обработчика ожидания, если ошибка пришла из глубоких слоев бизнес-логики.

Решение 3: Ручной возврат значения через переменную формы

Если мы не хотим перечитывать весь объект целиком (например, объект очень тяжелый или мы хотим сохранить изменения в других полях), мы можем запомнить старое значение при открытии формы. Выясним, как это сделать правильно.

1. Создадим реквизит формы (не объекта!) с именем СтараяБазоваяЕдиница.

2. В событии ПриЧтенииНаСервере заполним его:


&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
    СтараяБазоваяЕдиница = ТекущийОбъект.БазоваяЕдиница;
КонецПроцедуры

3. В клиентской процедуре ПередЗаписью, если проверка не прошла, мы просто возвращаем значение из нашей переменной:


&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
    
    Если Объект.БазоваяЕдиница <> СтараяБазоваяЕдиница Тогда
        Если ЕстьДвиженияПоРегистрам() Тогда
            Отказ = Истина;
            // Возвращаем старое значение в реквизит объекта
            Объект.БазоваяЕдиница = СтараяБазоваяЕдиница;
            Сообщить("Значение возвращено к исходному.");
        КонецЕсли;
    КонецЕсли;
    
КонецПроцедуры

Тонкости использования Модифицированности

Важно учитывать свойство формы Модифицированность. Когда пользователь меняет значение реквизита, форма становится «грязной» (появляется звездочка в заголовке). Если мы программно возвращаем старое значение (через присваивание Объект.Реквизит = СтароеЗначение), форма всё равно может считаться измененной.

Если же мы используем метод Прочитать(), платформа сама сбрасывает флаг модифицированности, так как данные теперь полностью соответствуют базе данных. Это более «чистый» способ с точки зрения платформенных механизмов.

Особенности для обычных форм (1С 8.2)

В старых конфигурациях на обычных формах ситуация несколько иная. Там процедура ПередЗаписью в модуле объекта выполняется в той же транзакции и часто в том же потоке, что и работа формы. Чтобы обновить данные в обычной форме после отказа, можно использовать метод ЭтаФорма.ОбновитьОтображениеДанных(), но он не всегда гарантирует возврат значения из базы. В этом случае наиболее надежным методом остается вызов ЭтотОбъект.Прочитать() непосредственно в обработчике формы после получения отказа.

Резюме

Мы рассмотрели несколько подходов к решению задачи. Выбор конкретного метода зависит от требований к интерфейсу:

Помните, что хорошая программа не только запрещает неверные действия, но и помогает пользователю быстро вернуться в рабочее состояние без лишних кликов и перезагрузок.

← На главную