Почему данные в форме 1С обновляются только при повторном открытии и как это исправить?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С:Предприятие 8 (особенно в режиме обычного приложения) программисты часто сталкиваются с ситуацией, когда программное изменение реквизитов объекта при его открытии не отражается на экране мгновенно. Пользователь видит старые значения, и только после закрытия и повторного открытия карточки данные становятся актуальными. Это создает иллюзию того, что код «срабатывает со второго раза».

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

Разбор ситуации: почему данные «опаздывают»?

Рассмотрим типовой сценарий, представленный в вопросе автора. В процедуре ПередОткрытием выполняется запрос, который рассчитывает суммарный вес и объем упаковок, а затем записывает эти значения в справочник Номенклатура. Проблема заключается в том, что в момент выполнения этого кода форма уже начала процесс инициализации данных.

Проанализируем последовательность событий при открытии формы объекта:

  1. Система инициирует открытие формы и считывает данные объекта из базы данных во внутренний кэш формы.
  2. Вызывается событие ПередОткрытием.
  3. Внутри этого события разработчик пишет код, который через ПолучитьОбъект() создает новую независимую копию объекта в памяти, меняет её и вызывает метод Записать().
  4. Данные в базе данных обновились, но текущая форма уже «держит» в своих реквизитах те значения, которые были актуальны на шаге 1.
  5. Форма отрисовывается со старыми данными.

Именно поэтому при первом заходе мы видим старые данные, а при втором — уже обновленные (так как на шаге 1 будут считаны результаты предыдущей записи).

Решение №1: Принудительное перечитывание данных

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


Процедура ПередОткрытием(Отказ, СтандартнаяОбработка)
    // Код расчета данных (запрос)
    ЗапросОбъем = Новый Запрос;
    ЗапросОбъем.Текст = 
    "ВЫБРАТЬ
    |    СУММА(ТЧ.ШаблонУпаковки.Вес) КАК Вес,
    |    СУММА(ТЧ.ШаблонУпаковки.Объем) КАК Объем
    |ИЗ
    |    Справочник.ШаблоныСхемПакования.ТЧСостав КАК ТЧ
    |ГДЕ
    |    ТЧ.Ссылка.Владелец = &Ссылка";
    
    ЗапросОбъем.УстановитьПараметр("Ссылка", Ссылка);
    Выборка = ЗапросОбъем.Выполнить().Выбрать();
    
    Если Выборка.Следующий() Тогда
        // Получаем объект для записи в БД
        ОбъектБД = Ссылка.ПолучитьОбъект();
        ОбъектБД.Объем = Выборка.Объем;
        ОбъектБД.Вес = Выборка.Вес;
        ОбъектБД.Записать();
        
        // КРИТИЧЕСКИ ВАЖНО: Перечитываем данные в текущую форму
        ЭтаФорма.Прочитать();
    КонецЕсли;
КонецПроцедуры

Метод ЭтаФорма.Прочитать() заставляет форму сбросить текущие значения реквизитов и заново загрузить их из базы данных. Это устраняет визуальное несоответствие.

Решение №2: Работа с объектом формы (Рекомендуемое)

Хотя первый метод работает, он не является оптимальным с точки зрения производительности и архитектуры. Записывать объект в базу данных каждый раз при открытии — плохая практика. Если пользователь просто зашел посмотреть карточку и закрыл её, мы все равно создали лишнюю транзакцию записи, изменили версию объекта и увеличили нагрузку на сервер.

Проанализируем более изящный подход: изменять данные не «в базе», а непосредственно «в форме». В обычном приложении для этого используется переменная ЭтотОбъект.

Рассмотрим правильный алгоритм:

  1. В событии ПередОткрытием или ПриОткрытии рассчитываем значения запросом.
  2. Присваиваем значения напрямую реквизитам формы: ЭтотОбъект.Вес = Выборка.Вес;.
  3. Не вызываем метод Записать(). Данные отобразятся сразу, а в базу они попадут только тогда, когда пользователь сам нажмет «ОК» или «Сохранить».

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

Сравнение методов обновления

Выясним разницу между похожими методами, чтобы не допускать ошибок в будущем:

Подводные камни и риски

Разберем, к каким проблемам может привести использование Записать() + Прочитать() при открытии:

1. Конфликт версий: Если два пользователя одновременно откроют одну и ту же номенклатуру, и у обоих сработает код записи при открытии, второй пользователь при попытке сохранить свои изменения вручную получит ошибку: «Объект изменен или удален другим пользователем».

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

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

Выводы и рекомендации

Проанализировав ситуацию, мы можем дать следующие рекомендации по работе с формами в 1С:

Соблюдение этих правил позволит создавать надежные и быстрые интерфейсы, которые ведут себя предсказуемо для пользователя и не перегружают систему лишними операциями записи.

← На главную