Как обновить данные на клиенте после их программного изменения на сервере в 1С?

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

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

Анализ проблемы: почему данные не обновляются автоматически?

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

Причина кроется в клиентском кэше объектов. Платформа 1С кэширует значения реквизитов ссылочных типов на стороне клиента, чтобы не обращаться к серверу при каждом обращении к Ссылка.Реквизит. Когда вы вызываете Записать() на сервере, клиентское приложение об этом изменении ничего не знает. Его локальный кэш остается «грязным» (содержит устаревшие данные) до тех пор, пока не произойдет событие, заставляющее систему его очистить.

Многие начинающие разработчики пытаются использовать метод ОбновитьОтображениеДанных(). Проанализируем, почему он не помогает в данном случае. Этот метод предназначен исключительно для визуального обновления элементов формы: он пересчитывает условное оформление, проверяет видимость и доступность, но он не перечитывает данные из базы данных. Если в кэше клиента лежит старое значение, ОбновитьОтображениеДанных() просто еще раз отрисует это старое значение.

Решение проблемы через механизм оповещений

Для решения задачи синхронизации данных существует специализированный механизм оповещения системы об изменениях. Рассмотрим основной инструмент — метод ОповеститьОбИзменении().

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

Разбор практического примера кода

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

Сначала реализуем серверную часть в общем модуле. Обратите внимание на последовательную запись двух разных объектов:


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

Теперь самое важное — вызов этой процедуры с клиента и последующее обновление интерфейса. Простого вызова серверной функции недостаточно. Посмотрим на правильную реализацию обработчика команды:


&НаКлиенте
Процедура СоздатьСчет(Команда)
    
    СтруктураТекущихДанных = ПолучитьСтруктуруДанныхФормы();
    
    // Выполняем изменения на сервере
    СоздатьСчетНаСервере(СтруктураТекущихДанных);
    
    // Оповещаем систему об изменениях, чтобы сбросить кэш
    // Важно оповестить об изменении ОБОИХ объектов
    
    // Оповещаем об изменении конкретного банковского счета
    ОповеститьОбИзменении(СтруктураТекущихДанных.СсылкаНаСчет); 
    
    // Оповещаем об изменении контрагента (так как у него сменился основной счет)
    ОповеститьОбИзменении(СтруктураТекущихДанных.КонтрагентСсылка);
    
    // Теперь визуальные элементы, связанные с этими данными, обновятся корректно
    ОбновитьОтображениеДанных();
    
КонецПроцедуры

Почему важно двойное оповещение?

Выясним причину, по которой автору исходного вопроса помогло именно двойное оповещение. В 1С объекты «Контрагент» и «Банковский счет» — это разные записи в разных таблицах базы данных. У каждого из них свой собственный кэш на стороне клиента.

  1. Если мы оповестим только об изменении банковского счета, система обновит данные в списках банковских счетов, но форма контрагента может по-прежнему показывать в поле ОсновнойСчет старую ссылку или пустую информацию, так как кэш самого контрагента не был сброшен.
  2. Если мы оповестим только о контрагенте, то поле ОсновнойСчет на форме может обновиться, но при попытке открыть этот счет из списка или перейти в его карточку, мы можем столкнуться с тем, что данные внутри карточки счета еще «старые».

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

Дополнительные методы обновления данных

Помимо ОповеститьОбИзменении(), существуют и другие способы синхронизации, которые полезно знать программисту:

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

2. Оповестить() и обработка оповещения. Если вам нужно не просто обновить кэш, а выполнить какие-то действия в других открытых формах (например, пересчитать итоги в открытом отчете при изменении данных в справочнике), используйте связку Оповестить("ИмяСобытия", Параметры) на клиенте и обработчик ОбработкаОповещения в формах.

3. Обновить() для динамических списков. Если данные изменились, и они должны отобразиться в списке (например, в форме списка контрагентов), использование ОповеститьОбИзменении обычно достаточно. Однако в сложных случаях, когда используются произвольные запросы с объединениями, может потребоваться явный вызов Элементы.Список.Обновить().

Резюме

Проанализировав ситуацию, мы видим, что «невидимость» изменений на клиенте — это не ошибка платформы, а следствие работы механизмов оптимизации. Для гарантированного обновления данных придерживайтесь следующего алгоритма:

  1. Выполните все необходимые изменения данных на сервере и зафиксируйте их методом Записать().
  2. Вернитесь в клиентский контекст.
  3. Используйте ОповеститьОбИзменении() для каждой измененной ссылки.
  4. При необходимости обновите визуальное состояние текущей формы через ОбновитьОтображениеДанных().

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

← На главную