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

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

Зачастую при разработке механизмов контроля сроков оплаты, доставки или обработки документов возникает необходимость рассчитать «плановую» дату окончания срока, которая должна выпадать строго на рабочий день согласно производственному календарю. Прямое прибавление количества секунд (дней) к дате приводит к тому, что срок может истечь в субботу или праздничный день, что является недопустимым в бизнес-процессах. Рассмотрим, как решить эту задачу в системе 1С:Предприятие с помощью запросов.

Понимание проблемы

Основная трудность заключается в том, что производственный календарь содержит нерегулярные разрывы (выходные, государственные праздники, переносы выходных дней). Если мы просто используем ДОБАВИТЬКДАТЕ(), мы получим календарную дату. Нам же требуется «сдвинуть» дату на заданное количество именно рабочих дней. Если исходная дата документа сама приходится на выходной день, расчет должен корректно начинаться либо с текущего дня, либо с ближайшего следующего рабочего.

Решение методом индексации рабочих дней

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

  1. Создаем временную таблицу втДанныеКалендаря, в которую отбираем только рабочие и предпраздничные дни из регистра РегламентированныйПроизводственныйКалендарь.
  2. С помощью внутреннего соединения таблицы самой с собой мы рассчитываем индекс для каждой даты: чем больше дат в календаре меньше или равно текущей, тем выше индекс.
  3. Присоединяем к этой таблице наши документы. Если дата документа — выходной, мы находим ближайший рабочий день, с которого и начинаем отсчет.
  4. Прибавляем количество дней из договора к индексу и выполняем обратное соединение, чтобы получить итоговую рабочую дату.

Рассмотрим пример формирования таблицы индексов:


ВЫБРАТЬ
    ДанныеКалендаря1.ДатаКалендаря,
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ДанныеКалендаря2.ДатаКалендаря) КАК Индекс
ПОМЕСТИТЬ втДанныеКалендаряСИндексами
ИЗ
    втДанныеКалендаря КАК ДанныеКалендаря1
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ втДанныеКалендаря КАК ДанныеКалендаря2
        ПО ДанныеКалендаря1.ДатаКалендаря >= ДанныеКалендаря2.ДатаКалендаря
СГРУППИРОВАТЬ ПО
    ДанныеКалендаря1.ДатаКалендаря
ИНДЕКСИРОВАТЬ ПО
    Индекс

Как обрабатывать документы, созданные в выходные дни

Если документ создан в субботу, мы не можем просто прибавить дни к этой дате. Нам нужно «нормализовать» входную дату. Для этого перед расчетом мы должны найти ближайший рабочий день, который идет после даты документа. В запросе это реализуется через ВЫБРАТЬ ПЕРВЫЕ 1 с упорядочиванием по возрастанию даты:


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

Алгоритм расчета даты окончания

Когда у нас есть проиндексированный календарь и нормализованная дата отсчета, мы можем вычислить итоговую дату одной операцией соединения:


ВЫБРАТЬ
    Даты.Документ,
    КалендарьСИндексами.ДатаКалендаря КАК ДатаОкончания
ИЗ
    втНашиДокументыСИндексами КАК Даты
        ЛЕВОЕ СОЕДИНЕНИЕ втДанныеКалендаряСИндексами КАК КалендарьСИндексами
        ПО (КалендарьСИндексами.Индекс - Даты.ИндексИсходногоДня = Даты.КоличествоДней)

Практические рекомендации по оптимизации

Используя предложенный подход с индексацией рабочих дней, вы сможете легко справляться с любыми задачами по расчету сроков, независимо от того, попадают ли документы на праздники или выходные. Этот метод является наиболее устойчивым и масштабируемым для платформы 1С.

← На главную