При разработке обработок группового изменения данных (например, перенумерации кассовых документов ПриходныйКассовыйОрдер или РасходныйКассовыйОрдер) разработчики часто сталкиваются с неожиданным поведением управляемого интерфейса: процедура на сервере отработала успешно, документы перезаписаны с новыми номерами, но в табличной части обработки отображаются старые номера. При этом повторный вызов алгоритма заполнения таблицы формы не дает никакого результата, хотя при открытии самого документа номер уже изменен. Разберем по шагам, почему платформа так себя ведет и как правильно обновить отображение данных на клиенте.
Выясним причину такого поведения платформы. В управляемом приложении тонкий клиент активно кэширует строковые представления ссылочных типов (ДокументСсылка, СправочникСсылка) по их уникальным идентификаторам (GUID). Это архитектурное решение платформы "1С:Предприятие 8.3", направленное на минимизацию сетевого трафика между клиентом и сервером.
Когда мы выводим в колонку таблицы формы значение ссылочного типа, платформа получает с сервера пару: внутренний идентификатор ссылки и её строковое представление (например, "Расходный кассовый ордер 02 от 15.01.2025"). После перенумерации на сервере реквизит Номер изменился, однако сам объект базы данных сохранил свой прежний идентификатор GUID.
При повторном перезаполнении таблицы формы сервер возвращает клиенту те же самые ссылки. Тонкий клиент проверяет свой локальный кэш, находит там уже сохраненные соответствия для данных GUID и берет старое строковое представление, не запрашивая его заново с сервера. По этой причине даже полное очищение и повторное заполнение таблицы на сервере не приводит к изменению текста на экране. Кэш сбрасывается лишь спустя некоторое время (по умолчанию время жизни клиентского кэша представлений составляет около 20 минут) либо при принудительном сбросе.
Посмотрим на самое простое решение, предложенное в обсуждении темы. Для сброса клиентского состояния можно воспользоваться стандартным методом глобального контекста ОбновитьИнтерфейс().
Разберем пример кода вызова из клиентской процедуры:
&НаКлиенте
Процедура ВыполнитьПеренумерацию(Команда)
// Выполняем логику перенумерации и обновления данных на сервере
ВыполнитьПеренумерациюНаСервере();
// Сбрасываем кэш интерфейса на клиенте
ОбновитьИнтерфейс();
КонецПроцедуры
Ограничения данного способа: метод ОбновитьИнтерфейс() является достаточно ресурсоемким. Он выполняет полную перерисовку всех открытых окон приложения, принудительно обновляет командный интерфейс и сбрасывает весь клиентский кэш сеанса. При обработке больших массивов данных или частых вызовах это может приводить к заметным задержкам и мерцанию интерфейса пользователя.
Рассмотрим более производительный и архитектурно правильный подход. Платформа 1С предоставляет клиентский метод ОповеститьОбИзменении(), предназначенный именно для информирования клиентского приложения о том, что конкретный объект был изменен в базе данных.
Когда вызывается ОповеститьОбИзменении(), клиент точечно сбрасывает кэш представления именно для переданной ссылки, а также автоматически обновляет динамические списки, отображающие данный объект. Никакой лишней перерисовки командных панелей и чужих окон при этом не происходит.
Разберем реализацию этого механизма по шагам:
ОповеститьОбИзменении() для каждой ссылки.Посмотрим на пример программного кода:
&НаСервере
Функция ПеренумероватьДокументыНаСервере()
МассивИзмененных = Новый Массив;
Для Каждого СтрТЧ Из ТаблицаДокументов Цикл
ДокОбъект = СтрТЧ.Документ.ПолучитьОбъект();
// Назначаем новый номер объекту
ДокОбъект.Номер = ПолучитьНовыйНомер(СтрТЧ.Документ);
ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
МассивИзмененных.Добавить(СтрТЧ.Документ);
КонецЦикла;
Возврат МассивИзмененных;
КонецФункции
&НаКлиенте
Процедура КомандаПеренумеровать(Команда)
МассивСсылок = ПеренумероватьДокументыНаСервере();
// Точечно уведомляем клиентскую часть об изменении каждого объекта
Для Каждого СсылкаДок Из МассивСсылок Цикл
ОповеститьОбИзменении(СсылкаДок);
КонецЦикла;
// После сброса кэша для ссылок повторно читаем данные таблицы формы
ЗаполнитьТаблицуНаСервере();
КонецПроцедуры
Проанализируем еще один надежный способ, позволяющий полностью обойти проблему кэширования ссылок. Если таблица создается исключительно под задачи обработки, не обязательно хранить номер и дату внутри стандартного представления ссылки.
Вместо того чтобы полагаться на автоматическое представление колонки типа ДокументСсылка, добавим в таблицу формы явные колонки с примитивными типами данных:
Ссылка с типом ДокументСсылка (ее можно скрыть или оставить только для открытия документа).Номер с типом Строка.Дата с типом Дата.При таком подходе данные примитивных типов передаются между сервером и клиентом напрямую через контекст формы и не зависят от внутреннего кэша ссылок тонкого клиента. Как только на сервере в колонку Номер будет записано новое строковое значение, оно мгновенно и без дополнительных вызовов отобразится у пользователя.
Посмотрим, как выглядит обновление строковых колонок на сервере:
&НаСервере
Процедура ОбновитьНомераВТаблицеНаСервере()
Для Каждого СтрТЧ Из ТаблицаДокументов Цикл
ДокОбъект = СтрТЧ.Ссылка.ПолучитьОбъект();
ДокОбъект.Номер = СформироватьНомер();
ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
// Сразу обновляем строковый реквизит строки формы
СтрТЧ.Номер = ДокОбъект.Номер;
КонецЦикла;
КонецПроцедуры
Если групповая обработка документов выполняется в рамках явной транзакции (с использованием операторов НачатьТранзакцию() и ЗафиксироватьТранзакцию()), обратите внимание на момент перечитывания данных. Любые повторные запросы к базе данных или обновление реквизитов формы на сервере должны выполняться строго после вызова метода ЗафиксироватьТранзакцию(). В противном случае из-за уровней изоляции транзакций запросы и методы чтения данных могут вернуть неактуальное состояние базы.