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