При разработке интеграций в среде 1С часто возникает стандартная архитектурная проблема: регламентное или фоновое задание успешно затягивает новые данные из внешней базы (например, MS Access, MS SQL или веб-сервиса), записывает их в базу данных 1С, но пользователи, у которых в этот момент открыты списки справочников или документов, не видят изменений до тех пор, пока не нажмут клавишу F5 или кнопку «Обновить».
Происходит это из-за четкого разделения контекстов платформы: фоновые задания выполняются на сервере в отдельном неинтерактивном сеансе. Они физически «не знают» о существовании открытых окон у конкретных пользователей и не могут напрямую вызвать клиентские процедуры обновления интерфейса вроде ОповеститьОбИзменении(). В этой статье мы подробно разберем, как решить эту задачу, проанализируем доступные методы, их плюсы и минусы, а также напишем рабочие примеры кода для реализации различных сценариев.
Давайте проанализируем ситуацию. Метод ОповеститьОбИзменении() предназначен для того, чтобы уведомить все открытые формы и динамические списки о том, что данные определенного типа (или конкретная ссылка) были изменены. Получив такое оповещение, динамические списки автоматически инициируют неявное перечитывание данных из СУБД.
Однако этот метод является строго клиентским. Попытка вызвать его в общем модуле, который выполняется на сервере под управлением регламентного задания, приведет к ошибке компиляции или выполнения: «Процедура или функция с указанным именем не определена». Серверный код фонового задания выполняется изолированно от клиентских сеансов. Чтобы обойти это ограничение, нам придется использовать специальные архитектурные подходы.
---
Самый простой и не требующий сложного программирования способ — использование стандартных свойств самого динамического списка формы. Рассмотрим этот вариант подробнее.
У каждого динамического списка в управляемой форме (в свойствах таблицы на форме) есть два важных параметра:
Если мы установим свойство АвтоОбновление в значение Истина и укажем период, например, 60 секунд, платформа сама, без написания какого-либо программного кода, будет каждые 60 секунд отправлять неявный запрос к базе данных и обновлять содержимое списка на экране пользователя.
Плюсы метода:
Минусы метода:
1Cv8.1CD в файловом режиме) будет постоянно подвергаться бомбардировке тяжелыми запросами, даже если фоновое задание ничего не обновляло. В файловом режиме это гарантированно приведет к частым блокировкам таблиц.---
Если мы хотим избежать бессмысленной циклической нагрузки на базу данных и обновлять списки только тогда, когда фоновое задание действительно внесло изменения, мы можем применить проверенную схему с использованием промежуточного флага.
Разберем по шагам алгоритм реализации этого метода:
Шаг 1. Создание вспомогательного регистра сведений
Создадим в конфигураторе простой регистр сведений (например, ИсторияОбновленияДанных) со следующей структурой:
ИмяСправочника (тип Строка, длина 100).ДатаПоследнегоОбновления (тип Дата, состав даты: Время).Шаг 2. Фиксация изменений на сервере
В коде общего модуля, который выполняется регламентным заданием, в самом конце процедуры успешного импорта данных добавим запись в наш регистр сведений:
Процедура ЗафиксироватьОбновлениеДанныхНаСервере() Экспорт
МенеджерЗаписи = РегистрыСведений.ИсторияОбновленияДанных.СоздатьМенеджерЗаписи();
МенеджерЗаписи.ИмяСправочника = "Номенклатура";
МенеджерЗаписи.ДатаПоследнегоОбновления = ТекущаяДатаСеанса();
МенеджерЗаписи.Записать(Истина);
КонецПроцедуры
Шаг 3. Настройка формы списка справочника
Теперь перейдем в форму списка нашего справочника (например, «Номенклатура»). В модуле формы объявим переменную для хранения локального времени последнего обновления и подключим обработчик ожидания при создании формы:
&НаКлиенте
Перем ЛокальнаяДатаОбновления;
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Получаем начальную дату при открытии формы
ЛокальнаяДатаОбновления = ПолучитьДатуОбновленияССервера();
КонецПроцедуры
&НаКлиенте
Процедура ПриОткрытии(Отказ)
// Подключаем обработчик ожидания, который будет срабатывать каждые 30 секунд
ПодключитьОбработчикОжидания("ПроверитьАктуальностьДанных", 30);
КонецПроцедуры
Шаг 4. Написание логики проверки и обновления
В обработчике ожидания мы будем вызывать легкую серверную функцию без контекста формы, чтобы проверить, не изменилась ли дата в регистре:
&НаКлиенте
Процедура ПроверитьАктуальностьДанных()
АктуальнаяДатаНаСервере = ПолучитьДатуОбновленияССервера();
Если АктуальнаяДатаНаСервере > ЛокальнаяДатаОбновления Тогда
// Данные на сервере обновились! Обновляем список у пользователя
ЛокальнаяДатаОбновления = АктуальнаяДатаНаСервере;
// Вариант 1: Обновление конкретного списка формы
Элементы.Список.Обновить();
// Вариант 2: Оповещение всей системы (обновятся все открытые списки этого типа)
ОповеститьОбИзменении(Тип("СправочникСсылка.Номенклатура"));
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьДатуОбновленияССервера()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ИсторияОбновленияДанных.ДатаПоследнегоОбновления КАК ДатаПоследнегоОбновления
|ИЗ
| РегистрСведений.ИсторияОбновленияДанных КАК ИсторияОбновленияДанных
|ГДЕ
| ИсторияОбновленияДанных.ИмяСправочника = &ИмяСправочника";
Запрос.УстановитьПараметр("ИмяСправочника", "Номенклатура");
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.ДатаПоследнегоОбновления;
КонецЕсли;
Возврат '00010101000000';
КонецФункции
Использование директивы &НаСервереБезКонтекста критически важно: такой вызов выполняется сервером мгновенно, не передает состояние всей формы (данные, реквизиты) туда и обратно, благодаря чему нагрузка на сеть и процессор сводится к абсолютному минимуму.
---
Если вы работаете на современной платформе (версии 8.3.10 и выше), наиболее технологичным решением является использование механизма Системы взаимодействия. Это встроенный инструмент 1С, позволяющий организовывать обмен сообщениями в реальном времени между сервером и клиентами (через веб-сокеты) по технологии Push.
При этом подходе клиенту вообще не нужно постоянно обращаться к серверу с проверками — сервер сам «толкнет» клиентские сеансы при необходимости.
Рассмотрим, как настроить этот процесс программно:
Шаг 1. Отправка Push-уведомления с сервера (в фоновом задании)
В регламентном задании после окончания обработки импорта пишем код отправки сообщения в специальный неинтерактивный канал (обсуждение):
Процедура ОтправитьСигналОбновленияКлиентам() Экспорт
Если НЕ СистемаВзаимодействия.Поддерживается() Тогда
Возврат;
КонецЕсли;
// В фоновом задании важно установить системного пользователя для работы с СВ
СистемаВзаимодействия.УстановитьТекущегоПользователя(ПользователиИнформационнойБазы.НайтиПоИмени("Администратор"));
// Ищем или создаем наше системное обсуждение
КлючОбсуждения = "ОбновлениеНоменклатурыСинхронизация";
ПараметрыОтбора = Новый Структура("Ключ", КлючОбсуждения);
СписокОбсуждений = СистемаВзаимодействия.ПолучитьОбсуждения(ПараметрыОтбора);
Если СписокОбсуждений.Количество() > 0 Тогда
Обсуждение = СписокОбсуждений[0];
Иначе
Обсуждение = СистемаВзаимодействия.СоздатьОбсуждение();
Обсуждение.Ключ = КлючОбсуждения;
Обсуждение.Наименование = "Системные уведомления о синхронизации";
Обсуждение.Записать();
КонецЕсли;
// Отправляем неинтерактивное сообщение-сигнал
НовоеСообщение = СистемаВзаимодействия.СоздатьСообщение(Обсуждение.Идентификатор);
НовоеСообщение.Текст = "REFRESH_NOMENCLATURE";
НовоеСообщение.Записать();
КонецПроцедуры
Шаг 2. Перехват сообщения на клиенте
В модуле управляемого приложения (или непосредственно в форме списка) подписываемся на получение сообщений этого обсуждения:
&НаКлиенте
Процедура ПриНачалеРаботыСистемы()
ПодключитьОбработчикСообщений();
КонецПроцедуры
&НаКлиенте
Процедура ПодключитьОбработчикСообщений()
// Подключаемся к обработчику системы взаимодействия
СистемаВзаимодействия.НачатьПодключениеОбработчикаНовыхСообщений(
Новый ОписаниеОповещения("ПолученоНовоеСообщениеСВ", ЭтотОбъект)
);
КонецПроцедуры
&НаКлиенте
Процедура ПолученоНовоеСообщениеСВ(Сообщение, ДополнительныеПараметры) Экспорт
Если Сообщение.Текст = "REFRESH_NOMENCLATURE" Тогда
// Срабатывает мгновенное обновление на клиенте без циклического опроса сервера!
ОповеститьОбИзменении(Тип("СправочникСсылка.Номенклатура"));
КонецЕсли;
КонецПроцедуры
Этот метод является самым изящным и производительным, так как полностью исключает лишнюю нагрузку на сервер БД.
---
В самых последних релизах платформы (начиная с версии 8.3.26) разработчики 1С представили долгожданный инструмент — МенеджерУведомленийКлиента. Этот механизм позволяет серверу отправлять сигналы напрямую в открытые сеансы без необходимости предварительной интеграции и настройки Системы взаимодействия.
Для отправки уведомления с сервера фоновое задание вызывает метод отправки сообщения в сеансы пользователей, а на клиенте регистрируется подписка на данные уведомления. Если ваша база работает на новейших версиях платформы, обязательно изучите этот встроенный класс объектов, так как он призван полностью стандартизировать сценарии клиент-серверного оповещения.
---
Проанализируем потенциальную проблему, которая часто возникает при автообновлении. Что произойдет, если пользователь открыл форму конкретного элемента справочника и начал вносить изменения, а в этот момент регламентное задание перезаписало этот же элемент в базе данных?
Если пользователь попытается сохранить свою форму, платформа выдаст ошибку: «Данные были изменены или удалены другим пользователем». Это происходит из-за несовпадения версии объекта в памяти формы и в базе данных.
Как минимизировать возникновение подобных конфликтов?
ТолькоПросмотр = Истина или ограничьте права на уровне ролей (разрешить только Чтение и Просмотр).Модифицированность:
Модифицированность = Ложь (пользователь ничего не менял на форме), мы можем безопасно вызвать метод ЭтаФорма.Прочитать(), чтобы обновить данные на актуальные из БД.Модифицированность = Истина, трогать форму нельзя. Можно деликатно предупредить пользователя всплывающим сообщением о том, что данные этого элемента были обновлены внешней системой.Мы подробно разобрали основные методы автоматического обновления открытых списков справочников в 1С. Выбирайте наиболее подходящий под ваши технические условия вариант: простое автообновление для небольших баз, паттерн с регистром сведений для надежной работы без перегрузки СУБД или современные Push-технологии платформы для интерактивных систем с высокой нагрузкой.