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