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

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

В практике разработки на платформе 1С:Предприятие часто возникает задача определения даты предстоящего платежа или отгрузки, исходя из условий договора. Одной из самых коварных вариаций этой задачи является расчет даты с отсрочкой в рабочих днях, выполняемый непосредственно внутри запроса. Это критично для построения быстрых отчетов (например, по дебиторской задолженности), где недопустим расчет в цикле при обходе результата.

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

Метод 1: Использование производственного календаря и индексации рабочих дней

Это самый надежный и универсальный метод. Его суть заключается в том, что мы не пытаемся «вычислить» дату математически, а используем готовую таблицу-справочник. Рассмотрим подробнее, как это работает.

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

Разберем по шагам алгоритм в запросе:

  1. Выберем таблицу документов, для которых нужно рассчитать дату.
  2. Соединим ее с таблицей календаря по условию равенства дат, чтобы получить «порядковый номер рабочего дня» для даты документа.
  3. Прибавим к этому номеру количество дней отсрочки X.
  4. Снова соединим результат с таблицей календаря, но уже по новому номеру, чтобы получить итоговую дату.

Проанализируем пример кода запроса, использующего временные таблицы для оптимизации:


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

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Продажи.Ссылка КАК Документ,
    Продажи.Дата КАК ДатаДокумента,
    ВТ_Начало.НомерРабочегоДня + Продажи.СрокОтсрочки КАК ЦелевойНомер
ПОМЕСТИТЬ ВТ_РасчетНомеров
ИЗ
    Документ.РеализацияТоваровУслуг КАК Продажи
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_РабочиеДни КАК ВТ_Начало
    ПО Продажи.Дата = ВТ_Начало.Дата;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Расчет.Документ,
    ВТ_Конец.Дата КАК ДатаОплаты
ИЗ
    ВТ_РасчетНомеров КАК ВТ_Расчет
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_РабочиеДни КАК ВТ_Конец
    ПО ВТ_Расчет.ЦелевойНомер = ВТ_Конец.НомерРабочегоДня

Важный момент: Использование функции АВТОНОМЕРЗАПИСИ() или соединение таблицы саму с собой для генерации индекса позволяет превратить сложную календарную логику в простую арифметику.

Метод 2: Математический расчет (только субботы и воскресенья)

Если задача ограничивается только отсечением выходных (суббота и воскресенье) и не требует учета праздничных дней, мы можем применить математическую формулу. Посмотрим на ситуацию: в каждой неделе 5 рабочих дней и 2 выходных. Значит, каждые полные 5 рабочих дней отсрочки превращаются в 7 календарных дней.

Рассмотрим формулу смещения. Пусть X — количество рабочих дней отсрочки. Количество полных недель: Целое(X / 5). Количество добавленных календарных дней: (Целое(X / 5) * 7) + (X % 5). Однако здесь есть нюанс: если остаток дней (X % 5) при добавлении к начальной дате «перепрыгивает» через субботу или воскресенье, нужно добавить еще 2 дня.

В запросе 1С это реализуется через конструкцию ВЫБОР и функцию ДЕНЬНЕДЕЛИ. Разберем пример логики для отсрочки в 5 дней:


ВЫБРАТЬ
    Продажи.Ссылка,
    ВЫБОР
        КОГДА ДЕНЬНЕДЕЛИ(Продажи.Дата) + 5 > 5 
            ТОГДА ДОБАВИТЬКДАТЕ(Продажи.Дата, ДЕНЬ, 7)
        ИНАЧЕ ДОБАВИТЬКДАТЕ(Продажи.Дата, ДЕНЬ, 5)
    КОНЕЦ КАК ДатаОплаты
ИЗ
    Документ.РеализацияТоваровУслуг КАК Продажи

Для произвольного X формула становится значительно сложнее, так как требует учета того, на какой день недели выпал старт. Если X — переменная величина, лучше использовать Метод 1 или Метод 4.

Метод 3: Оконные функции (для платформы 8.3.20 и выше)

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

Мы можем использовать функцию ROW_NUMBER() непосредственно в запросе к календарю. Посмотрим на пример:


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

Этот подход идеологически похож на первый метод, но работает гораздо быстрее и читается проще. Мы получаем виртуальную таблицу, где каждый рабочий день имеет свой четкий индекс, и далее просто находим дату по индексу ИндексНачала + X.

Метод 4: Метод временных таблиц и «генератора чисел»

Проанализируем ситуацию, когда у нас нет доступа к производственному календарю или он не заполнен, а расчет нужен «здесь и сейчас». Мы можем сгенерировать последовательность чисел (от 1 до, например, 30) и прибавить их к дате документа.

Рассмотрим по шагам:

  1. Создаем временную таблицу с числами ВТ_Числа.
  2. Соединяем таблицу документов с ВТ_Числа. Получаем для каждого документа набор строк с будущими датами: ДОБАВИТЬКДАТЕ(ДатаДок, ДЕНЬ, Число).
  3. В этом наборе строк отфильтровываем те, где ДЕНЬНЕДЕЛИ(БудущаяДата) равен 6 или 7.
  4. Для каждого документа считаем количество оставшихся строк (это и есть рабочие дни).
  5. Выбираем ту БудущуюДату, для которой количество рабочих дней, прошедших с даты документа, в точности равно X.

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

Рекомендации по оптимизации

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

Подводя итог, можно сказать, что наиболее сбалансированным по производительности и сложности реализации является Метод 1 (индексация рабочего календаря). Он корректно обрабатывает и выходные, и праздники, и переносы дней, что невозможно реализовать чистой математикой без обращения к базе данных.

← На главную