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