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