При построении отчетов по взаиморасчетам и контролю дебиторской задолженности часто возникает задача: рассчитать плановую дату оплаты путем прибавления к дате документа отгрузки отсрочки, указанной в договоре в рабочих днях. В языке запросов 1С отсутствуют стандартные функции вроде ДОБАВИТЬКДАТЕРАБОЧИЕДНИ(), поэтому расчет выполняется через соединение с данными производственного календаря.
Разберем по шагам, как правильно реализовать такой расчет в запросе, какие нюансы важно учитывать и как оптимизировать производительность при обработке большого объема документов.
Для корректного смещения даты необходимо использовать регистр сведений, хранящий производственный календарь (в типовых конфигурациях это РегистрСведений.РегламентированныйПроизводственныйКалендарь или РегистрСведений.ДанныеПроизводственногоКалендаря). В расчет должны попадать только дни со значениями перечисления Рабочий и Предпраздничный.
Общая логика алгоритма строится следующим образом:
Дата1 <= Дата2, чтобы пронумеровать их или отобрать N-й по счету рабочий день.Рассмотрим рабочий и универсальный вариант запроса. Чтобы избежать тяжелых соединений на всей массе документов, сначала свернем исходные данные до уникальных пар Период и Отсрочка, рассчитаем для них даты оплаты и затем соединим с основной выборкой.
Посмотрим на пример текста запроса:
ВЫБРАТЬ
ВТ_Документы.Период КАК Период,
ВТ_Документы.ДнейОтсрочкиДолга КАК Отсрочка
ПОМЕСТИТЬ ИсходныеДаты
ИЗ
ВТ_Документы КАК ВТ_Документы
ИНДЕКСИРОВАТЬ ПО
Период,
Отсрочка
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
ИсходныеДаты.Период КАК Период,
ИсходныеДаты.Отсрочка КАК Отсрочка,
ДанныеКалендаря.ДатаКалендаря КАК РабочийДень
ПОМЕСТИТЬ СоединениеСРабочими
ИЗ
РегистрСведений.РегламентированныйПроизводственныйКалендарь КАК ДанныеКалендаря
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ИсходныеДаты КАК ИсходныеДаты
ПО ДанныеКалендаря.ДатаКалендаря >= ИсходныеДаты.Период
И ДанныеКалендаря.ДатаКалендаря < ДОБАВИТЬКДАТЕ(ИсходныеДаты.Период, ДЕНЬ, ИсходныеДаты.Отсрочка * 2 + 15)
ГДЕ
ДанныеКалендаря.ВидДня В (
ЗНАЧЕНИЕ(Перечисление.ВидыДнейПроизводственногоКалендаря.Рабочий),
ЗНАЧЕНИЕ(Перечисление.ВидыДнейПроизводственногоКалендаря.Предпраздничный)
)
ИНДЕКСИРОВАТЬ ПО
Период,
Отсрочка
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
СоединениеСРабочими.Период КАК Период,
СоединениеСРабочими.Отсрочка КАК Отсрочка,
СоединениеСРабочими1.РабочийДень КАК ДатаОплаты
ПОМЕСТИТЬ ВТ_ДатыОплат
ИЗ
СоединениеСРабочими КАК СоединениеСРабочими
ВНУТРЕННЕЕ СОЕДИНЕНИЕ СоединениеСРабочими КАК СоединениеСРабочими1
ПО СоединениеСРабочими.Период = СоединениеСРабочими1.Период
И СоединениеСРабочими.Отсрочка = СоединениеСРабочими1.Отсрочка
И СоединениеСРабочими.РабочийДень <= СоединениеСРабочими1.РабочийДень
СГРУППИРОВАТЬ ПО
СоединениеСРабочими.Период,
СоединениеСРабочими.Отсрочка,
СоединениеСРабочими1.РабочийДень
ИМЕЮЩИЕ
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ СоединениеСРабочими.РабочийДень) = СоединениеСРабочими.Отсрочка
ИНДЕКСИРОВАТЬ ПО
Период,
Отсрочка
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_Документы.Ссылка КАК Документ,
ВТ_Документы.Период КАК ДатаДокумента,
ВТ_Документы.ДнейОтсрочкиДолга КАК Отсрочка,
ВТ_ДатыОплат.ДатаОплаты КАК ДатаОплаты
ИЗ
ВТ_Документы КАК ВТ_Документы
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ДатыОплат КАК ВТ_ДатыОплат
ПО ВТ_Документы.Период = ВТ_ДатыОплат.Период
И ВТ_Документы.ДнейОтсрочкиДолга = ВТ_ДатыОплат.Отсрочка
Проанализируем технические нюансы, которые могут повлиять на корректность результата:
00:00:00). Если дата документа содержит время (например, 14:30:00), прямое условие ДатаКалендаря >= ДатаДокумента отсечет текущий день, даже если он рабочий. Поэтому перед сравнением следует приводить дату к началу дня с помощью функции НАЧАЛОПЕРИОДА(Документ.Дата, ДЕНЬ).NULL из-за отсутствия строк.Если запрос формируется для сложного отчета с иерархическими группировками, выполнение тета-соединений в СУБД может приводить к замедлению. В таких случаях эффективнее вынести расчет рабочих дней в программный код отчета:
ТаблицаЗначений).