При разработке многопользовательских систем на платформе 1С:Предприятие часто возникает задача синхронизации интерфейсов. Например, когда один менеджер создает или изменяет документ Реализация товаров и услуг, другие пользователи, у которых в этот момент открыт список этих документов, должны сразу увидеть изменения. В этой статье мы подробно разберем, почему стандартные методы оповещения могут не работать в этой ситуации, и рассмотрим все доступные способы решения задачи: от самых простых до технологически продвинутых.
Начинающие разработчики часто пытаются использовать метод Оповестить() или ОповеститьОбИзменении(). Давайте проанализируем, почему это не дает нужного результата для других пользователей. Рассмотрим типичный код, который часто пишут в форме объекта:
&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
// Это оповещение сработает ТОЛЬКО в текущем сеансе
Оповестить("Запись_Реализация", ПараметрыЗаписи, Объект.Ссылка);
КонецПроцедуры
Методы Оповестить() и ОповеститьОбИзменении() являются "широковещательными" только в пределах одного конкретного сеанса. Это означает, что если у одного пользователя открыто три разных окна (например, список, форма документа и отчет), сообщение дойдет до каждого из них. Однако другой пользователь, работающий в своем отдельном сеансе, это сообщение не получит. Платформа 1С спроектирована так, чтобы минимизировать сетевой трафик между клиентами, поэтому прямая передача сигналов между сеансами через эти методы невозможна.
Самый простой и не требующий сложного программирования способ — это использование стандартного свойства динамического списка. Рассмотрим, как это настроить в конфигураторе или программно.
У каждого динамического списка (который обычно отображается в форме списка документов) есть свойство Период автообновления. По умолчанию оно равно 0 (выключено).
ДинамическийСписок (обычно он называется Список).Важный нюанс: Платформа рекомендует устанавливать значения не менее 60 секунд для снижения нагрузки на сервер. Однако на практике, если пользователей немного, а запрос списка оптимизирован, можно установить 20–30 секунд. Выясним причину, по которой не стоит ставить слишком маленькие значения (например, 1–3 секунды): каждое обновление — это полноценный запрос к базе данных, который при большом количестве пользователей может привести к существенным задержкам в работе SQL-сервера.
Проанализируем более современный подход, который появился в платформе 8.3. — Систему взаимодействия. Это отдельный компонент (сервер), который позволяет сеансам обмениваться сообщениями в реальном времени. Это наиболее "чистое" решение, так как оно работает по принципу Push: список обновится только тогда, когда действительно произошло изменение.
Разберем примерную схему реализации:
В модуле формы документа (или в подписке на событие) при записи мы отправляем сообщение в специальный канал:
&НаСервере
Процедура ПослеЗаписиНаСервере(ТекущийОбъект, ПараметрыЗаписи)
СистемаВзаимодействия.ОтправитьСообщение("ОбновлениеСпискаРеализаций", "ДокументИзменен");
КонецПроцедуры
А в форме списка мы должны "подписаться" на это событие. В процедуре ПриОткрытии или ПриСозданииНаСервере настраивается обработчик:
&НаКлиенте
Процедура ПриОткрытии(Отказ)
// Подключаем обработку сообщений системы взаимодействия
// Здесь упрощенный пример логики
Элементы.Список.Обновить();
КонецПроцедуры
Основное преимущество этого метода в том, что система не делает "холостых" запросов к базе данных каждые N секунд. Обновление происходит мгновенно после фиксации транзакции записи другим пользователем.
Если Система взаимодействия не используется, можно применить классический метод с созданием буферной таблицы. Посмотрим, как это работает по шагам:
ОчередьОбновлений с измерениями ИмяТаблицы и ресурсом ДатаПоследнегоИзменения.ПередЗаписью или подписку) обновляем запись в этом регистре для текущего типа документа, устанавливая текущую дату и время.ПодключитьОбработчикОжидания() запускаем процедуру, которая будет срабатывать, например, раз в 15 секунд.ОчередьОбновлений. Если дата в регистре больше, чем дата последнего обновления в этой форме, значит, пора вызвать Элементы.Список.Обновить().Рассмотрим пример кода для формы списка:
&НаКлиенте
Процедура ПриОткрытии(Отказ)
ПодключитьОбработчикОжидания("ПроверитьОбновленияСписка", 15);
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьОбновленияСписка()
Если НужноОбновитьСписокНаСервере() Тогда
Элементы.Список.Обновить();
КонецЕсли;
КонецПроцедуры
Такой подход гораздо эффективнее полного автообновления, так как проверка одной записи в маленьком регистре практически не нагружает систему по сравнению с выполнением тяжелого запроса динамического списка (особенно если в нем есть сложные соединения и условия).
Независимо от выбранного способа обновления, важно убедиться, что сам динамический список работает максимально быстро. Проанализируем ключевые настройки:
Подведем итог: если вам нужно быстрое и простое решение для небольшой базы — используйте стандартный Период автообновления в свойствах списка (от 20 до 60 секунд). Если же система высоконагруженная и пользователей много, стоит рассмотреть Систему взаимодействия или механизм с регистром уведомлений, чтобы сберечь ресурсы сервера.