adamsaghy commented on code in PR #3544:
URL: https://github.com/apache/fineract/pull/3544#discussion_r1375230727
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java:
##########
@@ -176,6 +178,153 @@ private void handleRepayment(LoanTransaction
loanTransaction, MonetaryCurrency c
loanTransaction.resetDerivedComponents();
}
Money transactionAmountUnprocessed =
loanTransaction.getAmount(currency);
+ handleOverAmount(loanTransaction, currency, installments,
transactionAmountUnprocessed, charges);
+ }
+
+ private LoanTransactionToRepaymentScheduleMapping getTransactionMapping(
+ List<LoanTransactionToRepaymentScheduleMapping>
transactionMappings, LoanTransaction loanTransaction,
+ LoanRepaymentScheduleInstallment currentInstallment,
MonetaryCurrency currency) {
+ Money zero = Money.zero(currency);
+ LoanTransactionToRepaymentScheduleMapping
loanTransactionToRepaymentScheduleMapping = transactionMappings.stream()
+ .filter(e -> loanTransaction.equals(e.getLoanTransaction()))
+ .filter(e ->
currentInstallment.equals(e.getLoanRepaymentScheduleInstallment())).findFirst().orElse(null);
+ if (loanTransactionToRepaymentScheduleMapping == null) {
+ loanTransactionToRepaymentScheduleMapping =
LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction,
+ currentInstallment, zero, zero, zero, zero);
+ transactionMappings.add(loanTransactionToRepaymentScheduleMapping);
+ }
+ return loanTransactionToRepaymentScheduleMapping;
+ }
+
+ private Money payAllocation(PaymentAllocationType paymentAllocationType,
LoanRepaymentScheduleInstallment currentInstallment,
+ LoanTransaction loanTransaction, Money
transactionAmountUnprocessed,
+ LoanTransactionToRepaymentScheduleMapping
loanTransactionToRepaymentScheduleMapping, Balances balances) {
+ LocalDate transactionDate = loanTransaction.getTransactionDate();
+ Money zero = transactionAmountUnprocessed.zero();
+ return switch (paymentAllocationType.getAllocationType()) {
+ case PENALTY -> {
+ Money subPenaltyPortion =
currentInstallment.payPenaltyChargesComponent(transactionDate,
transactionAmountUnprocessed);
+
balances.setAggregatedPenaltyChargesPortion(balances.getAggregatedPenaltyChargesPortion().add(subPenaltyPortion));
+
addToTransactionMapping(loanTransactionToRepaymentScheduleMapping, zero, zero,
zero, subPenaltyPortion);
+ yield subPenaltyPortion;
+ }
+ case FEE -> {
+ Money subFeePortion =
currentInstallment.payFeeChargesComponent(transactionDate,
transactionAmountUnprocessed);
+
balances.setAggregatedFeeChargesPortion(balances.getAggregatedFeeChargesPortion().add(subFeePortion));
+
addToTransactionMapping(loanTransactionToRepaymentScheduleMapping, zero, zero,
subFeePortion, zero);
+ yield subFeePortion;
+ }
+ case INTEREST -> {
+ Money subInterestPortion =
currentInstallment.payInterestComponent(transactionDate,
transactionAmountUnprocessed);
+
balances.setAggregatedInterestPortion(balances.getAggregatedInterestPortion().add(subInterestPortion));
+
addToTransactionMapping(loanTransactionToRepaymentScheduleMapping, zero,
subInterestPortion, zero, zero);
+ yield subInterestPortion;
+ }
+ case PRINCIPAL -> {
+ Money subPrincipalPortion =
currentInstallment.payPrincipalComponent(transactionDate,
transactionAmountUnprocessed);
+
balances.setAggregatedPrincipalPortion(balances.getAggregatedPrincipalPortion().add(subPrincipalPortion));
+
addToTransactionMapping(loanTransactionToRepaymentScheduleMapping,
subPrincipalPortion, zero, zero, zero);
+ yield subPrincipalPortion;
+ }
+ };
+ }
+
+ private void
addToTransactionMapping(LoanTransactionToRepaymentScheduleMapping
loanTransactionToRepaymentScheduleMapping,
+ Money principalPortion, Money interestPortion, Money feePortion,
Money penaltyPortion) {
+ BigDecimal aggregatedPenalty = ObjectUtils
+
.defaultIfNull(loanTransactionToRepaymentScheduleMapping.getPenaltyChargesPortion(),
BigDecimal.ZERO)
+ .add(penaltyPortion.getAmount());
+ BigDecimal aggregatedFee = ObjectUtils
+
.defaultIfNull(loanTransactionToRepaymentScheduleMapping.getFeeChargesPortion(),
BigDecimal.ZERO)
+ .add(feePortion.getAmount());
+ BigDecimal aggregatedInterest = ObjectUtils
+
.defaultIfNull(loanTransactionToRepaymentScheduleMapping.getInterestPortion(),
BigDecimal.ZERO)
+ .add(interestPortion.getAmount());
+ BigDecimal aggregatedPrincipal = ObjectUtils
+
.defaultIfNull(loanTransactionToRepaymentScheduleMapping.getPrincipalPortion(),
BigDecimal.ZERO)
+ .add(principalPortion.getAmount());
+
loanTransactionToRepaymentScheduleMapping.setComponents(aggregatedPrincipal,
aggregatedInterest, aggregatedFee, aggregatedPenalty);
+ }
+
+ private void handleOverpayment(Money overpaymentPortion, LoanTransaction
loanTransaction) {
+ if (overpaymentPortion.isGreaterThanZero()) {
+ onLoanOverpayment(loanTransaction, overpaymentPortion);
+ loanTransaction.updateOverPayments(overpaymentPortion);
+ }
+ }
+
+ private void handleChargeOff(LoanTransaction loanTransaction,
MonetaryCurrency currency,
+ List<LoanRepaymentScheduleInstallment> installments) {
+ loanTransaction.resetDerivedComponents();
+ // determine how much is outstanding total and breakdown for
principal, interest and charges
+ Money principalPortion = Money.zero(currency);
+ Money interestPortion = Money.zero(currency);
+ Money feeChargesPortion = Money.zero(currency);
+ Money penaltychargesPortion = Money.zero(currency);
+ for (final LoanRepaymentScheduleInstallment currentInstallment :
installments) {
+ if (currentInstallment.isNotFullyPaidOff()) {
+ principalPortion =
principalPortion.plus(currentInstallment.getPrincipalOutstanding(currency));
+ interestPortion =
interestPortion.plus(currentInstallment.getInterestOutstanding(currency));
+ feeChargesPortion =
feeChargesPortion.plus(currentInstallment.getFeeChargesOutstanding(currency));
+ penaltychargesPortion =
penaltychargesPortion.plus(currentInstallment.getPenaltyChargesCharged(currency));
+ }
+ }
+
+ loanTransaction.updateComponentsAndTotal(principalPortion,
interestPortion, feeChargesPortion, penaltychargesPortion);
+ }
+
+ private void handleChargePayment(LoanTransaction loanTransaction,
MonetaryCurrency currency,
+ List<LoanRepaymentScheduleInstallment> installments,
Set<LoanCharge> charges) {
+ Money zero = Money.zero(currency);
+ Money feeChargesPortion = zero;
+ Money penaltyChargesPortion = zero;
+ List<LoanTransactionToRepaymentScheduleMapping> transactionMappings =
new ArrayList<>();
+ LoanChargePaidBy loanChargePaidBy =
loanTransaction.getLoanChargesPaid().stream().findFirst().get();
+ LoanCharge loanCharge = loanChargePaidBy.getLoanCharge();
+ Money amountToBePaid = Money.of(currency, loanTransaction.getAmount());
+ if
(loanCharge.getAmountOutstanding(currency).isLessThan(amountToBePaid)) {
+ amountToBePaid = loanCharge.getAmountOutstanding(currency);
+ }
+
+ LocalDate startDate = loanTransaction.getLoan().getDisbursementDate();
+
+ Money unprocessed = loanTransaction.getAmount(currency);
+ for (final LoanRepaymentScheduleInstallment installment :
installments) {
+ boolean isDue = installment.isFirstPeriod()
+ ?
loanCharge.isDueForCollectionFromIncludingAndUpToAndIncluding(startDate,
installment.getDueDate())
+ :
loanCharge.isDueForCollectionFromAndUpToAndIncluding(startDate,
installment.getDueDate());
+ if (isDue) {
+ Integer installmentNumber = installment.getInstallmentNumber();
+ Money paidAmount =
loanCharge.updatePaidAmountBy(amountToBePaid, installmentNumber, zero);
+
+ LoanTransactionToRepaymentScheduleMapping
loanTransactionToRepaymentScheduleMapping = getTransactionMapping(
+ transactionMappings, loanTransaction, installment,
currency);
+
+ if (loanTransaction.isPenaltyPayment()) {
+ penaltyChargesPortion =
installment.payPenaltyChargesComponent(loanTransaction.getTransactionDate(),
paidAmount);
+ loanTransaction.setLoanChargesPaid(Collections
+ .singleton(new LoanChargePaidBy(loanTransaction,
loanCharge, paidAmount.getAmount(), installmentNumber)));
+
addToTransactionMapping(loanTransactionToRepaymentScheduleMapping, zero, zero,
zero, penaltyChargesPortion);
+ } else {
+ feeChargesPortion =
installment.payFeeChargesComponent(loanTransaction.getTransactionDate(),
paidAmount);
+ loanTransaction.setLoanChargesPaid(Collections
+ .singleton(new LoanChargePaidBy(loanTransaction,
loanCharge, paidAmount.getAmount(), installmentNumber)));
+
addToTransactionMapping(loanTransactionToRepaymentScheduleMapping, zero, zero,
feeChargesPortion, zero);
+ }
+
+ loanTransaction.updateComponents(zero, zero,
feeChargesPortion, penaltyChargesPortion);
+ unprocessed =
loanTransaction.getAmount(currency).minus(paidAmount);
+
loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(transactionMappings);
+ }
+ }
+
+ if (unprocessed.isGreaterThanZero()) {
+ handleOverAmount(loanTransaction, currency, installments,
unprocessed, charges);
Review Comment:
In the handle over amount there is a piece of code which goes through the
charges and checks whether any charge was paid by the loan transaction. We
need to exclude the amount that was already paid for the "original charges" and
consider on the "overpay amount" might paid other charges.
--
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]