Как отследить неактивность пользователя в 1С и вернуть форму в исходное состояние

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

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

Базовый подход: Использование клиентского обработчика ожидания

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

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

Рассмотрим пример реализации в модуле управляемой формы нашего рабочего места:


&НаКлиенте
Перем ВремяПоследнейАктивности; // Переменная для хранения времени действия

&НаКлиенте
Процедура ПриОткрытии(Отказ)
    
    // Инициализируем время при старте работы с формой
    ВремяПоследнейАктивности = ТекущаяДата();
    
    // Подключаем фоновый опросчик. Интервал проверки — 10 секунд.
    // Процедура "ПроверитьПростойПользователя" должна быть экспортной на клиенте.
    ПодключитьОбработчикОжидания("ПроверитьПростойПользователя", 10, Ложь);
    
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьПростойПользователя() Экспорт
    
    ТекущееВремя = ТекущаяДата();
    СекундПростоя = ТекущееВремя - ВремяПоследнейАктивности;
    
    // Лимит неактивности — 5 минут (300 секунд)
    Если СекундПростоя >= 300 Тогда
        
        // Отключаем обработчик на время сброса, чтобы избежать зацикливания
        ОтключитьОбработчикОжидания("ПроверитьПростойПользователя");
        
        ВыполнитьОчисткуИВозвратВИсходноеСостояние();
        
        // После успешного сброса инициализируем таймер заново
        ВремяПоследнейАктивности = ТекущаяДата();
        ПодключитьОбработчикОжидания("ПроверитьПростойПользователя", 10, Ложь);
        
    КонецЕсли;
    
КонецПроцедуры

Чтобы этот механизм работал корректно, нам необходимо обновлять переменную ВремяПоследнейАктивности при каждом действии пользователя. Развешивать обработчик ПриИзменении на каждый реквизит формы — трудоемкое и неблагодарное занятие. Вместо этого мы можем использовать стандартное событие формы ПриАктивизацииЭлемента или интерактивные действия с табличными частями. Как только пользователь кликает по любому элементу или переходит в другое поле, мы актуализируем время:


&НаКлиенте
Процедура ПриАктивизацииЭлемента(Элемент)
    ВремяПоследнейАктивности = ТекущаяДата();
КонецПроцедуры

Продвинутый подход: Хеширование состояния интерфейса

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

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

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

Давайте посмотрим на пример реализации такого "слепка" состояния:


&НаКлиенте
Перем ПредыдущийСлепокСостояния;
&НаКлиенте
Перем ВремяСтабильногоСостояния;

&НаКлиенте
Процедура ПриОткрытии(Отказ)
    ПредыдущийСлепокСостояния = "";
    ВремяСтабильногоСостояния = ТекущаяДата();
    ПодключитьОбработчикОжидания("АнализироватьСостояниеИнтерфейса", 15, Ложь);
КонецПроцедуры

&НаКлиенте
Процедура АнализироватьСостояниеИнтерфейса() Экспорт
    
    ТекущийСлепок = СформироватьСлепокИнтерфейса();
    
    Если ТекущийСлепок = ПредыдущийСлепокСостояния Тогда
        // Состояние интерфейса не изменилось с момента прошлой проверки
        Простой = ТекущаяДата() - ВремяСтабильногоСостояния;
        Если Простой >= 300 Тогда
            СброситьИнтерфейс();
        КонецЕсли;
    Else
        // Зафиксировано действие или перемещение фокуса. Сбрасываем счетчик простоя.
        ПредыдущийСлепокСостояния = ТекущийСлепок;
        ВремяСтабильногоСостояния = ТекущаяДата();
    КонецЕсли;
    
КонецПроцедуры

&НаКлиенте
Функция СформироватьСлепокИнтерфейса()
    
    Результат = "";
    
    // Фиксируем фокус ввода
    Если ТекущийЭлемент <> Неопределено Тогда
        Результат = Результат + ТекущийЭлемент.Имя;
    КонецЕсли;
    
    // Фиксируем количество и состав окон
    МассивОкон = ПолучитьОкна();
    Результат = Результат + "_Окон:" + Строка(МассивОкон.Количество());
    
    Для Каждого ТекущееОкно Из МассивОкон Цикл
        Результат = Результат + "_" + ТекущееОкно.Заголовок;
    КонецЦикла;
    
    Возврат Результат;
    
КонецФункции

Особенности работы в веб-клиенте (HTML5 + JavaScript)

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

JavaScript умеет нативно перехватывать любые низкоуровневые движения мыши (mousemove), нажатия клавиш на клавиатуре (keydown) и касания тач-скрина (touchstart) на веб-странице. Разберем этот сценарий подробнее:

  1. Добавим на управляемую форму реквизит с типом Строка и перетащим его на форму как элемент с видом Поле HTML-документа. Назовем его ДетекторАктивности. Сделаем его минимального размера (например, 1х1 пиксель), чтобы он не мешал пользователю.
  2. При создании формы на сервере загрузим в это поле простую HTML-страницу с JS-скриптом, который будет слушать глобальные события браузера и передавать сигнал в 1С при обнаружении активности.

Код инициализации HTML-поля на сервере:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    // Формируем HTML-код со скриптом отслеживания событий
    ДетекторАктивности = 
    "<html>" +
    "<head>" +
    "<script type='text/javascript'>" +
    "   var lastActivity = Date.now();" +
    "   function resetTimer() {" +
    "       lastActivity = Date.now();" +
    "       // Вызываем внешнее событие 1С для уведомления об активности" +
    "       window.external.raiseEvent('UserActivity', 'active');" +
    "   }" +
    "   window.onload = function() {" +
    "       document.addEventListener('mousemove', resetTimer);" +
    "       document.addEventListener('keypress', resetTimer);" +
    "       document.addEventListener('touchstart', resetTimer);" +
    "   };" +
    "</script>" +
    "</head>" +
    "<body style='margin:0; padding:0; background:transparent;'>" +
    "</body>" +
    "</html>";
    
КонецПроцедуры

Теперь в модуле управляемой формы нам достаточно обрабатывать событие ПриНажатии или ОнажатиеКнопки (в зависимости от версии платформы — обработка внешних событий от HTML-поля). При получении события "active" мы просто обновляем нашу внутреннюю переменную 1С ВремяПоследнейАктивности = ТекущаяДата();.

Важный нюанс: Спящие сеансы и вызовы сервера

Критически важный момент для оптимизации производительности СУБД и экономии лицензий! На уровне настроек кластера серверов 1С существуют параметры «Время засыпания пассивного сеанса» и «Время завершения спящего сеанса». Если пользователь не работает в системе, сервер 1С автоматически переводит его сеанс в спящий режим и освобождает лицензию.

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

Правило разработки: Процедура, вызываемая обработчиком ожидания для проверки простоя, должна выполняться исключительно на клиенте (&НаКлиенте) и оперировать только локальными клиентскими переменными. Обращение к серверу должно происходить один-единственный раз — непосредственно в момент сброса формы, когда пятиминутный интервал неактивности уже подтвержден.

Эргономика и UX: Защита от случайного сброса данных

Давайте проанализируем реальный жизненный сценарий. Бабушка на кассе самообслуживания или клиент у платежного терминала ввели все данные, но замешкались, доставая кошелек или подсчитывая наличные деньги. Ровно через 5 минут система молча стирает все введенные данные и возвращается на начальный экран. Это вызовет сильный стресс и ступор у пользователя.

Поэтому перед тем, как произвести жесткий сброс и очистку, **за 30 секунд до истечения таймаута необходимо предупредить пользователя**. На экран выводится яркое, возможно, мигающее окно со звуковым сопровождением и обратным отсчетом времени:

«Вы еще здесь? Сеанс будет сброшен через 30... 29... 28...»

На этой форме должна быть одна большая кнопка: «Продолжить работу». Если пользователь нажимает ее, мы закрываем предупреждение, обновляем метку времени активности и продолжаем работу. Если по истечении 30 секунд реакция не последовала — только тогда система закрывает все окна и обнуляет данные.

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


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

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

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

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

← На главную