Описание задачи:
Есть задача начисления процентов по займам за произвольный период, включая случаи кратного количества лет, т.е. в общем случае, когда обе даты начала и конца периода лежат в разных годах. Займы могут выплачиваться частями. Кроме того, процентные ставки по договорам займа могут меняться в любой момент (день). Эта задача была сделана в платформе 7.7, но с помощью объектной модели запросов по бухгалтерским итогам и таблицы значений. Сейчас я сделал исключительно через механизм запросов.
ВЫБРАТЬ
РегистрБухгалтерииХозрасчетныйОстаткиИОбороты.Счет КАК Счет,
РегистрБухгалтерииХозрасчетныйОстаткиИОбороты.Субконто1 КАК Контрагент,
РегистрБухгалтерииХозрасчетныйОстаткиИОбороты.Субконто2 КАК Договор,
РегистрБухгалтерииХозрасчетныйОстаткиИОбороты.СуммаКонечныйОстаток КАК СуммаЗайма,
РегистрБухгалтерииХозрасчетныйОстаткиИОбороты.Период
ПОМЕСТИТЬ Врем1
ИЗ
РегистрБухгалтерии.РегистрБухгалтерииХозрасчетный.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, День, ДвиженияИГраницыПериода, Счет = ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.ВыданныеЗаймы), , ) КАК РегистрБухгалтерииХозрасчетныйОстаткиИОбороты
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ПроцентныеСтавкиПоЗаймам.Период,
ПроцентныеСтавкиПоЗаймам.Договор,
ПроцентныеСтавкиПоЗаймам.ПроцентнаяСтавка
ПОМЕСТИТЬ Врем2
ИЗ
РегистрСведений.ПроцентныеСтавкиПоЗаймам КАК ПроцентныеСтавкиПоЗаймам
ГДЕ
ПроцентныеСтавкиПоЗаймам.Период И ПроцентныеСтавкиПоЗаймам.Период >= &НачалоПериода
И ПроцентныеСтавкиПоЗаймам.Договор В
(ВЫБРАТЬ РАЗЛИЧНЫЕ
Врем1.Договор
ИЗ
Врем1 КАК Врем1)
ОБЪЕДИНИТЬ
ВЫБРАТЬ
ПроцентныеСтавкиПоЗаймамСрезПоследних.Период,
ПроцентныеСтавкиПоЗаймамСрезПоследних.Договор,
ПроцентныеСтавкиПоЗаймамСрезПоследних.ПроцентнаяСтавка
ИЗ
РегистрСведений.ПроцентныеСтавкиПоЗаймам.СрезПоследних(
&НачалоПериода,
Договор В
(ВЫБРАТЬ РАЗЛИЧНЫЕ
Врем1.Договор
ИЗ
Врем1 КАК Врем1)) КАК ПроцентныеСтавкиПоЗаймамСрезПоследних
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Врем1.Счет,
Врем1.Контрагент,
Врем1.Договор,
Врем2.Период,
Врем1.СуммаЗайма,
МАКСИМУМ(Врем1.Период) КАК Период1
ПОМЕСТИТЬ Врем3
ИЗ
Врем1 КАК Врем1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Врем2 КАК Врем2
ПО Врем1.Договор = Врем2.Договор
И Врем1.Период
СГРУППИРОВАТЬ ПО
Врем1.Счет,
Врем1.Контрагент,
Врем1.Договор,
Врем2.Период,
Врем1.СуммаЗайма
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Врем1.Счет КАК Счет,
Врем1.Контрагент КАК Контрагент,
Врем1.Договор КАК Договор,
ДОБАВИТЬКДАТЕ(КОНЕЦПЕРИОДА(Врем1.Период, ГОД), СЕКУНДА, 1) КАК Период,
МАКСИМУМ(Врем1.Период) КАК Период1
ПОМЕСТИТЬ Врем4
ИЗ
Врем1 КАК Врем1
ГДЕ
ДОБАВИТЬКДАТЕ(КОНЕЦПЕРИОДА(Врем1.Период, ГОД), СЕКУНДА, 1)
СГРУППИРОВАТЬ ПО
Врем1.Счет,
Врем1.Контрагент,
Врем1.Договор,
ДОБАВИТЬКДАТЕ(КОНЕЦПЕРИОДА(Врем1.Период, ГОД), СЕКУНДА, 1)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Врем4.Счет,
Врем4.Контрагент,
Врем4.Договор,
Врем4.Период,
Врем1.СуммаЗайма
ПОМЕСТИТЬ Врем5
ИЗ
Врем1 КАК Врем1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Врем4 КАК Врем4
ПО Врем1.Счет = Врем4.Счет
И Врем1.Контрагент = Врем4.Контрагент
И Врем1.Период = Врем4.Период1
И Врем1.Договор = Врем4.Договор
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Врем1.Счет КАК Счет,
Врем1.Контрагент КАК Контрагент,
Врем1.Договор КАК Договор,
Врем1.Период КАК Период,
Врем1.СуммаЗайма КАК СуммаЗайма
ПОМЕСТИТЬ Врем6
ИЗ
Врем1 КАК Врем1
ОБЪЕДИНИТЬ
ВЫБРАТЬ
Врем3.Счет,
Врем3.Контрагент,
Врем3.Договор,
Врем3.Период,
Врем3.СуммаЗайма
ИЗ
Врем3 КАК Врем3
ОБЪЕДИНИТЬ
ВЫБРАТЬ
Врем5.Счет,
Врем5.Контрагент,
Врем5.Договор,
Врем5.Период,
Врем5.СуммаЗайма
ИЗ
Врем5 КАК Врем5
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Врем6.Счет,
Врем6.Контрагент,
Врем6.Договор,
Врем6.Период,
Врем6.СуммаЗайма,
МАКСИМУМ(Врем2.Период) КАК Период1
ПОМЕСТИТЬ Врем7
ИЗ
Врем6 КАК Врем6
ЛЕВОЕ СОЕДИНЕНИЕ Врем2 КАК Врем2
ПО Врем6.Договор = Врем2.Договор
И Врем6.Период >= Врем2.Период
СГРУППИРОВАТЬ ПО
Врем6.Счет,
Врем6.Контрагент,
Врем6.Договор,
Врем6.Период,
Врем6.СуммаЗайма
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Врем7.Счет,
Врем7.Контрагент,
Врем7.Договор,
Врем7.Период,
Врем7.СуммаЗайма,
ЕСТЬNULL(Врем2.ПроцентнаяСтавка, 0) КАК ПроцентнаяСтавка
ПОМЕСТИТЬ Врем8
ИЗ
Врем7 КАК Врем7
ЛЕВОЕ СОЕДИНЕНИЕ Врем2 КАК Врем2
ПО Врем7.Договор = Врем2.Договор
И Врем7.Период1 = Врем2.Период
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Врем8.Период КАК НачалоПериода,
МИНИМУМ(ЕСТЬNULL(ДОБАВИТЬКДАТЕ(Врем81.Период, СЕКУНДА, -1), &КонецПериода)) КАК КонецПериода,
Врем8.Счет,
Врем8.Контрагент,
Врем8.Договор,
Врем8.СуммаЗайма,
Врем8.ПроцентнаяСтавка
ПОМЕСТИТЬ Врем9
ИЗ
Врем8 КАК Врем8
ЛЕВОЕ СОЕДИНЕНИЕ Врем8 КАК Врем81
ПО Врем8.Счет = Врем81.Счет
И Врем8.Контрагент = Врем81.Контрагент
И Врем8.Договор = Врем81.Договор
И Врем8.Период < Врем81.Период
СГРУППИРОВАТЬ ПО
Врем8.Счет,
Врем8.Договор,
Врем8.Контрагент,
Врем8.Период,
Врем8.СуммаЗайма,
Врем8.ПроцентнаяСтавка
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Врем9.НачалоПериода КАК НачалоПериода,
Врем9.КонецПериода,
Врем9.Счет КАК Счет,
Врем9.Контрагент КАК Контрагент,
Врем9.Договор КАК Договор,
Врем9.СуммаЗайма,
Врем9.ПроцентнаяСтавка,
РАЗНОСТЬДАТ(Врем9.НачалоПериода, Врем9.КонецПериода, ДЕНЬ) + 1 КАК РазностьДат,
РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(Врем9.НачалоПериода, ГОД), КОНЕЦПЕРИОДА(Врем9.КонецПериода, ГОД), ДЕНЬ) + 1 КАК КоличествоДнейВГодах,
ВЫРАЗИТЬ(Врем9.СуммаЗайма * Врем9.ПроцентнаяСтавка / 100 * (РАЗНОСТЬДАТ(Врем9.НачалоПериода, Врем9.КонецПериода, ДЕНЬ) + 1) / (РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(Врем9.НачалоПериода, ГОД), КОНЕЦПЕРИОДА(Врем9.КонецПериода, ГОД), ДЕНЬ) + 1) * (ГОД(Врем9.КонецПериода) - ГОД(Врем9.НачалоПериода) + 1) КАК ЧИСЛО(15, 2)) КАК НачисленныйПроцент
ИЗ
Врем9 КАК Врем9
ГДЕ
Врем9.НачалоПериода <> Врем9.КонецПериода
И Врем9.СуммаЗайма > 0
1. В первом запросе мы производим выборку остатков по суммам займов на протяжении периода. При этом метод дополнения мы выбираем такой, чтобы получить остатки даже если не было движений по займам в течение периода.
2. Во втором запросе мы делаем выборку процентных ставок, выбирая сначала существующие ставки на начало периода, а затем объединяя это выборку с изменениями процентных ставок в пределах периода. Каждая выборка ограничивается теми договорами, которые были выбраны на первом этапе.
3. Изменение процентной ставки вызывает дробление периода расчета, т.е., например, если сумма займа постоянна на каком отрезке времени, то изменение процентной ставки внутри этого отрезка времени делит этот отрезок времени на две части, двукратное изменение внутри этого отрезка на три части и т.д.. Поэтому мы делаем выборку тех дат изменения процентных ставок, которые внутри периода отчета и находим ближайшие к ним значения сумм займов, счетов, контрагентов.
4. В четвертом запросе мы учитываем, что период отчета может быть лежать в разных годах, т.е., например, отрезок времени должен поделиться на такие отрезки: Дата1-КонецГода(Дата1), Начало следующего года-Дата2/Конец н-го года.
5. К четвертому запросу добавляем, где мы получаем начала следующих годов, добавляем суммы займов.
6. Объединяем все полученные данные.
7. Запросы 7 и 8 позволяют вычислить недостающие значения процентных ставок.
8. Запрос 9 формирует периоды для расчетов.
9. Последний запрос вычисляет суммы процентов. Оператор Выразить используется для округления.
Если интервал представляет собой кратное количество лет, то умножается на количество лет, если нет, то на 1.
Прилагается выгрузка с тестовыми примерами, отчет сделанный с помощью СКД "Отчет по начисленным процентам", призванные показать алгоритм.