Appearance
Архитектура подсистемы ставок и расчёта пеней (ЖКХ, правила РФ)
Обзор
Подсистема обеспечивает:
- Справочник ставок — хранение исторических ключевых ставок ЦБ РФ и применяемых ставок для ЖКХ (с учётом ПП №329 и др.).
- Автозагрузка ставки ЦБ — раз в сутки запрос к официальному SOAP-сервису ЦБ РФ KeyRateXML; при изменении ставки закрывается предыдущий период и создаётся новая запись.
- Расчёт пеней — чистая функция по правилам ПП 354: пени только на основной долг, по дням просрочки с учётом действующей ставки на каждый день.
1. Хранение ставок (справочник)
Модель PenaltyRate (таблица penalty_rates)
| Поле | Тип | Описание |
|---|---|---|
| id | bigserial | PK |
| valid_from | date | Дата начала действия ставки |
| valid_to | date (nullable) | Дата окончания (NULL = действует по сей день) |
| base_cbr_rate | numeric(8,4) | Фактическая ставка ЦБ РФ |
| effective_rate | numeric(8,4) | Применяемая ставка для ЖКХ (по умолчанию = base_cbr_rate; может быть заморожена вручную) |
| description | varchar(500) | Комментарий (например «ПП №329», «Автоимпорт») |
- Непересекающиеся периоды: у одной записи
valid_toлибо NULL, либо дата окончания. - При автоимпорте создаётся запись с
effective_rate = base_cbr_rate; ручное переопределение (заморозка) делается через CRUD.
REST API (CRUD)
GET /api/billing/penalty-rates— список всех записей (по убыванию valid_from).GET /api/billing/penalty-rates/:id— одна запись.POST /api/billing/penalty-rates— создание (тело: validFrom, validTo?, baseCbrRate, effectiveRate, description).PUT /api/billing/penalty-rates/:id— обновление.DELETE /api/billing/penalty-rates/:id— удаление.
2. Автозагрузка ставки ЦБ (cron)
- Источник: официальный SOAP-сервис ЦБ РФ KeyRateXML. Сторонние прокси не используются.
- Расписание: ежедневно в 04:00 (cron
0 4 * * *), плюс один запуск при старте сервиса. - Логика:
- POST SOAP 1.1 к ЦБ: операция KeyRateXML, параметры
fromDateиToDate(последние 31 день). - Разбор XML-ответа (KeyRateXMLResult): извлечение записей KeyRateOnDate или Table с полями Dt и KeyRate.
- Выбор последней по дате ставки.
- Поиск в БД записи с
valid_to IS NULL; при изменении ставки — закрытие периода и создание новой записи сdescription = "Автоимпорт с сайта ЦБ РФ (KeyRateXML)".
- POST SOAP 1.1 к ЦБ: операция KeyRateXML, параметры
Ручные записи (заморозка ставки) не перезаписываются.
Как проверить работу
После старта billing-service синхронизация запускается один раз. В логах — «добавлена ставка ЦБ» или «ставка не изменилась». Справочник: GET /api/billing/penalty-rates. Никакой дополнительный URL или прокси настраивать не нужно.
3. Алгоритм расчёта пеней
Правила (ПП 354 и др.)
- Дни 1–30 просрочки: пени не начисляются (0).
- Дни 31–90: за каждый день пени = основной долг × (effective_rate / 100) × (1/300).
- День 91 и далее: за каждый день пени = основной долг × (effective_rate / 100) × (1/130).
- Пени считаются только на основной долг (principal_debt); пени на пени не начисляются.
- На каждый день просрочки применяется та ставка, которая действовала в этот день (по справочнику
PenaltyRate).
Чистая функция
go
// internal/penalty/calc.go
func CalculatePenaltyForDebt(
debtAmount decimal.Decimal,
dueDate, targetDate time.Time,
rateHistory []RateEntry,
) decimal.DecimaldueDate— дата, с которой долг считается просроченным.targetDate— дата, на которую считаем пени (включительно).rateHistory— срез записей с полямиValidFrom,ValidTo,EffectiveRate(в процентах).
Внутри: цикл по каждому дню просрочки (от dueDate+1 до targetDate), для каждого дня определение ставки через EffectiveRateForDate(day, rateHistory) и прибавление к итогу по формулам 1/300 или 1/130.
Использование в сервисе: загрузить из БД penalty_rates за нужный диапазон дат, преобразовать в []penalty.RateEntry (ValidFrom, ValidTo, EffectiveRate в decimal), вызвать CalculatePenaltyForDebt.
Тесты
В internal/penalty/calc_test.go покрыты сценарии:
- отсутствие просрочки → 0;
- первые 30 дней → 0;
- дни 31–90 (1/300);
- дни 91+ (1/130);
- смена ставки по дням (разные effective_rate в разные периоды);
- нулевой долг;
EffectiveRateForDateдля границ периодов.
4. Файлы и зависимости
| Компонент | Путь |
|---|---|
| Миграция | services/billing-service/migrations/005_penalty_rates.sql |
| Модель GORM | internal/models/penalty_rate.go |
| CRUD handlers | internal/handlers/penalty_rates.go |
| Клиент ЦБ (SOAP KeyRateXML) | internal/cbr/keyrate_client.go |
| Cron-задача синхронизации | internal/job/penalty_rate_sync.go |
| Расчёт пеней (pure) | internal/penalty/calc.go |
| Тесты расчёта | internal/penalty/calc_test.go |
Зависимости: github.com/shopspring/decimal, github.com/robfig/cron/v3.