# Late Fee Start Date and Daily Recalculation Evidence (2026-06-30) ## Scope - Backend feature: late-fee start-date calculation for four configured modes, billing-time persistence, and daily recalculation for unpaid charges. - Backend branch: `feat/late-fee-recalc`, merged locally into `develop` - Backend merge commit: `4f1a99b7e Merge branch 'feat/late-fee-recalc' into develop` - Docs branch: `feat/rev004-dict-alignment-docs` ## Backend Commits - `8c2714af2 feat: add late fee begin date calculator` - `1724f0c3c feat: resolve late fee parameter config` - `282c327e7 feat: add late fee daily calculator` - `320e50d73 feat: add late fee recalculation mapper helpers` - `fd5154f83 feat: save late fee begin date during billing` - `f7207fe61 feat: add daily late fee recalculation job` - `af05601f1 fix: harden late fee recalculation batch` - `3722c54fa fix: paginate late fee recalculation batches` - `d551dba84 fix: handle late fee reversal and batch transactions` - `bcc635e81 fix: guard late fee recalculation updates` ## Implemented Files - `sw-business/sw-business-server/src/main/java/cn/com/emsoft/sw/business/service/charge/latefee/LateFeeSettingCodes.java` - `sw-business/sw-business-server/src/main/java/cn/com/emsoft/sw/business/service/charge/latefee/LateFeeConfig.java` - `sw-business/sw-business-server/src/main/java/cn/com/emsoft/sw/business/service/charge/latefee/LateFeeConfigResolver.java` - `sw-business/sw-business-server/src/main/java/cn/com/emsoft/sw/business/service/charge/latefee/LateFeeBeginDateCalculator.java` - `sw-business/sw-business-server/src/main/java/cn/com/emsoft/sw/business/service/charge/latefee/LateFeeBeginDateApplyService.java` - `sw-business/sw-business-server/src/main/java/cn/com/emsoft/sw/business/service/charge/latefee/LateFeeDailyCalculator.java` - `sw-business/sw-business-server/src/main/java/cn/com/emsoft/sw/business/service/charge/latefee/LateFeeRecalculationBatchService.java` - `sw-business/sw-business-server/src/main/java/cn/com/emsoft/sw/business/service/charge/latefee/LateFeeRecalculationService.java` - `sw-business/sw-business-server/src/main/java/cn/com/emsoft/sw/business/job/charge/LateFeeRecalculationJob.java` - `sw-business/sw-business-server/src/main/java/cn/com/emsoft/sw/business/config/ClockConfig.java` - `sw-business/sw-business-server/src/main/java/cn/com/emsoft/sw/business/dal/mysql/charge/ChargeMapper.java` - `sw-business/sw-business-server/src/main/java/cn/com/emsoft/sw/business/dal/mysql/paymentrecorddetail/PaymentRecordDetailMapper.java` - `sw-business/sw-business-server/src/main/java/cn/com/emsoft/sw/business/dal/mysql/latefeereducedetail/LateFeeReduceDetailMapper.java` - `sw-business/sw-business-server/src/main/java/cn/com/emsoft/sw/business/service/charge/ChargeServiceImpl.java` - `script/xxl-job-init.sql` - `sql/rev004/REV004_late_fee_daily_recalculation_index.sql` - Unit tests under `sw-business/sw-business-server/src/test/java/cn/com/emsoft/sw/business/service/charge/latefee/` - `sw-business/sw-business-server/src/test/java/cn/com/emsoft/sw/business/service/charge/ChargeLateFeeBeginDateWiringTest.java` - `sw-business/sw-business-server/src/test/java/cn/com/emsoft/sw/business/service/accountingadjust/pricediff/PriceDiffWriteBackServiceTest.java` ## Review Fixes - Paid late-fee deduction excludes original `APPLIED` late-fee details that have a `REVERSED` detail referencing them through `relatedDetailId`. - The daily recalculation batch now selects unpaid charges with non-null `lateFeeBeginDate`, avoiding repeated starvation behind historical rows that cannot be recalculated. - `script/xxl-job-init.sql` registers handler `lateFeeDailyRecalculation` for business-server with cron `0 30 3 * * ?`. - `LateFeeRecalculationServiceTest` includes a defensive scenario where a reversed late-fee detail is returned by the mapper and ignored by aggregation. - The daily recalculation job now uses keyset pagination (`id > lastId`) until no eligible rows remain, so rows after the first page are recalculated in the same run instead of waiting forever behind unchanged earlier rows. - `LateFeeBeginDateApplyService` centralizes `lateFeeBeginDate` persistence and is reused by normal billing, manual charge creation, counter add-reading formalization, and special billing formalization before `chargeMapper.insert`. - Focused tests now cover the shared begin-date apply service, keyset pagination across batches, and direct insert path wiring for counter add-reading and special billing. - Price-diff write-back now applies missing `lateFeeBeginDate` before inserting the replacement unpaid charge. - Daily recalculation now keeps the outer pagination loop non-transactional and executes each page through `LateFeeRecalculationBatchService.recalculateBatch(...)` with its own transaction boundary. - Daily recalculation now updates `lateFee` through a conditional mapper update that still requires `payState=UNPAID` and non-null `lateFeeBeginDate`, so a charge paid after selection is not overwritten or counted. - `LateFeeDailyCalculator` ignores non-positive penalty coefficients defensively. - `REV004_late_fee_daily_recalculation_index.sql` adds `idx_biz_charge_late_fee_recalc` on `(tenant_id, pay_state, id)` for the tenant-scoped unpaid keyset scan. ## Verification ### Focused Late-Fee Tests Command: ```bash mvn -pl sw-business/sw-business-server -am -Dtest=LateFeeBeginDateCalculatorTest,LateFeeBeginDateApplyServiceTest,LateFeeConfigResolverTest,LateFeeDailyCalculatorTest,LateFeeRecalculationServiceTest,ChargeLateFeeBeginDateWiringTest,CounterAddReadingApplicationServiceImplTest,SpecialBillingServiceImplTest,PriceDiffWriteBackServiceTest -Dsurefire.failIfNoSpecifiedTests=false test ``` Result: PASS - `LateFeeBeginDateApplyServiceTest`: 2 tests, 0 failures, 0 errors, 0 skipped - `LateFeeRecalculationServiceTest`: 8 tests, 0 failures, 0 errors, 0 skipped - `LateFeeConfigResolverTest`: 3 tests, 0 failures, 0 errors, 0 skipped - `LateFeeBeginDateCalculatorTest`: 7 tests, 0 failures, 0 errors, 0 skipped - `LateFeeDailyCalculatorTest`: 9 tests, 0 failures, 0 errors, 0 skipped - `ChargeLateFeeBeginDateWiringTest`: 5 tests, 0 failures, 0 errors, 0 skipped - `PriceDiffWriteBackServiceTest`: 1 test, 0 failures, 0 errors, 0 skipped - `CounterAddReadingApplicationServiceImplTest`: 24 tests, 0 failures, 0 errors, 0 skipped - `SpecialBillingServiceImplTest`: 4 tests, 0 failures, 0 errors, 0 skipped - Total: 63 tests, 0 failures, 0 errors, 0 skipped ### Existing Accounting Regression Tests Command: ```bash mvn -pl sw-business/sw-business-server -am -Dtest=LateFeeDateModeCalculatorTest,PaymentRecordServiceImplTest,AccountingAdjustActionServiceImplTest,ChargeServiceBillingValidationTest -Dsurefire.failIfNoSpecifiedTests=false test ``` Result: PASS - `ChargeServiceBillingValidationTest`: 8 tests, 0 failures, 0 errors, 0 skipped - `LateFeeDateModeCalculatorTest`: 4 tests, 0 failures, 0 errors, 0 skipped - `AccountingAdjustActionServiceImplTest`: 19 tests, 0 failures, 0 errors, 0 skipped - `PaymentRecordServiceImplTest`: 18 tests, 0 failures, 0 errors, 0 skipped - Total: 49 tests, 0 failures, 0 errors, 0 skipped ### Compile Command: ```bash mvn -pl sw-business/sw-business-server -am -DskipTests compile ``` Result: PASS ## Known Warnings - Maven reports existing effective-model warnings for duplicate or missing plugin declarations in unrelated modules. - Mockito reports the existing dynamic-agent warning during unit tests. - These warnings did not fail the executed verification commands.