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