Skip to content

FINERACT-2455: WC - delinquency bucket for regular loan shouldn't be available on WC loan product creation - #6477

Open
mariiaKraievska wants to merge 2 commits into
apache:developfrom
openMF:FINERACT-2455/wc-delinquency-bucket-filter-and-validation-on-wc-loan-product-creation
Open

mariiaKraievska wants to merge 2 commits into
apache:developfrom
openMF:FINERACT-2455/wc-delinquency-bucket-filter-and-validation-on-wc-loan-product-creation

Conversation

@mariiaKraievska

Copy link
Copy Markdown
Contributor

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!

  • Write the commit message as per our guidelines
  • Acknowledge that we will not review PRs that are not passing the build ("green") - it is your responsibility to get a proposed PR to pass the build, not primarily the project's maintainers.
  • Create/update unit or integration tests for verifying the changes made.
  • Follow our coding conventions.
  • Add required Swagger annotation and update API documentation at fineract-provider/src/main/resources/static/legacy-docs/apiLive.htm with details of any API changes
  • This PR must not be a "code dump". Large changes can be made in a branch, with assistance. Ask for help on the developer mailing list.
  • If merging this PR resolves a JIRA issue, I will mark that issue as resolved and set "Fix Version/s" appropriately.
  • I followed the AI Policy.

Your assigned reviewer(s) will follow our guidelines for code reviews.

@mariiaKraievska
mariiaKraievska force-pushed the FINERACT-2455/wc-delinquency-bucket-filter-and-validation-on-wc-loan-product-creation branch from f1e0144 to 7e3aadb Compare September 18, 2026 11:21
@mariiaKraievska
mariiaKraievska force-pushed the FINERACT-2455/wc-delinquency-bucket-filter-and-validation-on-wc-loan-product-creation branch from 7e3aadb to 7b9f754 Compare September 21, 2026 12:37
@mariiaKraievska
mariiaKraievska marked this pull request as ready for review September 22, 2026 09:46
@adamsaghy

Copy link
Copy Markdown
Contributor

@mariiaKraievska Please review my concerns / findings:

  1. updateFrom resolves the bucket before the change check

WorkingCapitalLoanAssemblerImpl.java:420-429

if (fromApiJsonHelper.parameterExists(delinquencyBucketIdParamName, element)) {
final Long bucketId = ...;
final DelinquencyBucket bucket = resolver.findWorkingCapitalBucketByIdIfProvided(bucketId); // always runs
final Long existingBucketId = ...;
if (!Objects.equals(bucketId, existingBucketId)) { ... }
}

The lookup + type validation runs unconditionally, outside the !Objects.equals(...) guard. The three sibling blocks around it (breachId, nearBreachId, delinquencyGraceDays) all gate the lookup on the change check. Consequence: a WC loan created before this PR with a REGULAR bucket (the old code was findById(bucketId).orElse(null) with no type check, so this was reachable) can no longer be modified at all if the client resends the unchanged delinquencyBucketId — it 400s on a no-op. Same shape for a since-deleted id, which used to be silently ignored and now throws DelinquencyBucketNotFoundException.

Moving the resolve inside the if fixes both and matches the surrounding style. The WC product update path already does this correctly via isChangeInLongParameterNamed (WorkingCapitalLoanProductWritePlatformServiceImpl.java:276), as does the LP update path.

  1. Error codes and unused message args

Both new errors pass the bucket id as a defaultUserMessageArgs value, but neither message has a {0} placeholder, so it's never rendered. The codes also drop the entity segment that the rest of the codebase uses (validation.msg.delinquencyBucketId.must.be.regular.type vs. the conventional validation.msg.loanproduct.delinquencyBucketId.…). The two messages are otherwise near-identical — one code parameterized by expected type would do.

  1. retrieveWorkingCapitalDelinquencyBucketOptions() returns null when empty

WorkingCapitalDelinquencyBucketResolver.java:46-50. This preserves the old serialization behaviour, but it pushes null-returning into a shared service method that now has two callers (WC product template and, transitively, the WC loan template). Returning List.of() and keeping the null-ing at the response-building boundary would be safer.

  1. Duplication

retrieveAllDelinquencyBuckets() and retrieveDelinquencyBucketsByType() are identical apart from the finder (DelinquencyReadPlatformServiceImpl.java:105-119). Also validateIsWorkingCapitalType is public but only called from within its own class.

  1. Test-side nits

DelinquencyBucketResolver.resolveBucketId calls resolve(...) on this, so Spring's @Cacheable proxy is bypassed — every resolution re-fetches all buckets. Not a correctness issue.
WorkingCapitalLoanAccount.feature still hardcodes delinquencyBucketId | 1 in three existing scenarios. Harmless today (buildCreateLoanRequest ignores column 7, and the negative scenario fails in the validator before the assembler), but the seeded bucket ids are genuinely nondeterministic — DelinquencyGlobalInitializerStep.initialize() creates BASIC_DELINQUENCY_BUCKET and WC_DELINQUENCY_BUCKET via ParallelExecutionHelper.runInParallel. Now that resolveBucketId accepts names, those literals are worth converting before someone wires column 7 up.
LoanProduct.feature C106741 creates a loan product and leaves it behind; the WC counterpart C106744 deletes its product.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants