-
Notifications
You must be signed in to change notification settings - Fork 2.2k
FINERACT-2102: Loan Product enableDownPayment overrided in the Loan a… #3952
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FINERACT-2102: Loan Product enableDownPayment overrided in the Loan a… #3952
Conversation
| loan.updateLoanRates(rateAssembler.fromParsedJson(command.parsedJson())); | ||
| } | ||
|
|
||
| if (command.hasParameter(LoanProductConstants.ENABLE_DOWN_PAYMENT)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please follow the same logic as it was done when loan was submitted:
- You can disable down payment on loan, when the it was allowed on the loan product, but not the otherwise! It should fail with validation when not allowed action was tried:
- user tries to enable down payment but it was not configured on loan product!
Ignoring the field silently that was sent is wrong!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! new validation method implemented in the LoanScheduleValidator
...org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java
Show resolved
Hide resolved
...va/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
Show resolved
Hide resolved
adamsaghy
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please kindly check my review!
65d1860 to
f87752c
Compare
| .isPrincipalCompoundingDisabledForOverdueLoans(); | ||
|
|
||
| final boolean isDownPaymentEnabled = loanProduct.getLoanProductRelatedDetail().isEnableDownPayment(); | ||
| final boolean isDownPaymentEnabled = validateDownPaymentAttribute(loanProduct.getLoanProductRelatedDetail().isEnableDownPayment(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move this into the loan validator!
| loanProductRelatedDetail.setEqualAmortization(newValue); | ||
| } | ||
|
|
||
| final boolean isDownPaymentEnabled = validateDownPaymentAttribute( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move this into the loan validator!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
| loan.loanProduct().getLoanProductRelatedDetail().isEnableDownPayment(), command.parsedJson()); | ||
| if (isDownPaymentEnabled != loanProductRelatedDetail.isEnableDownPayment()) { | ||
| changes.put(LoanProductConstants.ENABLE_DOWN_PAYMENT, isDownPaymentEnabled); | ||
| loanProductRelatedDetail.setEnableDownPayment(isDownPaymentEnabled); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its more straightforward to set the values as "false" instead of assigning the value of the isDownPaymentEnabled which cannot be anything else but false.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to consider a couple of scenarios:
- LP with DownPayment enabled and originally the LA with DownPayment enabled, then LA disable DownPayment, and
2.LP with DownPayment enabled and originally the LA with DownPayment disabled, then LA enable DownPayment
This is why we are validating If we have a change in the new value compared with the old value
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are checking the loan product configuration, but what you should check is only the configuration on loan. This method is covering the update loan application part, we already have something configured on the loan, thats the only thing that should be checked!
If on the loan product it was enabled down payment, but it was disabled when the loan was submitted and now when the loan is got updated, we want to enable again, you should not check what was set on the loan product, but check whether on the loan level what was set and now during the update if we wanna change it and enable again, then we can set from the loan product the down payment configurations on the loan level again.
| private boolean validateDownPaymentAttribute(final boolean isDownPaymentEnabledInLoanProduct, final JsonElement element) { | ||
| boolean isDownPaymentEnabled = isDownPaymentEnabledInLoanProduct; | ||
| if (!isDownPaymentEnabledInLoanProduct) { | ||
| if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.ENABLE_DOWN_PAYMENT, element)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is wrong. We should only throw exception if the the down payment is disabled on the loan product (which is correctly checked) and when user tries to set down payment to be enabled during loan submission (this is the wrongly checked part)! If user provide enableDownPayment=false then nothing should happen: No violation!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! Logic updated
| return Pair.of(loan, actualChanges); | ||
| } | ||
|
|
||
| private boolean validateDownPaymentAttribute(final boolean isDownPaymentEnabledInLoanProduct, final JsonElement element) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move the validations into the Validator class!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! moved to the validator
adamsaghy
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please kindly see my review!
f87752c to
5f5585e
Compare
| private BigDecimal totalCreditBalanceRefundReversed; | ||
| private BigDecimal totalRepaymentTransaction; | ||
| private BigDecimal totalRepaymentTransactionReversed; | ||
| private BigDecimal totalInterestRefund; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
totalInterestRefund is not yet calculated. Please remove till it got implemented fully.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! Removed
| private final Long chargeOffReasonId; | ||
| private final String chargeOffReason; | ||
|
|
||
| private BigDecimal totalUnpaidAccruedDueInterest; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
totalUnpaidAccruedDueInterest and totalUnpaidAccruedNotDueInterest are not set with value. Please compute their value or remove them till it got implemented fully.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! Removed
|
|
||
| private boolean isBackdatedCharge() { | ||
| return loanCharge.get().getDueDate().isBefore(loanCharge.get().getSubmittedOnDate()); | ||
| return (loanCharge.get().getDueDate() != null && loanCharge.get().getDueDate().isBefore(loanCharge.get().getSubmittedOnDate())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change is unrelated. If this is a fix for something, please raise it in a new PR!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! removed, It belongs to an issue when you have a Loan Installment Fee
| REAMORTIZE(30, "loanTransactionType.reAmortize"), // | ||
| INTEREST_PAYMENT_WAIVER(31, "loanTransactionType.interestPaymentWaiver"), // | ||
| ; | ||
| INTEREST_PAYMENT_WAIVER(31, "loanTransactionType.interestPaymentWaiver"); // |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unrelated change. Please remove it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! Removed
| @Schema(example = "0.000000") | ||
| public Double totalRepaymentTransaction; | ||
| @Schema(example = "0.000000") | ||
| public Double totalInterestRefund; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
totalInterestRefund is not set properly. Please remove till!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! Removed
adamsaghy
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like you mixed together different PRs! Please remove the unrelated changes!
| return CollectionData.template(); | ||
| } | ||
|
|
||
| BigDecimal delinquentPrincipal = BigDecimal.ZERO; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unrelated changes!
| .isPrincipalCompoundingDisabledForOverdueLoans(); | ||
|
|
||
| final boolean isDownPaymentEnabled = loanProduct.getLoanProductRelatedDetail().isEnableDownPayment(); | ||
| final boolean isDownPaymentEnabled = loanScheduleValidator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validations are called when loan got submitted or updated:
LoanApplicationWritePlatformServiceJpaRepositoryImpl.submitApplication
and
LoanApplicationWritePlatformServiceJpaRepositoryImpl.modifyApplication
Please move the validations into the loanApplicationValidator.validateForCreate and loanApplicationValidator.validateForModify
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! All the validations are in the LoanScheduleValidator class
| } | ||
|
|
||
| public boolean validateDownPaymentAttribute(final boolean isDownPaymentEnabledInLoanProduct, final JsonElement element) { | ||
| boolean isDownPaymentEnabled = isDownPaymentEnabledInLoanProduct; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is overcomplicated:
public void validateDownPaymentAttribute(final boolean isDownPaymentEnabledInLoanProduct, final JsonElement element) {
Boolean inputIsDownPaymentEnabled = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.ENABLE_DOWN_PAYMENT, element);
if(!isDownPaymentEnabledInLoanProduct && Boolean.TRUE.equals(inputIsDownPaymentEnabled)) {
throw new GeneralPlatformDomainRuleException("error.msg.downpayment.is.not.enabled.in.loan.product",
"The Loan can not override the downpayment flag because in the Loan Product the downpayment is disabled",
inputIsDownPaymentEnabled);
}
}
What do you think?
P.S.: You can consider passing the whole loan product as parameter and the validation method will decide what it is using from. Similarly the whole command passed as 2nd parameter
| loanProductRelatedDetail.setEqualAmortization(newValue); | ||
| } | ||
|
|
||
| final boolean isDownPaymentEnabled = loanScheduleValidator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We only need the incoming isDownPaymentEnabled and if it is different than the one on the loan actually set then either set the LP config or reset it (based on we enable or disable it).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! In the LoanScheduleAssembler is only the input read If exists, else we set the LP value
5f5585e to
28ff499
Compare
| collectionData.setDelinquentDate(overdueSinceDate); | ||
| } | ||
| collectionData.setDelinquentAmount(outstandingAmount); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated changes, please remove!
| BigDecimal totalCreditBalanceRefundReversed = BigDecimal.ZERO; | ||
| BigDecimal totalRepaymentTransaction = BigDecimal.ZERO; | ||
| BigDecimal totalRepaymentTransactionReversed = BigDecimal.ZERO; | ||
| BigDecimal totalInterestRefund = BigDecimal.ZERO; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated changes, please remove!
| loanTransactions); | ||
| totalRepaymentTransaction = computeTotalRepaymentTransactionAmount(loanTransactions); | ||
| totalRepaymentTransactionReversed = computeTotalAmountForReversedTransactions(LoanTransactionType.REPAYMENT, loanTransactions); | ||
| totalInterestPaymentWaiver = computeTotalAmountForNonReversedTransactions(LoanTransactionType.INTEREST_PAYMENT_WAIVER, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated changes, please remove!
| .totalChargeAdjustment(totalChargeAdjustment).totalChargeAdjustmentReversed(totalChargeAdjustmentReversed) | ||
| .totalChargeback(totalChargeback).totalCreditBalanceRefund(totalCreditBalanceRefund) | ||
| .totalCreditBalanceRefundReversed(totalCreditBalanceRefundReversed).totalRepaymentTransaction(totalRepaymentTransaction) | ||
| .totalRepaymentTransactionReversed(totalRepaymentTransactionReversed).build(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated changes, please remove!
|
|
||
| private boolean isBackdatedCharge() { | ||
| return loanCharge.get().getDueDate().isBefore(loanCharge.get().getSubmittedOnDate()); | ||
| return (loanCharge.get().getDueDate().isBefore(loanCharge.get().getSubmittedOnDate())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated changes, please remove!
| public LocalDate delinquentDate; | ||
| @Schema(example = "100.000000") | ||
| public Double delinquentAmount; | ||
| @Schema(example = "80.000000") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated changes, please remove!
| if (isDownPaymentEnabled != loanProductRelatedDetail.isEnableDownPayment()) { | ||
| changes.put(LoanProductConstants.ENABLE_DOWN_PAYMENT, isDownPaymentEnabled); | ||
| loanProductRelatedDetail.setEnableDownPayment(isDownPaymentEnabled); | ||
| loanProductRelatedDetail.setEnableAutoRepaymentForDownPayment(isDownPaymentEnabled); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just because down payment was enabled, it does not mean auto repayment for down payment should be enabled as well. It should be set based on what was configured on the loan product
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! We are just changing the enableDownPayment attribute
28ff499 to
86964b7
Compare
86964b7 to
1077fdd
Compare
adamsaghy
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
…pplication
Description
There is need to decide dynamically the loan is downpayment loan or not under the same product constraints. The change here to allow override previously configured
enableDownPaymentfieldFINERACT-2102
Checklist
Please make sure these boxes are checked before submitting your pull request - thanks!
Write the commit message as per https://github.com/apache/fineract/#pull-requests
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 coding conventions at https://cwiki.apache.org/confluence/display/FINERACT/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
Submission is not a "code dump". (Large changes can be made "in repository" via a branch. Ask on the developer mailing list for guidance, if required.)
FYI our guidelines for code reviews are at https://cwiki.apache.org/confluence/display/FINERACT/Code+Review+Guide.