Как обновить отображение данных в табличной части документа при изменении связанных справочников?

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

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

Суть проблемы: механизм клиентского кэширования

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

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

Разбор неэффективных подходов

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

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

Эффективное решение: метод ОповеститьОбИзменении()

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

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

Пример реализации процедуры на клиенте и сервере:


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

&НаСервере
Функция ИзменитьСтатусыНаСервере()
    
    МассивСсылок = Новый Массив;
    ДокОбъект = Объект.Документ.ПолучитьОбъект();
    
    Для Каждого Стр Из ДокОбъект.Товары Цикл
        
        // Получаем объект серии для редактирования
        СерияОбъект = Стр.Серия.ПолучитьОбъект();
        Если СерияОбъект <> Неопределено Тогда
            СерияОбъект.СтатусПродукции = Объект.НовыйСтатус;
            СерияОбъект.Записать();
            
            // Собираем ссылки, которые были изменены
            МассивСсылок.Добавить(СерияОбъект.Ссылка);
        КонецЕсли;
        
    КонецЦикла;
    
    Возврат МассивСсылок;
    
КонецФункции

В данном примере мы разделяем логику: серверная функция выполняет запись в базу данных и возвращает массив измененных ссылок, а клиентская процедура последовательно вызывает ОповеститьОбИзменении(). Это гарантирует, что визуальное отображение в табличной части документа обновится мгновенно.

Оптимизация производительности при массовых изменениях

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

  1. Исключение дублей. В одном документе может быть много строк с одной и той же серией. Нет смысла оповещать об изменении одной и той же ссылки несколько раз. Используйте соответствие или массив с проверкой уникальности перед возвратом данных на клиент.
  2. Использование метода ОбновитьОтображениеДанных(). Если изменений очень много и они затрагивают разные типы данных, вместо множественных оповещений можно использовать метод формы ЭтаФорма.ОбновитьОтображениеДанных(). Он принудительно перерисовывает все данные на форме, включая значения «через точку».

Пример использования ОбновитьОтображениеДанных:


&НаКлиенте
Процедура ИзменитьСтатусОптимизировано(Команда)
    
    ИзменитьСтатусыНаСервере();
    
    // Принудительное обновление всех данных формы
    ЭтаФорма.ОбновитьОтображениеДанных();
    
КонецПроцедуры

Важное замечание: Метод ОбновитьОтображениеДанных() является более «тяжелым» для клиентского приложения, чем точечное оповещение конкретных ссылок, так как он заставляет форму пересчитать вообще всё. Используйте его осмотрительно на сложных формах с большим количеством вычисляемых полей.

Работа с контекстом: НаСервере и НаСервереБезКонтекста

Для повышения быстродействия обработки, как мы видим в обсуждении, рекомендуется использовать процедуры с директивой &НаСервереБезКонтекста. Это позволяет не передавать всю форму (весь Объект) с клиента на сервер и обратно, что значительно экономит трафик и время выполнения, особенно при работе через медленное интернет-соединение.

Рассмотрим, как переписать наше решение в максимально производительном стиле:


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

&НаСервереБезКонтекста
Функция ОбновитьСтатусыНаСервереБезКонтекста(Параметры)
    
    СписокИзмененныхСерий = Новый Соответствие; // Для обеспечения уникальности
    
    ДокОбъект = Параметры.ДокументСсылка.ПолучитьОбъект();
    Для Каждого СтрокаТЧ Из ДокОбъект.Товары Цикл
        
        Если ЗначениеЗаполнено(СтрокаТЧ.Серия) Тогда
            СерияОбъект = СтрокаТЧ.Серия.ПолучитьОбъект();
            Если СерияОбъект.СтатусПродукции <> Параметры.НовыйСтатус Тогда
                СерияОбъект.СтатусПродукции = Параметры.НовыйСтатус;
                СерияОбъект.Записать();
                СписокИзмененныхСерий.Вставить(СерияОбъект.Ссылка, Истина);
            КонецЕсли;
        КонецЕсли;
        
    КонецЦикла;
    
    // Преобразуем ключи соответствия в массив для возврата на клиент
    МассивРезультат = Новый Массив;
    Для Каждого КлючИЗначение Из СписокИзмененныхСерий Цикл
        МассивРезультат.Добавить(КлючИЗначение.Ключ);
    КонецЦикла;
    
    Возврат МассивРезультат;
    
КонецФункции

Подведение итогов

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

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

← На главную