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

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

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

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

Почему динамический список тормозит при частом автообновлении

Прежде чем переходить к способам решения, выясним причину возникновения задержек и «подвисаний»:

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

Решение 1. Оптимизированный легковесный опрос через маркер изменений (для платформ до 8.3.10)

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

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

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


&НаКлиенте
Переменная ДатаПредыдущегоОбновления;

&НаКлиенте
Процедура ПриОткрытии(Отказ)
    ДатаПредыдущегоОбновления = ПолучитьДатуПоследнегоЗаказаНаСервере();
    ПодключитьОбработчикОжидания("ПроверитьНеобходимостьОбновления", 1);
КонецПроцедуры

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

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

В процедуре записи робота добавляем обновление метки:


&НаСервере
Процедура ЗафиксироватьСозданиеЗаказа(НовыйЗаказСсылка)
    // Код записи документа Заказ
    // ...
    Константы.ДатаПоследнегоСозданияЗаказа.Установить(ТекущаяДатаСессии());
КонецПроцедуры

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

Решение 2. Использование Системы взаимодействия (для платформ 8.3.10 и выше)

Начиная с версии платформы 8.3.10, разработчикам доступен штатный механизм push-уведомлений от сервера к клиенту — Система взаимодействия. Этот механизм позволяет отказаться от любых таймеров и периодических опросов.

Рассмотрим порядок настройки оповещения между сеансами:

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

Посмотрим на программный код отправки оповещения из серверного кода:


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

На клиенте менеджера в модуле формы списка разместим обработку события:


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

&НаКлиенте
Процедура ОбработкаСообщенийТелефонии(Сообщение, ДополнительныеПараметры) Экспорт
    Если Сообщение.Текст = "СозданНовыйЗаказ" Тогда
        Элементы.Список.Обновить();
    КонецЕсли;
КонецПроцедуры

Решение 3. Генерация внешнего события на клиентских рабочих местах

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

Для реализации этого метода:

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

&НаКлиенте
Процедура ВнешнееСобытие(Источник, Событие, Данные)
    Если Источник = "AsteriskIntegration" И Событие = "NewCall" Тогда
        Элементы.Список.Обновить();
    КонецЕсли;
КонецПроцедуры

Решение 4. Архитектурная оптимизация телефонии и интерфейса

Помимо программного обновления списка, проанализируем саму архитектуру бизнес-процесса обработки звонка:

  1. Фильтрация событий Asterisk по операторам: Зачастую создание заказов в одном общем сеансе-роботе является архитектурной ошибкой. Протокол Asterisk AMI позволяет отслеживать событие соединения с конкретным оператором (события Bridge или AgentConnect). Если интегрировать компоненту телефонии в сеанс каждого менеджера с фильтрацией по его внутреннему номеру, необходимость в межсессионном обновлении отпадает: звонок принимается в сеансе ответившего сотрудника, и форма нового заказа открывается модально или блокирующе именно у него.
  2. Оптимизация динамического списка: Если заказов много, проверьте, указана ли в динамическом списке ОсновнаяТаблица. Без основной таблицы платформа не может построить эффективный постраничный запрос к таблице базы данных. Также хорошим решением может стать использование специализированного объекта конфигурации Журнал документов, который считывает ключевые поля быстрее, чем произвольный запрос по тяжелым таблицам заказов.
← На главную