Как адаптировать запрос с исключаемыми периодами больничного для множества документов в 1С?

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

При разработке алгоритмов расчета зарплаты и кадрового учета в 1С часто возникает задача: получить периоды оплаты по больничному листу, "вырезав" из них исключаемые периоды (например, отпуск за свой счет, учебный отпуск или дни удаленной работы). Если алгоритм изначально писался и тестировался на одном документе (с отбором по параметру &Ссылка), то при попытке применить его ко всем проведенным больничным за период разработчики часто сталкиваются с ошибкой: алгоритм начинает путать пересечения и смешивать периоды разных сотрудников и документов. Разберем по шагам, как правильно модифицировать такой запрос и какие нюансы предметной области стоит учесть.

Анализ исходной проблемы

Рассмотрим ситуацию. Имеется сложный запрос, который вычисляет фрагменты периодов. Он разбивает даты начала и окончания оплаты, присоединяет исключаемые периоды из табличной части, добавляет дни через функцию ДОБАВИТЬКДАТЕ и затем сворачивает это всё через соединения таблиц саму с собой для нахождения новых границ интервалов.

Когда мы убираем жесткий отбор ГДЕ БольничныйЛист.Ссылка = &Ссылка и пытаемся получить данные сразу по списку документов, запрос выдает некорректные результаты. Почему так происходит? Выясним причину: в соединениях виртуальных таблиц (особенно при самосоединениях для поиска "следующего" периода) отсутствуют условия связи по самому документу. Программа видит, что дата одного больничного меньше даты другого (совершенно чужого) больничного, и ошибочно объединяет их в один интервал.

Шаг 1. Добавление глобальных отборов и группировок

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


ВЫБРАТЬ
    БольничныйЛист.ДатаНачалаОплаты КАК Начало,
    БольничныйЛист.ДатаОкончанияОплаты КАК конец,
    БольничныйЛист.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТТаблица1
ИЗ
    Документ.БольничныйЛист КАК БольничныйЛист
ГДЕ
    БольничныйЛист.Проведен = ИСТИНА
    И БольничныйЛист.Организация = &Организация
    И (БольничныйЛист.ДатаНачалаОплаты МЕЖДУ &ДатаНачала И &ДатаОкончания
        ИЛИ БольничныйЛист.ДатаОкончанияОплаты МЕЖДУ &ДатаНачала И &ДатаОкончания
        ИЛИ &ДатаНачала МЕЖДУ БольничныйЛист.ДатаНачалаОплаты И БольничныйЛист.ДатаОкончанияОплаты)

Те же самые отборы необходимо продублировать и во втором пакете при обращении к табличной части ИсключаемыеПериоды, чтобы не тянуть в память лишние данные.

Шаг 2. Обязательное условие соединения по Ссылке

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


ВЫБРАТЬ
    Таблица_1.Период КАК ДатаНачала,
    МИНИМУМ(ДОБАВИТЬКДАТЕ(СледующиеПериоды.Период, ДЕНЬ, -1)) КАК ДатаКонец,
    Таблица_1.Ссылка КАК Ссылка
ПОМЕСТИТЬ Таблица_2
ИЗ
    Таблица_1 КАК Таблица_1
        ЛЕВОЕ СОЕДИНЕНИЕ Таблица_1 КАК СледующиеПериоды
        // КЛЮЧЕВОЕ ИСПРАВЛЕНИЕ ЗДЕСЬ:
        ПО Таблица_1.Период < СледующиеПериоды.Период
        И Таблица_1.Ссылка = СледующиеПериоды.Ссылка
ГДЕ
    НЕ СледующиеПериоды.Период ЕСТЬ NULL
СГРУППИРОВАТЬ ПО
    Таблица_1.Период,
    Таблица_1.Ссылка
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Таблица_2.ДатаНачала КАК ДатаНачала,
    Таблица_2.ДатаКонец КАК ДатаКонец,
    Таблица_2.Ссылка КАК Ссылка
ПОМЕСТИТЬ ИТОГПериодов
ИЗ
    Таблица_2 КАК Таблица_2
        ЛЕВОЕ СОЕДИНЕНИЕ ВТТаблица2 КАК ВТТаблица2
        ПО Таблица_2.ДатаНачала = ВТТаблица2.Начало
            И Таблица_2.ДатаКонец = ВТТаблица2.конец
            // И ЗДЕСЬ ТОЖЕ:
            И Таблица_2.Ссылка = ВТТаблица2.Ссылка
ГДЕ
    ВТТаблица2.Начало ЕСТЬ NULL

Добавление Таблица_1.Ссылка = СледующиеПериоды.Ссылка изолирует расчеты каждого больничного друг от друга, предотвращая "перепутывание пересечений".

Альтернативный архитектурный подход: Метод Календаря

Хотя предложенный выше математический подход с функциями ДОБАВИТЬКДАТЕ и множественными объединениями работает, при обработке массивов данных он сложен в отладке. Рассмотрим более надежную архитектурную альтернативу, которую часто используют опытные разработчики — разворот периодов по дням.

  1. Создаем виртуальную таблицу со всеми датами за выбранный период (например, из регистра сведений РегламентированныйПроизводственныйКалендарь или ДанныеПроизводственногоКалендаря).
  2. Соединяем её с периодами больничных листов. Одна строка больничного превращается в N строк по количеству дней.
  3. Делаем ЛЕВОЕ СОЕДИНЕНИЕ с таблицей исключаемых периодов по условию, что конкретный день попадает в интервал исключения.
  4. Отсеиваем дни исключений через условие ЕСТЬ NULL.
  5. Оставшиеся дни сворачиваем обратно в интервалы.

Этот подход (назовем его "поденным") работает абсолютно стабильно для любого количества документов, так как оперирует не отрезками дат, которые могут наслаиваться, а атомарными днями.

Особенности учета исключаемых периодов в новых версиях 1С:ЗУП

При работе с больничными и их оплатой необходимо учитывать современные реалии законодательства и новые механизмы платформы.

Сдвиг оплаты больничного: В последних релизах 1С:ЗУП (версии 3.1.30.152 / 3.1.32.39 и старше) появился функционал сдвига оплаты. По требованиям СФР, если первые дни болезни сотрудника пришлись на исключаемый период (например, отпуск за свой счет), то оплата за счет работодателя (первые 3 дня) не просто сгорает, а сдвигается и начинает отсчитываться строго после завершения исключаемого периода. Если ваш запрос строится для кастомного расчета дней, этот флаг в настройках необходимо проверять и учитывать в алгоритме.

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

← На главную