При разработке на платформе 1С:Предприятие 8.3, особенно при работе с управляемыми формами, разработчики часто сталкиваются с ситуацией, когда после программного изменения данных объекта (например, справочника или документа) его представление в табличной части другой формы или в выпадающих списках остается старым, несмотря на то что в базе данных изменения уже сохранены. Эта проблема вызвана механизмами кэширования платформы, которые используются для оптимизации производительности. Давайте подробно разберем, почему возникает такая ситуация и как эффективно ее решать.
Платформа 1С:Предприятие активно использует кэширование для ускорения работы. Это касается как данных объектов, так и их представлений (того, что мы видим в полях ввода, таблицах, списках выбора). Когда вы работаете с управляемой формой, клиентская часть (тонкий клиент, веб-клиент) получает данные и их представления с сервера и хранит их в своем локальном кэше. Если объект изменяется на сервере (например, в модуле объекта после записи или в фоновом задании), клиентская форма автоматически не узнает об этих изменениях и продолжает отображать старые, закэшированные данные. Такое поведение проявляется до тех пор, пока клиентский кэш не будет принудительно очищен или обновлен.
В частности, проблема может возникнуть, если:
ПослеЗаписиНаСервере модуля объекта или формы).В результате, чтобы увидеть актуальное представление, иногда приходится перезапускать весь сеанс 1С или выполнять специфические действия, такие как открытие формы измененного объекта, что принудительно обновляет кэш.
ОповеститьОбИзменении()Наиболее эффективным и рекомендованным способом для уведомления клиентских форм об изменениях объекта является использование метода глобального контекста ОповеститьОбИзменении(). Этот метод предназначен для очистки клиентского кэша, связанного с измененным объектом или типом объектов, что, в свою очередь, приводит к обновлению представлений в динамических списках и полях формы.
ОповеститьОбИзменении()?Когда вы вызываете ОповеститьОбИзменении(), платформа выполняет следующие действия:
Вся эта процедура направлена на то, чтобы клиентская часть получила актуальные данные при следующем запросе. Важно отметить, что метод ОповеститьОбИзменении() должен быть вызван на клиенте.
ОповеститьОбИзменении()Рассмотрим типовой сценарий, описанный в начальном сообщении: изменение серий номенклатуры в документе "ТаможеннаяДекларация" после его проведения.
Предположим, у нас есть процедура на сервере, которая изменяет наименование и номер серии:
// В модуле формы документа, например, в обработчике ПослеЗаписиНаСервере
&НаСервере
Процедура ПослеЗаписиНаСервере(ТекущийОбъект, ПараметрыЗаписи)
// Предположим, что ТекущийОбъект - это ТаможеннаяДекларация
Для Каждого СтрокаТЧ Из ТекущийОбъект.ТабличнаяЧастьССериями Цикл
// Получаем ссылку на объект серии
СсылкаНаСерию = СтрокаТЧ.СерияНоменклатуры;
Если СсылкаНаСерию.Пустая() Тогда
Продолжить;
КонецЕсли;
// Получаем сам объект серии, чтобы изменить его
ОбъектСерии = СсылкаНаСерию.ПолучитьОбъект();
Если ОбъектСерии = Неопределено Тогда
Продолжить;
КонецЕсли;
// Формируем новое наименование и номер, включающий номер ГТД
НовыйНомерСерии = СтрокаТЧ.НомерГТД + "-" + ОбъектСерии.НомерСерии;
НовоеНаименованиеСерии = СтрокаТЧ.НомерГТД + " " + ОбъектСерии.Наименование;
ОбъектСерии.НомерСерии = НовыйНомерСерии;
ОбъектСерии.Наименование = НовоеНаименованиеСерии;
// Записываем измененный объект серии
Попытка
ОбъектСерии.Записать();
// Здесь серия изменилась, но представление на клиенте может остаться старым
// Необходимо оповестить клиента об изменении серии
ВызватьНаКлиенте_ОповеститьОбИзмененииСерии(СсылкаНаСерию);
Исключение
// Обработка ошибок записи
ЗаписатьЖурналРегистрации("Ошибка записи серии: " + ОписаниеОшибки(), УровеньВажностиЖурнала.Ошибка);
КонецПопытки;
КонецЦикла;
КонецПроцедуры
// В модуле формы документа
&НаКлиенте
Процедура ВызватьНаКлиенте_ОповеститьОбИзмененииСерии(СсылкаНаИзмененнуюСерию) Экспорт
// Передаем ссылку на измененный объект серии
ОповеститьОбИзменении(СсылкаНаИзмененнуюСерию);
// Также может быть полезно обновить данные самой формы документа
// ЭтаФорма.Прочитать(); // Перечитает данные объекта формы
// ЭтаФорма.ОбновитьОтображениеДанных(); // Обновит отображение элементов на форме
// Элементы.ТабличнаяЧастьССериями.Обновить(); // Обновит конкретную табличную часть
КонецПроцедуры
В этом примере мы сначала изменяем и записываем объект серии на сервере. Затем, чтобы уведомить клиентскую часть формы о произошедших изменениях, мы вызываем клиентскую процедуру ВызватьНаКлиенте_ОповеститьОбИзмененииСерии, передавая ей ссылку на измененную серию. Внутри этой клиентской процедуры вызывается метод ОповеститьОбИзменении(). Это действие очистит соответствующий кэш на клиенте, и при следующем обращении к этой серии (например, при прокрутке таблицы или даже при повторном открытии формы документа в том же сеансе) будет получено уже новое, актуальное представление.
Если изменения произошли сразу для множества объектов одного типа, вы можете оповестить об изменении всего типа объектов:
&НаКлиенте
Процедура ОповеститьОбИзмененииТипаСерий()
ОповеститьОбИзменении(Тип("СправочникСсылка.СерииНоменклатуры"));
КонецПроцедуры
Этот вариант очистит кэш для всех объектов типа "СерииНоменклатуры", что может быть полезно при массовых изменениях, но менее оптимально с точки зрения производительности, чем оповещение по конкретной ссылке.
ОповеститьОбИзменении()ОповеститьОбИзменении() действует только в рамках текущего клиентского сеанса. Если изменения произошли в одном сеансе, а другой пользователь открыл форму в своем сеансе, то у него представление обновится только после перезапуска его сеанса или выполнения аналогичного оповещения.ЭтаФорма.Прочитать().ОповеститьОбИзменении() невозможно, так как это клиентский метод. В этом случае клиенты узнают об изменениях только после перезапуска своих сеансов. Для более оперативного обновления в таких сценариях могут потребоваться дополнительные механизмы, такие как периодический опрос сервера клиентской формой или использование системы взаимодействия (хотя это более сложные решения, выходящие за рамки прямого использования ОповеститьОбИзменении()).Помимо ОповеститьОбИзменении(), существуют и другие методы, которые могут помочь в обновлении данных и отображения на форме. Они часто используются в комбинации с оповещением или в случаях, когда оповещение не подходит:
ЭтаФорма.Прочитать()Этот метод перезачитывает объект управляемой формы с сервера. Если вы изменили данные объекта формы (например, реквизиты шапки документа) и хотите, чтобы эти изменения отразились на форме, вызов ЭтаФорма.Прочитать() обновит все данные объекта формы. Это особенно полезно, когда изменения затрагивают основные реквизиты формы, а не только ссылочные представления в табличных частях.
&НаКлиенте
Процедура ОбновитьФормуПолностью()
ЭтаФорма.Прочитать(); // Перечитываем данные объекта формы с сервера
КонецПроцедуры
ЭтаФорма.ОбновитьОтображениеДанных()Принудительно обновляет содержание элементов управления на форме. Этот метод полезен, если вы программно изменили значения реквизитов формы (не объекта формы, а именно реквизитов формы) и хотите, чтобы эти изменения немедленно отобразились на экране без полной перезагрузки данных объекта.
&НаКлиенте
Процедура ОбновитьВизуальноеОтображение()
// Например, если вы изменили какой-то реквизит формы
ЭтотОбъект.КакойТоРеквизитФормы = НовоеЗначение;
ЭтаФорма.ОбновитьОтображениеДанных(); // Принудительно отображаем изменения
КонецПроцедуры
Элементы.ИмяЭлемента.Обновить()Этот метод позволяет обновить данные в конкретном элементе формы, например, в динамическом списке или табличном поле. Если изменения касаются только содержимого одной табличной части или одного поля, можно обновить только его, что более производительно, чем обновление всей формы.
&НаКлиенте
Процедура ОбновитьТабличнуюЧасть()
Элементы.ТабличнаяЧастьССериями.Обновить(); // Обновляем данные конкретной табличной части
КонецПроцедуры
Оповестить() и ОбработкаОповещения()Если изменение происходит в одной форме, а вам необходимо обновить данные в другой открытой форме (например, форме списка), вы можете использовать механизм оповещений. Изменяющая форма вызывает Оповестить(), а целевая форма обрабатывает событие ОбработкаОповещения().
В форме, где произошли изменения (например, форме элемента справочника):
// В модуле формы элемента справочника
&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
// Оповещаем другие формы об изменении конкретного объекта
Оповестить("ОбъектСправочникаИзменился", ЭтотОбъект.Ссылка);
КонецПроцедуры
В форме, которая должна обновиться (например, в форме списка или форме документа с табличной частью):
// В модуле формы списка или документа
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
Если ИмяСобытия = "ОбъектСправочникаИзменился" Тогда
// Параметр содержит ссылку на измененный объект.
// Здесь можно принять решение, как обновить данные.
// Например, обновить динамический список, если он содержит этот объект.
// Или перечитать данные, если это форма документа, где отображается этот объект.
Если Параметр = Элементы.ТабличнаяЧасть.ТекущиеДанные.СсылкаНаОбъектСправочника Тогда
// Если измененный объект - это текущая строка в табличной части,
// можно попробовать обновить только ее.
Элементы.ТабличнаяЧасть.ОбновитьСтроки();
КонецЕсли;
// Для общего случая можно обновить весь динамический список
Если ТипЗнч(Элементы.Список) = Тип("ДинамическийСписок") Тогда
Элементы.Список.Обновить();
КонецЕсли;
// Или, если это форма документа, которая может содержать данный объект
// в табличной части, и мы хотим обновить ее представление
// Лучше всего вызвать ОповеститьОбИзменении(Параметр) для очистки кэша
ОповеститьОбИзменении(Параметр);
// А также, возможно, обновить визуальное отображение формы
ЭтаФорма.ОбновитьОтображениеДанных();
КонецЕсли;
КонецПроцедуры
В приведенном примере мы используем ОповеститьОбИзменении(Параметр) внутри ОбработкаОповещения, что является хорошей практикой. Дополнительно можно обновить конкретные элементы или всю форму в зависимости от ситуации.
В некоторых крайних случаях, особенно когда ни один из стандартных методов не помогает, а проблема связана с визуальным кэшем, можно попробовать "передернуть" видимость элемента формы. Этот подход заключается в том, чтобы на очень короткий промежуток времени скрыть элемент, а затем снова показать его. Платформа при этом может принудительно перерисовать элемент и запросить его актуальное представление. Это довольно "костыльное" решение и не рекомендуется для регулярного использования, но может помочь в безвыходных ситуациях.
&НаКлиенте
Процедура ОбновитьТабличнуюЧастьЧерезСкрытие()
Элементы.ТабличнаяЧастьДокументыШаблонПечатныеФормы.Видимость = Ложь;
ПодключитьОбработчикОжидания("Оработчик_ТабличнаяЧастьДокументыШаблонПечатныеФормы_Видимость", 0.1, Истина);
КонецПроцедуры
&НаКлиенте
Процедура Оработчик_ТабличнаяЧастьДокументыШаблонПечатныеФормы_Видимость() Экспорт
Элементы.ТабличнаяЧастьДокументыШаблонПечатныеФормы.Видимость = Истина;
КонецПроцедуры
Этот пример из сообщения 22 демонстрирует использование обработчика ожидания для кратковременного скрытия и последующего отображения табличной части. Это может вызвать перерисовку элемента и, как следствие, обновление его представления. Однако, повторимся, это следует рассматривать как крайнюю меру.
В некоторых случаях, если проблема носит систематический характер, связана с некорректным поведением клиентского кэша в целом или является артефактом старых версий платформы, может помочь ручная очистка пользовательского кэша 1С. Это можно сделать следующим образом:
%APPDATA%\1C\1CE\ или %LOCALAPPDATA%\1C\1CE\).Для более удобной очистки кэша можно также использовать параметр запуска /ClearCache, который добавляется в строку запуска 1С:Предприятия. Этот параметр очищает кэш при каждом запуске, что полезно для тестирования, но не рекомендуется для постоянной работы, так как снижает производительность.
Проблема с необновлением представлений объектов в табличных частях управляемых форм 1С:Предприятия является распространенной и вызвана особенностями кэширования платформы. Основное и наиболее элегантное решение — использование метода ОповеститьОбИзменении() на клиенте после программного изменения объекта. Мы с вами разобрали его принцип работы, примеры использования и важные нюансы. В ситуациях, когда ОповеститьОбИзменении() оказывается недостаточным, мы также рассмотрели дополнительные методы, такие как ЭтаФорма.Прочитать(), ЭтаФорма.ОбновитьОтображениеДанных(), Элементы.ИмяЭлемента.Обновить(), а также механизмы взаимодействия между формами через Оповестить() и ОбработкаОповещения(). Помните, что правильный выбор метода зависит от конкретного сценария и места возникновения проблемы. Всегда стремитесь к наиболее производительному и наименее инвазивному решению.