Почему при программном изменении справочника на форме отображается старое наименование и как это исправить?

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

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

В чем причина проблемы: Кэширование данных на клиенте

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

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

Давайте сопоставим поведение платформы при интерактивной и программной записи объектов:

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

Почему стандартные методы формы не помогают?

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

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

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

Правильное решение проблемы с помощью ОповеститьОбИзменении()

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

Рассмотрим синтаксис этого метода:

ОповеститьОбИзменении(<Ссылка или Тип>)

Этот метод выполняет следующие важные действия:

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

Для начала опишем процедуру программного изменения объекта на сервере:


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

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


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

Решение проблемы с помощью глобальной системы оповещений

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

Давайте реализуем эту схему пошагово.

Шаг 1. В форме-источнике (например, в форме документа, где мы программно изменили данные справочника) после записи объекта отправляем глобальное оповещение:


&НаКлиенте
Процедура ПослеПрограммногоИзменения(СсылкаНаИзмененныйОбъект)
    
    // Очищаем кэш локально у себя
    ОповеститьОбИзменении(СсылкаНаИзмененныйОбъект);
    
    // Посылаем сигнал всем открытым формам сеанса
    ПараметрыОповещения = Новый Структура;
    ПараметрыОповещения.Вставить("Ссылка", СсылкаНаИзмененныйОбъект);
    
    Оповестить("Событие_ИзменениеСправочника2", ПараметрыОповещения, ЭтотОбъект);
    
КонецПроцедуры

Шаг 2. В форме-приемнике (например, в форме элемента Спр1, где отображается ссылка на измененный объект) создаем обработчик события ОбработкаОповещения. Не забудьте подключить эту процедуру в палитре свойств самой формы!


&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
    
    // Проверяем, наше ли это событие
    Если ИмяСобытия = "Событие_ИзменениеСправочника2" Тогда
        
        // Убеждаемся, что параметры переданы корректно
        Если ТипЗнч(Параметр) = Тип("Структура") И Параметр.Свойство("Ссылка") Тогда
            
            // Сбрасываем кэш конкретной ссылки, которая пришла в событии
            ОповеститьОбИзменении(Параметр.Ссылка);
            
            // Принудительно обновляем элементы отображения текущей формы
            ЭтаФорма.ОбновитьОтображениеДанных();
            
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

Использование механизмов БСП (Библиотеки стандартных подсистем)

Если вы ведете разработку в типовой конфигурации (например, «1С:ERP Управление предприятием», «1С:Комплексная автоматизация» или «1С:Бухгалтерия»), то наиболее правильным решением будет использование встроенных механизмов БСП. В составе БСП есть общий модуль СтандартныеПодсистемыКлиент, который содержит готовую процедуру для оповещения системы об изменении объектов базы данных.

Рассмотрим пример ее использования:


&НаКлиенте
Процедура ИзменитьИОповеститьБСП(СсылкаНаОбъект)
    
    // Выполняем программное изменение на сервере...
    ВыполнитьЗаписьНаСервере(СсылкаНаОбъект);
    
    // Оповещаем систему средствами БСП
    СтандартныеПодсистемыКлиент.ОповеститьОбИзмененииОбъектов(СсылкаНаОбъект);
    
КонецПроцедуры

Процедура ОповеститьОбИзмененииОбъектов() не только очищает кэш представлений платформы с помощью стандартного метода ОповеститьОбИзменении(), но и автоматически рассылает стандартизированные системные события вида Запись_<ИмяМетаданных>. Это позволяет динамическим спискам, журналам документов и другим связанным формам БСП синхронно реагировать на любые программные изменения объектов.

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

Программная запись объектов в 1С выполняется на уровне сервера СУБД, минуя интерфейсные механизмы клиента. Чтобы избежать рассинхронизации отображаемых данных и реального содержимого базы данных, разработчик всегда должен самостоятельно заботиться об актуализации клиентского кэша.

Запомните простое правило: сделали программную запись объекта на сервере — вызовите метод ОповеститьОбИзменении() на клиенте для измененной ссылки. Это гарантирует пользователю комфортную работу с актуальными данными без необходимости вручную перевыбирать реквизиты или перезапускать программу.

← На главную