Как оповестить все формы, открытые в разных сеансах 1С, об изменении данных?

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

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

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

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

---

Сценарий 1. Современное штатное решение: Механизм «Уведомления клиента» (начиная с платформы 8.3.26)

Начиная с версии платформы 8.3.26, разработчики получили долгожданный и крайне эффективный инструмент — встроенный механизм отправки уведомлений с сервера на клиент без необходимости развертывания тяжелой инфраструктуры Системы взаимодействия. Данная возможность доступна «из коробки» на любых лицензиях (включая ПРОФ) и работает напрямую через сетевые протоколы платформы.

Давайте разберем по шагам, как настроить сквозное оповещение с использованием глобального свойства УведомленияКлиента (объект типа МенеджерУведомленийКлиента).

Шаг 1. Подписка на уведомление в форме (Сеанс 1)

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


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

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

Шаг 2. Отправка уведомления при записи (Сеанс 2)

В модуле объекта справочника или в обработчике события, который выполняет программную запись, мы инициируем отправку сообщения во все активные сеансы:


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

Важное преимущество метода: Платформа берет на себя всю низкоуровневую работу по маршрутизации сообщений. Сигнал доставляется практически мгновенно, не нагружая СУБД фоновыми запросами.

---

Сценарий 2. Использование Системы взаимодействия (1С:Диалог или собственный сервер)

Если ваша конфигурация работает на более ранних версиях платформы (до 8.3.26), но в системе развернута и настроена Система взаимодействия (через облачный сервис 1С:Диалог или локальный сервер взаимодействия), мы можем задействовать ее программный интерфейс (API).

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

Рассмотрим логику реализации:

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

Этот подход невероятно быстр и надежен, однако имеет инфраструктурные ограничения: требуется развертывание дополнительного ПО (сервер взаимодействия на Java с базами данных Elasticsearch и PostgreSQL) или наличие постоянного интернет-доступа для работы с бесплатным облачным сервером 1С. Кроме того, при копировании информационных баз настройки интеграции могут потребовать повторной инициализации.

---

Сценарий 3. Превентивная блокировка данных (Альтернативный подход)

Вместо того чтобы решать задачу «как обновить форму, когда данные уже изменились», мы можем пойти по превентивному пути: не дать второму сеансу изменить данные, пока первый сеанс держит форму открытой.

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

В форме элемента справочника при ее открытии вызовем метод управляемой формы ЗаблокироватьДанныеФормыДляРедактирования():


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

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

Минус подхода: Блокировка держится все время, пока у пользователя открыта форма. Если сотрудник откроет карточку контрагента и уйдет на обед, этот объект будет заблокирован для всех остальных процессов в системе на весь период его отсутствия. Используйте этот метод аккуратно и только там, где монопольный доступ действительно необходим.

---

Сценарий 4. Классический периодический опрос сервера по таймеру (Обработчик ожидания)

Если использование современных механизмов (Системы взаимодействия или платформы 8.3.26+) по каким-то причинам невозможно, остается классический «костыль», проверенный годами — периодический опрос сервера по таймеру.

Идея заключается в том, что клиентское приложение с определенным интервалом времени (например, каждые 10–15 секунд) «ходит» на сервер и проверяет, не изменилась ли версия открытого объекта.

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

Шаг 1. Инициализация обработчика ожидания на клиенте

При открытии формы запускаем фоновый таймер:


&НаКлиенте
Процедура ПриОткрытии(Отказ)
    // Подключаем проверку обновлений каждые 15 секунд
    ПодключитьОбработчикОжидания("ПроверитьОбновлениеОбъектаНаСервере", 15);
КонецПроцедуры

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

Шаг 2. Сверхлегкая серверная функция проверки версии

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


&НаСервереБезКонтекста
Функция ОбъектИзмененНаСервере(СсылкаНаОбъект, ТекущаяВерсияДанных)
    // Если ссылка пустая (новый элемент) — проверять нечего
    Если Не ЗначениеЗаполнено(СсылкаНаОбъект) Тогда
        Возврат Ложь;
    КонецЕсли;
    
    // Выполняем быстрый точечный запрос только к системному полю ВерсияДанных
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Номенклатура.ВерсияДанных КАК ВерсияДанных
        |ИЗ
        |   Справочник.Номенклатура КАК Номенклатура
        |ГДЕ
        |   Номенклатура.Ссылка = &Ссылка";
        
    Запрос.УстановитьПараметр("Ссылка", СсылкаНаОбъект);
    Выборка = Запрос.Выполнить().Выбрать();
    
    Если Выборка.Следующий() Тогда
        // Получаем строку версии из базы данных
        ВерсияВБД = ПолучитьСтрокуИзДвоичныхДанных(Выборка.ВерсияДанных);
        // Сравниваем ее с версией, которая сейчас отображается на клиенте
        Возврат (ВерсияВБД <> ТекущаяВерсияДанных);
    КонецЕсли;
    
    Возврат Ложь;
КонецФункции

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

---

Резюме и рекомендации

Подводя итог анализу механизмов межсеансового оповещения, мы можем составить следующую матрицу применимости решений:

← На главную