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