При разработке кадровых отчетов часто возникает задача визуализации периодов (например, отпусков, командировок или графиков работ) в виде календарной сетки. Основная сложность заключается в том, что в базе данных один сотрудник может иметь несколько записей о периодах, а в отчете они должны располагаться в одной строке, где колонки соответствуют дням или неделям выбранного интервала. В этой статье мы подробно разберем, как реализовать такой механизм программно и с помощью встроенных инструментов системы «1С:Предприятие».
Это классический способ формирования отчета через макет. Основная идея заключается в том, чтобы не выводить строку в табличный документ до тех пор, пока мы не обработаем все записи по конкретному сотруднику. Рассмотрим этот алгоритм по шагам.
Во-первых, проанализируем структуру запроса. Чтобы данные было удобно обрабатывать, необходимо отсортировать результат по сотруднику и дате начала периода. Еще лучше — использовать группировки в самом запросе. Выясним, как правильно организовать цикл обработки:
// Пример структуры обхода выборки
ВыборкаСотрудники = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаСотрудники.Следующий() Цикл
// 1. Получаем область строки из макета (ФИО и пустые ячейки календаря)
ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
ОбластьСтрока.Параметры.Сотрудник = ВыборкаСотрудники.Сотрудник;
// 2. Вложенный цикл по детальным записям (конкретным отпускам)
ВыборкаПериоды = ВыборкаСотрудники.Выбрать();
Пока ВыборкаПериоды.Следующий() Цикл
// Определяем, какие ячейки в текущей области нужно закрасить
РазберемНиже();
КонецЦикла;
// 3. Только после обработки всех периодов выводим строку
ТабДок.Вывести(ОбластьСтрока);
КонецЦикла;
Важный нюанс: Если мы используем произвольный период, количество колонок в макете может меняться. В этом случае мы не можем полагаться на статичную область макета. Проанализируем ситуацию: нам нужно сначала вывести фиксированную левую часть (ФИО), а затем в цикле по дням периода использовать метод Присоединить().
Рассмотрим подробнее процесс заполнения календарной сетки. Если отчет строится по дням, нам нужно сопоставить дату отпуска с конкретной колонкой отчета. Для этого удобно использовать разницу в днях между датой начала отчета и датой начала конкретного отпуска.
Посмотрим на пример реализации заполнения ячеек:
// Предположим, ТекущаяДата - это день внутри интервала отпуска
// НачалоПериодаОтчета - дата начала формирования всего отчета
НомерКолонки = (НачалоДня(ТекущаяДата) - НачалоДня(НачалоПериодаОтчета)) / 86400 + 2;
// +2, так как первая колонка обычно занята ФИО
ОбластьЯчейка = ОбластьСтрока.Область(1, НомерКолонки);
ОбластьЯчейка.ЦветФона = ЦветаСтиля.ОсобоеБронирование; // Или любой нужный цвет
ОбластьЯчейка.Текст = "—";
Чтобы закрасить весь интервал, мы должны внутри цикла по периодам запустить еще один цикл, который будет «шагать» по дням от ДатаНачалаОтпуска до ДатаОкончанияОтпуска. Это гарантирует, что даже если у сотрудника два отпуска в одном месяце, они оба отобразятся в одной строке макета.
Многие программисты забывают, что СКД умеет решать задачу группировки данных «в строку» автоматически. Для этого используется тип отчета «Таблица». Выясним, как это настроить:
Сотрудник, а в «Колонки» — поле Дата (с дополнением периодов по дням).КоличествоДнейОтпуска).ЗначениеРесурса > 0, то установить Цвет фона в нужный вам цвет (например, зеленый).Преимущество этого метода в том, что 1С сама построит шапку, сама распределит данные по нужным ячейкам и сгруппирует нескольких Иванов в одну строку. Нам остается только правильно написать запрос, который выберет все даты внутри периодов отпусков (этого можно добиться соединением с производственным календарем или таблицей чисел).
Если задача стоит не просто «вывести в макет», а «сделать наглядно», проанализируем возможность использования объекта ДиаграммаГанта. Это встроенный объект 1С, предназначенный именно для отображения интервалов времени.
Посмотрим, как программно добавить данные в диаграмму, чтобы они шли в одну строку:
Диаграмма = Элементы.ДиаграммаОтпусков;
Диаграмма.Обновление = Ложь;
// Точка - это строка (наш сотрудник)
Точка = Диаграмма.УстановитьТочку(Выборка.Сотрудник);
// Серия - это группа данных (например, "Плановый отпуск")
Серия = Диаграмма.УстановитьСерию("Отпуска");
// Значение - это конкретный интервал
Значение = Диаграмма.ПолучитьЗначение(Точка, Серия);
// Добавляем интервал в существующее значение (это позволяет выводить несколько полосок в ряд)
Интервал = Значение.Добавить();
Интервал.Начало = Выборка.ДатаНачала;
Интервал.Конец = Выборка.ДатаОкончания;
Интервал.Текст = "Отпуск";
Диаграмма.Обновление = Истина;
Диаграмма Ганта автоматически масштабируется, позволяет настраивать шкалу времени (день, неделя, месяц) и избавляет разработчика от ручного рисования сетки в макете табличного документа.
Рассмотрим ситуацию, когда отчет работает медленно из-за большого количества сотрудников. Проблема часто кроется в том, что для каждой ячейки программа выполняет поиск в таблице значений или массиве. Чтобы этого избежать, рекомендуется подготовить данные в запросе таким образом, чтобы они были развернуты по дням.
Для этого в самописных конфигурациях часто используют вспомогательный регистр сведений «Календарь» или «Даты года». Соединив таблицу отпусков с такой таблицей дат по условию Календарь.Дата МЕЖДУ Отпуска.ДатаНачала И Отпуска.ДатаОкончания, мы получим готовую плоскую таблицу, где каждая строка — это один закрашенный «квадратик» в будущем отчете. Пройти по такой выборке один раз гораздо быстрее, чем постоянно вычислять интервалы в коде 1С.
Мы проанализировали несколько подходов к решению задачи. Для простых отчетов с фиксированным периодом подойдет программный обход макета с использованием метода Присоединить(). Для сложных отчетов с динамическими группировками лучше использовать СКД с условным оформлением. Если же важна интерактивность и современный вид — выбирайте диаграмму Ганта. В любом случае, главным правилом остается предварительная подготовка данных в запросе, чтобы минимизировать логические вычисления в цикле вывода.