Как обновить кэшированное значение реквизита "через точку" в табличной части на управляемой форме 1С?

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

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

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

Выясняем причину: как устроен локальный кэш платформы 1С

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

Когда управляемая форма впервые считывает и отображает реквизит «через точку» (например, ЕдиницаИзмерения.Коэффициент), происходит следующее:

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

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

Почему стандартные методы обновления отображения не помогают?

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

Решение первое: Принудительный сброс кэша с помощью ОповеститьОбИзменении()

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

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

Посмотрим на пример кода, который можно разместить, например, в форме выбора или в обработчике закрытия карточки единицы измерения:


// Пример 1: Сброс кэша для конкретной измененной ссылки
СсылкаНаЕдиницу = Элементы.ТабличнаяЧасть.ТекущиеДанные.ЕдиницаИзмерения;
Если ЗначениеЗаполнено(СсылкаНаЕдиницу) Тогда
    ОповеститьОбИзменении(СсылкаНаЕдиницу);
КонецЕсли;

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

Существует также альтернативный вариант — сброс кэша для всех объектов определенного типа. Для этого в метод передается описание типа:


// Пример 2: Полный сброс кэша для всего справочника единиц измерения
ОповеститьОбИзменении(Тип("СправочникСсылка.ЕдиницыИзмерения"));

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

Также стоит помнить, что метод ОповеститьОбИзменении() работает строго в рамках текущего клиентского сеанса. Если пользователь «А» изменил коэффициент, а у пользователя «Б» в этот момент открыт заказ, локальный кэш на компьютере пользователя «Б» об этом изменении не узнает до тех пор, пока форма не будет перечитана или кэш не устареет естественным путем.

Почему обращение «через точку» — это плохая практика?

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

  1. Избыточное чтение данных (Падение производительности): Когда вы обращаетесь к реквизиту связанного объекта через точку (даже если вам нужно простое числовое поле, такое как коэффициент), платформа не умеет делать выборочный запрос только к этому полю. Она неявно считывает из базы данных весь объект целиком со всеми его многочисленными реквизитами, табличными частями и присоединенными файлами. Если в вашей табличной части документа содержится 100 строк, система выполнит колоссальный объем избыточной работы по чтению и передаче тяжелых объектов, что гарантированно приведет к замедлению работы системы на больших объемах данных.
  2. Нарушение исторической достоверности данных (Логическая ошибка): Это ключевая методическая проблема автоматизации. Такие показатели, как цена, ставка НДС, валютные курсы или коэффициенты единиц измерения, имеют свойство меняться со временем. Коэффициент в документе должен быть жестко зафиксирован на момент совершения хозяйственной операции. Если вы выводите коэффициент «через точку», то изменив коэффициент в справочнике сегодня, вы автоматически измените отображаемое значение во всех закрытых архивных документах за прошлые годы. При любой попытке пересчета, проведения или печати старого документа логика расчетов будет безнадежно испорчена, а данные управленческого и бухгалтерского учета разойдутся.

Рекомендуемое правильное решение: Физическое хранение реквизита

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

Разберем по шагам реализацию этого паттерна:

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

Рассмотрим пример реализации кода для формы документа:


&НаКлиенте
Процедура ТоварыЕдиницаИзмеренияПриИзменении(Элемент)
    
    СтрокаТаблицы = Элементы.Товары.ТекущиеДанные;
    Если СтрокаТаблицы <> Неопределено Тогда
        // Получаем коэффициент с сервера и записываем в физический реквизит строки
        СтрокаТаблицы.Коэффициент = ПолучитьКоэффициентЕдиницы(СтрокаТаблицы.ЕдиницаИзмерения);
    КонецЕсли;
    
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьКоэффициентЕдиницы(Знач СсылкаНаЕдиницу)
    
    Если ЗначениеЗаполнено(СсылкаНаЕдиницу) Тогда
        Возврат СсылкаНаЕдиницу.Коэффициент;
    КонецЕсли;
    Возврат 1;
    
КонецФункции

При таком подходе мы получаем целый ряд неоспоримых преимуществ:

Таким образом, отказ от динамического отображения реквизитов через точку в пользу физического хранения данных в табличных частях — это единственный надежный путь к созданию стабильных, быстрых и отказоустойчивых бизнес-приложений на платформе «1С:Предприятие 8.3».

← На главную