Cocoa-Puffs commented on code in PR #6385:
URL: https://github.com/apache/fineract/pull/6385#discussion_r4016226000
##########
fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanTransactionReadPlatformServiceImpl.java:
##########
@@ -78,52 +95,68 @@ public WorkingCapitalLoanCommandTemplateData
retrieveLoanTransactionTemplate(fin
.build();
} else if
(WorkingCapitalLoanConstants.REPAYMENT_LOAN_COMMAND.equals(command)
||
WorkingCapitalLoanConstants.GOODWILL_CREDIT_LOAN_COMMAND.equals(command)) {
- return WorkingCapitalLoanCommandTemplateData.builder()
- .expectedAmount(wcLoan.getBalance() != null ?
wcLoan.getBalance().getPrincipalOutstanding() : null)
- .currency(wcLoan.getLoanProduct().getCurrency().toData())
+ return template.expectedAmount(balance != null ?
balance.getPrincipalOutstanding() : null)
.paymentTypeOptions(paymentTypeReadPlatformService.retrieveAllPaymentTypes())
.classificationOptions(codeValueReadPlatformService
.retrieveCodeValuesByCode(WorkingCapitalLoanConstants.REPAYMENT_CLASSIFICATION_CODE_NAME))
.build();
} else if
(WorkingCapitalLoanConstants.CREDIT_BALANCE_REFUND_COMMAND.equals(command)) {
- final BigDecimal overpaymentAmount = wcLoan.getBalance() != null ?
wcLoan.getBalance().getOverpaymentAmount() : null;
- return WorkingCapitalLoanCommandTemplateData.builder()
- .expectedAmount(overpaymentAmount != null ?
overpaymentAmount : BigDecimal.ZERO)
- .currency(wcLoan.getLoanProduct().getCurrency().toData())
+ final BigDecimal overpaymentAmount = balance != null ?
balance.getOverpaymentAmount() : null;
+ return template.expectedAmount(overpaymentAmount != null ?
overpaymentAmount : BigDecimal.ZERO)
.paymentTypeOptions(paymentTypeReadPlatformService.retrieveAllPaymentTypes())
.classificationOptions(codeValueReadPlatformService
.retrieveCodeValuesByCode(WorkingCapitalLoanConstants.CREDIT_BALANCE_REFUND_CLASSIFICATION_CODE_NAME))
.build();
- } else if
(WorkingCapitalLoanConstants.DISCOUNT_FEE_LOAN_COMMAND.equals(command)) {
- return
WorkingCapitalLoanCommandTemplateData.builder().currency(wcLoan.getLoanProduct().getCurrency().toData())
- .classificationOptions(codeValueReadPlatformService
-
.retrieveCodeValuesByCode(WorkingCapitalLoanConstants.DISCOUNT_FEE_CLASSIFICATION_CODE_NAME))
- .build();
- } else if
(WorkingCapitalLoanConstants.DISCOUNT_FEE_ADJUSTMENT_LOAN_COMMAND.equals(command))
{
- return
WorkingCapitalLoanCommandTemplateData.builder().currency(wcLoan.getLoanProduct().getCurrency().toData())
- .classificationOptions(codeValueReadPlatformService
-
.retrieveCodeValuesByCode(WorkingCapitalLoanConstants.DISCOUNT_FEE_CLASSIFICATION_CODE_NAME))
- .build();
- } else if
(WorkingCapitalLoanConstants.CHARGE_OFF_LOAN_COMMAND.equals(command)) {
- // Charge-off amount is the auto-calculated outstanding balance;
the date defaults to the business date.
- return WorkingCapitalLoanCommandTemplateData.builder()
- .chargeOffAmount(wcLoan.getBalance() != null ?
wcLoan.getBalance().getTotalOutstanding() : BigDecimal.ZERO)
-
.chargeOffDate(DateUtils.getBusinessLocalDate()).currency(wcLoan.getLoanProduct().getCurrency().toData())
- .chargeOffReasonOptions(
-
codeValueReadPlatformService.retrieveCodeValuesByCode(WorkingCapitalLoanConstants.CHARGE_OFF_REASONS))
- .build();
} else if
(WorkingCapitalLoanConstants.RECOVERY_PAYMENT_LOAN_COMMAND.equals(command)) {
// The amount to pre-fill is what is still recoverable, NOT the
gross amount written off: a recovery may
// not exceed it, so offering the gross figure after a partial
recovery would pre-fill a value the API
// rejects. Term loan pre-fills the gross figure and has that
problem.
- return WorkingCapitalLoanCommandTemplateData.builder()
- .expectedAmount(wcLoan.getBalance() != null ?
wcLoan.getBalance().getWrittenOffOutstanding() : BigDecimal.ZERO)
- .currency(wcLoan.getLoanProduct().getCurrency().toData())
+ return template.expectedAmount(balance != null ?
balance.getWrittenOffOutstanding() : BigDecimal.ZERO)
.paymentTypeOptions(paymentTypeReadPlatformService.retrieveAllPaymentTypes()).build();
+ } else if
(WorkingCapitalLoanConstants.DISCOUNT_FEE_LOAN_COMMAND.equals(command)
+ ||
WorkingCapitalLoanConstants.DISCOUNT_FEE_ADJUSTMENT_LOAN_COMMAND.equals(command))
{
+ return template.classificationOptions(codeValueReadPlatformService
+
.retrieveCodeValuesByCode(WorkingCapitalLoanConstants.DISCOUNT_FEE_CLASSIFICATION_CODE_NAME)).build();
+ } else if
(WorkingCapitalLoanConstants.CHARGE_OFF_LOAN_COMMAND.equals(command)) {
+ // Charge-off amount is the auto-calculated outstanding balance;
the date defaults to the business date.
+ return template.chargeOffAmount(balance != null ?
balance.getTotalOutstanding() : BigDecimal.ZERO)
+
.chargeOffDate(DateUtils.getBusinessLocalDate()).chargeOffReasonOptions(
+
codeValueReadPlatformService.retrieveCodeValuesByCode(WorkingCapitalLoanConstants.CHARGE_OFF_REASONS))
+ .build();
+ } else if
(WorkingCapitalLoanConstants.PREPAY_LOAN_COMMAND.equals(command)) {
+ return prePaymentTemplate(template, balance, transactionDate);
}
return null;
}
+ /**
+ * The payoff quote is read straight off the current balance: a Working
Capital loan accrues nothing over time - the
+ * whole discount is loaded into principal at disbursement and its
amortization only moves income recognition - so
+ * the outstanding buckets move on explicit commands alone and the
snapshot is already the payoff for
+ * {@code transactionDate}. The date is carried into the response so
callers can echo back the date they quoted for.
+ * <p>
+ * It is typed as a plain Repayment because that is what the caller posts
it back as, through the ordinary repayment
+ * command, with the repayment allocation and accounting treatment that
comes with it.
+ */
+ private WorkingCapitalLoanTransactionTemplateData prePaymentTemplate(
+ final
WorkingCapitalLoanTransactionTemplateData.WorkingCapitalLoanTransactionTemplateDataBuilder
template,
+ final WorkingCapitalLoanBalance balance, final LocalDate
transactionDate) {
+ // The balance row is created when the application is submitted, so
every loan reached through the normal flow
+ // has one and the amounts below are real. Reading it defensively
anyway reports a missing balance as absent
+ // amounts rather than as a payoff of zero.
+ final Optional<WorkingCapitalLoanBalance> present =
Optional.ofNullable(balance);
+ return template.transactionDate(transactionDate != null ?
transactionDate : DateUtils.getBusinessLocalDate())
Review Comment:
Created `WorkingCapitalLoanAsOfBalanceReadService` which can return the
balance of a working capital loan at a given `asOfDate`. This calculated
balance is now used by the prepay template and all other templates which used
the balance previously.
It rebuilds the owed side of the balance from the dated events behind it.
Principal is summed from the loan's own transactions dated on or before the
requested date: the disbursement plus any discount fee, less any discount fee
adjustment, counting non-reversed rows only. Fees and penalties are summed from
the loan's charges, with each charge counting from the earlier of the day it
was added and the day it falls due. Everything on the paid side keeps its
stored value, amounts repaid, written off, recovered, the overpayment, and the
principal adjustment. Each outstanding bucket is then charged minus paid minus
written off, floored at zero, using the same arithmetic the balance entity
itself uses so the two can't drift apart. When no date is supplied it defaults
to the business date, so asking for today explicitly and asking by omission go
down the same path.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]