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