FINERACT-2455: WC breach/delinquency reschedule, re-date current period on frequency change + endDate validation - #6205
Conversation
476d15b to
9233fed
Compare
08ffad5 to
b3b45bc
Compare
|
AC3 is not met — 8 existing TestRail scenarios had their inputs changed to dodge the new validation, no? Breach C85273, C85274, C85275, C85280, C85588: business date 01 June 2026 → 15 May 2026 Each of those originally exercised "reschedule deep inside a long period", which is now precisely the AC2 error case. Rather than moving the date, keep the original inputs and flip the assertion to the new validation error, then add a separate happy-path scenario at the earlier date. That both satisfies AC3 honestly and gives AC2 coverage across several product configs instead of one. Scenarios where only the expected tables changed (e.g. C74496, C74497, C85488, C85501) are legitimate consequences of AC1. The pause-drop isn't stable. It's action.getFrequency() != null "a frequency was supplied", not "the frequency changed". Re-sending the same frequency still re-dates the period and discards any pause extension, which the new "ignoring an overlapping pause" scenario locks in. It isn't durable: a later PAUSE/RESUME calls recalculatePeriodsForPauses, which rebuilds every period from period 1 using the effective frequency and re-applies all recorded pauses: resurrecting the extension that was just dropped. Same loan, different boundaries depending on action order. The validator and the service compute the same date two different ways. The validator uses action.getFrequency()/getFrequencyType(); the service uses params.frequency() from resolveEffectiveRescheduleParams. They agree only because the action is saved before the service runs and is the latest with a frequency group. Both also do their own findCurrentOpenPeriod lookup. Extract one helper both call, so the check can't drift from the mutation it's guarding. findCurrentOpenPeriod returning Optional is stricter than the loop it replaces. The old code tolerated multiple matches (last one won); the derived query throws IncorrectResultSizeDataAccessException if two open periods ever cover the business date. findTop…OrderByPeriodNumberDesc would preserve the old tolerance. The delinquency queries add AND s.reset = false, which the replaced loops did not have. A behaviour change bundled into a refactor. Unreachable today (resetPeriods only touches periods with toDate < resetDate), but if a reset period ever landed in the future window, updateFuturePeriods renumbers sequentially and would skip it → duplicate period numbers. Also note the breach queries have no equivalent filter, so the two schedules now diverge. Three different "period expired" predicates coexist in WorkingCapitalLoanBreachScheduleServiceImpl: evaluateBreachOnDate uses businessDate >= toDate, evaluateExpiredPeriods uses toDate < businessDate, recomputeBreach uses businessDate > toDate. Pre-existing, but this PR makes the boundary reachable by design, since the validation deliberately permits a re-dated period ending exactly on the business date. The "equals the current business date" scenario closes the loan immediately and never runs another COB, so what that period does at the next evaluation is unasserted. Add a COB step and pin it. Minor The breach validator now mixes two error mechanisms (failWithCodeNoParameterAddedToErrorCode and the new failGeneralValidation) and hardcodes "workingCapitalLoanBreachAction" in two places. The delinquency validator has a VALIDATION_RESOURCE constant — mirror it. No unit tests. calculateToDate and both validateFrequencyDoesNotEndBeforeBusinessDate methods are cheap to cover directly, especially the == businessDate / -1 day boundary that currently only has slow e2e coverage. |
bfc6254 to
f7125f3
Compare
664f183 to
442b55f
Compare
…od on frequency change + endDate validation
442b55f to
0be5f79
Compare
Not changed. Those scenarios were written to cover reschedule inheritance and repeated reschedules, not the end date rule. Moving the business date keeps each of them testing what it was written for, and the new rule has its own dedicated scenarios on both the breach and the delinquency side.
Fixed.
Fixed.
Fixed.
Fixed.
The boundary part is fixed: the scenario now runs COB on the day after the recalculated end date and pins the resulting schedule, so the behaviour at that boundary is locked in. On the predicates, there are two rules, not three. evaluateExpiredPeriods and recomputeBreach are the same exclusive condition written two ways. The repository query and evaluateBreachOnDate are the same inclusive condition, and the query is just a prefilter for that check. The exclusive rule belongs to the reprocess path and the inclusive one to the COB evaluation path, which runs as of the previous business date. Unifying them would change breach timing logic.
Fixed.
Not added as unit tests, extended current e2e test scenarios. |
…od on frequency change + endDate validation - E2E tests
Description
Describe the changes made and why they were made. (Ignore if these details are present on the associated Apache Fineract JIRA ticket.)
Checklist
Please make sure these boxes are checked before submitting your pull request - thanks!
Your assigned reviewer(s) will follow our guidelines for code reviews.