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

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

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

Анализ проблемы: почему стандартный перебор и реквизиты формы не подходят

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

Если внутри каждого шага выполнять поиск по объекту ДанныеФормыКоллекция (табличная часть или таблица формы), мы сталкиваемся со следующими препятствиями:

  1. Отсутствие встроенных быстрых методов поиска: У объекта ДанныеФормыКоллекция нет встроенного индексного поиска. Метод НайтиСтроки или последовательный цикл перебирают все строки заново. При количестве строк $N$ и числе отображаемых дат $M$ сложность алгоритма возрастает до $O(N \times M)$, что моментально вызывает подтормаживание пользовательского интерфейса при прокрутке.
  2. Невозможность использования Таблицы Значений на клиенте: Попытка использовать на клиенте тип ТаблицаЗначений с индексами приводит к ошибке, так как эта коллекция недоступна в контексте тонкого клиента.
  3. Сброс контекста локальных переменных: Если объявить временную переменную внутри процедуры, она будет пересоздаваться при каждом вызове события, обнуляя все результаты подготовки данных.
  4. Запрет вызовов сервера из обработчика отрисовки: Вызов серверных процедур из события ПриВыводеПериода недопустим. Контекстный вызов сервера при перерисовке приводит к лавинообразному зацикливанию интерфейса и зависанию программы.
  5. Несовпадение формата времени: В свойстве ОформлениеПериода.Даты каждая дата передается строго с нулевым временем (00:00:00). Если исходные данные содержат время, отличное от начала дня, прямое сравнение дат будет возвращать Ложь.

Оптимальное решение: кэширование в объекте Соответствие на клиенте

Наиболее производительный и архитектурно правильный способ — выполнить предварительную подготовку данных и сохранить их в клиентской переменной модуля формы с использованием объекта Соответствие. Поиск по ключу в Соответствие выполняется за константное время $O(1)$, что обеспечивает мгновенную отрисовку календаря.

Разберем пошагово реализацию этого механизма.

  1. Объявление переменной уровня модуля: Чтобы структура сохранялась между вызовами событий отрисовки, объявляем переменную в самом начале модуля формы до первой процедуры:
    
    &НаКлиенте
    Перем мКэшЗанятыхДат;
    
  2. Подготовка и инициализация кэша: Формируем соответствие заранее — при открытии формы или сразу после изменения исходной таблицы на клиенте/сервере. Даты обязательно нормализуем функцией НачалоДня().
    
    &НаСервере
    Функция ПолучитьСоответствиеЗанятыхДатСервер()
        Кэш = Новый Соответствие;
        ЦветЗанято = Новый Цвет(255, 200, 200); // Светло-красный цвет фона
        
        Для Каждого СтрТЗ Из Объект.ТаблицаЗанятости Цикл
            // Нормализуем дату к началу дня для точного сопоставления
            ДатаКлюч = НачалоДня(СтрТЗ.ДатаВПериоде);
            Кэш.Вставить(ДатаКлюч, ЦветЗанято);
        КонецЦикла;
        
        Возврат Кэш;
    КонецФункции
    
    &НаКлиенте
    Процедура ОбновитьКэшКалендаря()
        мКэшЗанятыхДат = ПолучитьСоответствиеЗанятыхДатСервер();
        Элементы.Календарь.Обновить();
    КонецПроцедуры
    
    &НаКлиенте
    Процедура ПриОткрытии(Отказ)
        ОбновитьКэшКалендаря();
    КонецПроцедуры
    
  3. Оформление ячеек в событии ПриВыводеПериода: В самом обработчике остается лишь получить значение из подготовленного соответствия по ключу текущей даты.
    
    &НаКлиенте
    Процедура КалендарьПриВыводеПериода(Элемент, ОформлениеПериода)
        Если мКэшЗанятыхДат = Неопределено Тогда
            Возврат;
        КонецЕсли;
        
        Для Каждого ТекОформление Из ОформлениеПериода.Даты Цикл
            ЦветДня = мКэшЗанятыхДат[ТекОформление.Дата];
            Если ЦветДня <> Неопределено Тогда
                ТекОформление.ЦветФона = ЦветДня;
            КонецЕсли;
        КонецЦикла;
    КонецПроцедуры
    

Альтернативные варианты и их ограничения

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

  1. Использование свойства ВыделенныеДаты элемента формы: У элемента ПолеКалендаря доступно свойство ВыделенныеДаты, куда можно передать массив дат. Однако этот способ имеет жесткое ограничение: выделенные даты подсвечиваются только стандартным цветом пользовательского выделения платформы. Он не позволяет задавать произвольный цвет текста, фона или выводить индивидуальные подсказки для разных мастеров и статусов.
  2. Использование элемента формы Планировщик: Если итоговая задача заключается в распределении занятости мастеров по времени и дням, более устойчивым архитектурным решением является переход от ПолеКалендаря к элементу Поле планировщика (реквизит формы с типом Планировщик). Этот объект платформы изначально спроектирован для вывода интервалов, расписаний и ресурсов, автоматически обрабатывает пересечения периодов и избавляет разработчика от необходимости вручную оптимизировать низкоуровневые события отрисовки ячеек.
← На главную