FINERACT-2593: when loan product is null#5813
FINERACT-2593: when loan product is null#5813mansi75 wants to merge 5 commits intoapache:developfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes FINERACT-2593 by preventing NullPointerException in delinquency collection calculations when loan.getLoanProduct() returns null, and updates the unit test setup to improve Mockito injection for added dependencies.
Changes:
- Fix inverted null guard in the pre-approval/pending/cancelled branch.
- Add a null guard before computing
availableDisbursementAmountWithOverAppliedon the active-loan branch. - Add missing
@Mockfields inDelinquencyReadPlatformServiceImplTestto support constructor injection.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java | Corrects null-check logic and prevents NPE when loan product is missing. |
| fineract-provider/src/test/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImplTest.java | Adds mocks for newly required constructor dependencies (but still needs one more mock for reliable injection) and has minor formatting issues. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Good work @mansi75 ! The core fixes on lines 146 and 163 are correct and address the two most visible crash surfaces. Two things worth addressing before merge: 1. Helper still has no null guard (line 202)
Fix: 2. Tests assert null but don't cover the helper directly Asserting |
|
Hi @mansi75, you may want to update the PR title to use uppercase FINERACT-. I believe Jira only references the PR correctly when the issue key is uppercase. I ran into the same issue recently, where the PR was not linked to the Jira ticket because the key was not uppercase. |
|
@mansi75 Please review failing e2e test cases Then Loan has availableDisbursementAmountWithOverApplied field with value: 150 # org.apache.fineract.test.stepdef.loan.LoanStepDef.checkLoanDetailsAvailableDisbursementAmountWithOverAppliedField(double) Test failed for src/test/resources/features/LoanUpdateApprovedAmount.feature Execute tests for shard 9 |
|
@San-43 Thank you for informing. I will update accordingly. |
|
@mansi75 Please review the failing test cases! |
|
@mansi75 Please run: Before any PR or changes, please always run these two commands and make sure there is green build! |
|
@adamsaghy Got it! I am working on the failing tests |
…tection in DelinquencyReadPlatformServiceImpl
…tection in DelinquencyReadPlatformServiceImpl
…Helper mock, fix import formatting, add null-product regression tests, extend active-loan guard
…for calculateAvailableDisbursementAmountWithOverApplied
…when LoanProduct non-null
5655f24 to
0c080bf
Compare
| collectionData.setAvailableDisbursementAmount(calculateAvailableDisbursementAmount(loan)); | ||
| collectionData.setAvailableDisbursementAmountWithOverApplied(calculateAvailableDisbursementAmountWithOverApplied(loan)); | ||
| if (loan.getLoanProduct() != null) { | ||
| collectionData.setAvailableDisbursementAmountWithOverApplied(calculateAvailableDisbursementAmountWithOverApplied(loan)); |
There was a problem hiding this comment.
Can you please explain when loanProduct is required in calculateAvailableDisbursementAmountWithOverApplied function ? When loanProduct is not present what part of loan product it utilize when calculating AvailableDisburementAmountWithOverApplied
There was a problem hiding this comment.
Based on Adam's review I have one more concern. If loan product is non nullable and giving NPE shouldn't we use fail fast approach in this? As you have applied the npe handling in calculation wouldn't it cause logical issues? Even through all tests are passing.
Aman-Mittal
left a comment
There was a problem hiding this comment.
Seems ok to merge what i can see it Logic was fixed where || is causing problem but curious about AvailableDisbursementAmountWithOverapplied Logic
|
@mansi75 I dont see a use case where loan product is null. Can you please give me an example? |
|
@adamsaghy Correct me if I got it wrong. In the codebase, getLoanProduct() should never return null because loanProduct is a mandatory @manytoone relationship on the Loan entity but if a Loan object is loaded in one Hibernate session and then getLoanProduct() is called outside that session, hibernate will return null as it can't initialize the proxy |
Technically maybe but:
How did you come to the conclusion this is an issue? Have you noticed NPE somewhere? |
|
@adamsaghy Thank you for the correction. After checking the actual Loan.java source: loanProduct IS lazily loaded but it cannot be null in practice since nullable = false and DelinquencyReadPlatformServiceImpl is annotated @transactional, so it is open for entire method call. So I agree there is no real-world scenario where getLoanProduct() returns null and I have not noticed NPE anywhere. I will revert the unnecessary null guards and keep only the single operator fix. |
|
LGTM |
Aman-Mittal
left a comment
There was a problem hiding this comment.
Please address my concern.
Description
JIRA: https://issues.apache.org/jira/browse/FINERACT-2593
Problem
DelinquencyReadPlatformServiceImpl.calculateLoanCollectionDatacontained two bugs that caused an unhandledNullPointerException(HTTP 500) whenloan.getLoanProduct()returnsnull:Bug 1 — Inverted null guard (pre-approval / pending state branch)
The condition
loan.getLoanProduct() == null ||used the wrong operator. WhengetLoanProduct()returnsnull, the left-hand operand evaluates totrue, short-circuiting the||and routing execution directly intocalculateAvailableDisbursementAmountWithOverApplied(loan). The helper immediately dereferencesloanProductwith no null check, producing an NPE. The intended operator is!= null &&.Bug 2 — No null guard on active-loan branch
The active (open) loan branch called
calculateAvailableDisbursementAmountWithOverApplied(loan)unconditionally with no null check ongetLoanProduct()at all, giving a second independent crash surface from the same root cause.Fix
== null ||to!= null &&in the pre-approval branch so the helper is only invoked when aLoanProductis present and over-apply is enabled.loan.getLoanProduct() != nullguard.@Mockfield declarations inDelinquencyReadPlatformServiceImplTest(ConfigurationDomainService,LoanTransactionRepository,PossibleNextRepaymentCalculationServiceDiscovery,LoanDelinquencyActionRepository) that were causing silent Mockito injection failure on any new tests written for this path.Files Changed
DelinquencyReadPlatformServiceImpl.javaDelinquencyReadPlatformServiceImplTest.java@Mockdeclarations + new regression testsChecklist
Please make sure these boxes are checked before submitting your pull request - thanks!