При разработке алгоритмов расчета зарплаты и кадрового учета в 1С часто возникает задача: получить периоды оплаты по больничному листу, "вырезав" из них исключаемые периоды (например, отпуск за свой счет, учебный отпуск или дни удаленной работы). Если алгоритм изначально писался и тестировался на одном документе (с отбором по параметру &Ссылка), то при попытке применить его ко всем проведенным больничным за период разработчики часто сталкиваются с ошибкой: алгоритм начинает путать пересечения и смешивать периоды разных сотрудников и документов. Разберем по шагам, как правильно модифицировать такой запрос и какие нюансы предметной области стоит учесть.
Рассмотрим ситуацию. Имеется сложный запрос, который вычисляет фрагменты периодов. Он разбивает даты начала и окончания оплаты, присоединяет исключаемые периоды из табличной части, добавляет дни через функцию ДОБАВИТЬКДАТЕ и затем сворачивает это всё через соединения таблиц саму с собой для нахождения новых границ интервалов.
Когда мы убираем жесткий отбор ГДЕ БольничныйЛист.Ссылка = &Ссылка и пытаемся получить данные сразу по списку документов, запрос выдает некорректные результаты. Почему так происходит? Выясним причину: в соединениях виртуальных таблиц (особенно при самосоединениях для поиска "следующего" периода) отсутствуют условия связи по самому документу. Программа видит, что дата одного больничного меньше даты другого (совершенно чужого) больничного, и ошибочно объединяет их в один интервал.
В первую очередь уберем отбор по конкретной ссылке и добавим общие фильтры. Нам нужно отобрать только проведенные документы по нужной организации и за определенный период. Проанализируем, как это должно выглядеть в первом пакете запроса:
ВЫБРАТЬ
БольничныйЛист.ДатаНачалаОплаты КАК Начало,
БольничныйЛист.ДатаОкончанияОплаты КАК конец,
БольничныйЛист.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТТаблица1
ИЗ
Документ.БольничныйЛист КАК БольничныйЛист
ГДЕ
БольничныйЛист.Проведен = ИСТИНА
И БольничныйЛист.Организация = &Организация
И (БольничныйЛист.ДатаНачалаОплаты МЕЖДУ &ДатаНачала И &ДатаОкончания
ИЛИ БольничныйЛист.ДатаОкончанияОплаты МЕЖДУ &ДатаНачала И &ДатаОкончания
ИЛИ &ДатаНачала МЕЖДУ БольничныйЛист.ДатаНачалаОплаты И БольничныйЛист.ДатаОкончанияОплаты)
Те же самые отборы необходимо продублировать и во втором пакете при обращении к табличной части ИсключаемыеПериоды, чтобы не тянуть в память лишние данные.
Самый критичный момент — это соединения таблиц. Каждое левое или внутреннее соединение, которое раньше опиралось только на даты, теперь должно строго ограничиваться рамками одного документа. Посмотрим на исправленный итоговый запрос:
ВЫБРАТЬ
Таблица_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.Ссылка = СледующиеПериоды.Ссылка изолирует расчеты каждого больничного друг от друга, предотвращая "перепутывание пересечений".
Хотя предложенный выше математический подход с функциями ДОБАВИТЬКДАТЕ и множественными объединениями работает, при обработке массивов данных он сложен в отладке. Рассмотрим более надежную архитектурную альтернативу, которую часто используют опытные разработчики — разворот периодов по дням.
РегламентированныйПроизводственныйКалендарь или ДанныеПроизводственногоКалендаря).ЛЕВОЕ СОЕДИНЕНИЕ с таблицей исключаемых периодов по условию, что конкретный день попадает в интервал исключения.ЕСТЬ NULL.Этот подход (назовем его "поденным") работает абсолютно стабильно для любого количества документов, так как оперирует не отрезками дат, которые могут наслаиваться, а атомарными днями.
При работе с больничными и их оплатой необходимо учитывать современные реалии законодательства и новые механизмы платформы.
Сдвиг оплаты больничного: В последних релизах 1С:ЗУП (версии 3.1.30.152 / 3.1.32.39 и старше) появился функционал сдвига оплаты. По требованиям СФР, если первые дни болезни сотрудника пришлись на исключаемый период (например, отпуск за свой счет), то оплата за счет работодателя (первые 3 дня) не просто сгорает, а сдвигается и начинает отсчитываться строго после завершения исключаемого периода. Если ваш запрос строится для кастомного расчета дней, этот флаг в настройках необходимо проверять и учитывать в алгоритме.
Дистанционная работа во время болезни: Часто сотрудник на больничном продолжает трудиться удаленно. Такие дни попадают в табличную часть исключаемых периодов с причиной "Работа (в т.ч. удаленная)", но признак регистрации отсутствия для них снимается. В запросах обращайте внимание на причину исключения, так как такие дни табелируются как рабочие (Явки), и пособие за них не начисляется, но начисляется оклад.