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

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

В практике разработки на платформе 1С:Предприятие часто возникает задача вычисления даты завершения какого-либо процесса (поможет автоматизация расчета пени и дат оплаты). Например, нужно рассчитать дату отгрузки через 3 рабочих дня после оплаты. Если просто использовать стандартную функцию ДобавитьКДате, система прибавит календарные дни, что приведет к ошибке, если на этот период выпадают суббота, воскресенье или государственные праздники.

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

Анализ проблемы: почему не подходит ДобавитьКДате

Функция ДобавитьКДате работает исключительно с астрономическим временем. Для нее день — это 86 400 секунд. Она не обладает информацией о том, является ли конкретное число в календаре рабочим или праздничным. Чтобы решить задачу, нам необходимо обратиться к источнику данных, где хранится график работы. В типовых конфигурациях 1С (ERP, УТ 11, ЗУП 3, БП 3) таким источником является справочник ПроизводственныеКалендари и соответствующий регистр сведений ДанныеПроизводственногоКалендаря — для этого есть расчет дат выхода на работу и выплат.

Решение №1: Использование Библиотеки стандартных подсистем (БСП)

Если вы работаете в современной типовой конфигурации, самый правильный и надежный метод — использовать программный интерфейс БСП. В составе БСП есть общий модуль КалендарныеГрафики, который содержит все необходимые инструменты. Для этой задачи есть расчет календарного плана и графиков задач.

Рассмотрим основной метод ДатаЗавершенияСрока. Проанализируем его параметры:

  1. ДатаНачала — точка отсчета (например, дата заказа).
  2. КоличествоДней — сколько рабочих дней нужно прибавить (может быть и отрицательным, если нужно отсчитать назад).
  3. ГрафикРаботы — ссылка на элемент справочника «Производственные календари» или «Календари».

Пример использования данного метода в коде:


// Определяем параметры
ДатаНачала = ТекущаяДата();
КоличествоРабочихДней = 2;
// Обычно используется основной производственный календарь РФ
КалендарьСсылка = Справочники.ПроизводственныеКалендари.НайтиПоНаименованию("Российская Федерация");

// Вызываем функцию БСП
ДатаРезультат = КалендарныеГрафики.ДатаЗавершенияСрока(ДатаНачала, КоличествоРабочихДней, КалендарьСсылка);

Сообщить("Дата завершения: " + ДатаРезультат);

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

Решение №2: Реализация логики через запрос

Бывают ситуации, когда расчет необходимо выполнить непосредственно в запросе (например, при компоновке данных в отчете или в динамическом списке) — удобнее через автоматизация построения сетевого графика проекта. В этом случае логика усложняется, так как нам нужно «пронумеровать» рабочие дни.

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

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

Посмотрим на пример структуры запроса (упрощенно):


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

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    РабочиеДниБудущего.ДатаРабочая
ИЗ
    ВТ_РабочиеДни КАК РабочиеДниТекущие
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_РабочиеДни КАК РабочиеДниБудущего 
        ПО РабочиеДниБудущего.НомерДня = РабочиеДниТекущие.НомерДня + &КоличествоДней
ГДЕ
    РабочиеДниТекущие.ДатаРабочая = &ДатаНачала

Важный нюанс: В реальном запросе 1С нет функции АВТОНОМЕРЗАПИСИ() (кроме СКД), поэтому порядковый номер обычно вычисляется через соединение таблицы с самой собой по условию Т1.Дата >= Т2.Дата и группировку с функцией КОЛИЧЕСТВО(Т2.Дата).

Решение №3: Программный перебор (универсальный цикл)

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

Пример реализации функции:


Функция ДобавитьРабочиеДни(ДатаНачала, КолДней, Календарь) Экспорт
    
    ТекущаяДатаРасчета = ДатаНачала;
    ДобавленоДней = 0;
    
    Пока ДобавленоДней < КолДней Цикл
        // Прибавляем один календарный день
        ТекущаяДатаРасчета = ТекущаяДатаРасчета + 86400; 
        
        // Проверяем статус дня в регистре
        ВидДня = РегистрыСведений.ДанныеПроизводственногоКалендаря.ПолучитьЗначениеРесурса(
            ТекущаяДатаРасчета, 
            "ВидДня", 
            Новый Структура("ПроизводственныйКалендарь", Календарь)
        );
        
        // Если день рабочий — увеличиваем счетчик
        Если ВидДня = Перечисления.ВидыДнейПроизводственногоКалендаря.Рабочий 
            ИЛИ ВидДня = Перечисления.ВидыДнейПроизводственногоКалендаря.Предпраздничный Тогда
            ДобавленоДней = ДобавленоДней + 1;
        КонецЕсли;
    КонецЦикла;
    
    Возврат ТекущаяДатаРасчета;
    
КонецФункции

Подведем итоги и рекомендации

Выясним, какой метод лучше выбрать в зависимости от ситуации:

  1. Если есть БСП: Всегда используйте КалендарныеГрафики.ДатаЗавершенияСрока. Это стандарт платформы, который учитывает все нюансы (переносы, праздники, разные часовые пояса).
  2. Если нужна производительность в отчете: Используйте метод с запросом и временными таблицами. Это позволит получить данные для тысяч строк одним обращением к БД.
  3. Проверка заполнения: Всегда помните, что производственный календарь должен быть заполнен на текущий и следующий год. Если в регистре нет данных на 1 января следующего года, любой алгоритм (и БСП, и ваш код) может выдать ошибку или пустую дату. Рекомендуется добавлять проверку на заполненность календаря перед расчетами.

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

← На главную