From 59c5ad45003254accc62ed4c71941d5d83138f39 Mon Sep 17 00:00:00 2001 From: Adam Saghy Date: Wed, 30 Aug 2023 11:55:36 +0200 Subject: [PATCH] FINERACT-1724: Refactor loan repayment transaction processor --- .../portfolio/loanaccount/domain/Loan.java | 61 +-- ...RepaymentScheduleTransactionProcessor.java | 29 +- ...RepaymentScheduleTransactionProcessor.java | 408 ++++++------------ ...edPaymentScheduleTransactionProcessor.java | 21 + ...RepaymentScheduleTransactionProcessor.java | 18 - ...RepaymentScheduleTransactionProcessor.java | 5 - ...RepaymentScheduleTransactionProcessor.java | 5 - ...RepaymentScheduleTransactionProcessor.java | 22 +- ...RepaymentScheduleTransactionProcessor.java | 3 - ...RepaymentScheduleTransactionProcessor.java | 19 - .../domain/AbstractLoanScheduleGenerator.java | 4 +- .../LoanChargeWritePlatformServiceImpl.java | 4 +- 12 files changed, 213 insertions(+), 386 deletions(-) diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index 72eb5fe8d..d6756550e 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -702,7 +702,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategyCode); final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); - changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), + changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); for (final Map.Entry mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { @@ -789,7 +789,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { } final Set loanCharges = new HashSet<>(1); loanCharges.add(charge); - loanRepaymentScheduleTransactionProcessor.handleTransaction(chargesPayment, getCurrency(), chargePaymentInstallments, loanCharges); + loanRepaymentScheduleTransactionProcessor.processLatestTransaction(chargesPayment, getCurrency(), chargePaymentInstallments, + loanCharges, getTotalOverpaidAsMoney()); updateLoanSummaryDerivedFields(); doPostLoanTransactionChecks(chargesPayment.getTransactionDate(), loanLifecycleStateMachine); @@ -862,8 +863,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { * affected Transactions ***/ final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); - loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement, - getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); + loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(getDisbursementDate(), + allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); } this.charges.remove(loanCharge); updateLoanSummaryDerivedFields(); @@ -930,8 +931,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { * affected Transactions ***/ final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); - loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement, - getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); + loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(getDisbursementDate(), + allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); } else { // reprocess loan schedule based on charge been waived. final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper(); @@ -1123,8 +1124,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { * affected Transactions ***/ final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); - loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement, - getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); + loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(getDisbursementDate(), + allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); } else { // reprocess loan schedule based on charge been waived. final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper(); @@ -2705,7 +2706,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { if (!allNonContraTransactionsPostDisbursement.isEmpty()) { final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategyCode); - changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), + changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); for (final Map.Entry mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { mapEntry.getValue().updateLoan(this); @@ -3330,8 +3331,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { if (isTransactionChronologicallyLatest && adjustedTransaction == null && (!reprocess || !this.repaymentScheduleDetail().isInterestRecalculationEnabled()) && !isForeclosure()) { - loanRepaymentScheduleTransactionProcessor.handleTransaction(loanTransaction, getCurrency(), getRepaymentScheduleInstallments(), - getActiveCharges()); + loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, getCurrency(), + getRepaymentScheduleInstallments(), getActiveCharges(), getTotalOverpaidAsMoney()); reprocess = false; if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (currentInstallment == null || currentInstallment.isNotFullyPaidOff()) { @@ -3350,7 +3351,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO); } final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); - changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), + changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); for (final Map.Entry mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { mapEntry.getValue().updateLoan(this); @@ -3778,7 +3779,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) { regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO); } - ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction( + ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions( getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); updateLoanSummaryDerivedFields(); @@ -3904,7 +3905,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { } addLoanTransaction(loanTransaction); - loanRepaymentScheduleTransactionProcessor.handleWriteOff(loanTransaction, loanCurrency(), getRepaymentScheduleInstallments()); + loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, loanCurrency(), + getRepaymentScheduleInstallments(), getActiveCharges(), getTotalOverpaidAsMoney()); updateLoanSummaryDerivedFields(); } @@ -3926,7 +3928,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO); } final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); - changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), + changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); for (final Map.Entry mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { mapEntry.getValue().updateLoan(this); @@ -4008,8 +4010,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { } addLoanTransaction(loanTransaction); - loanRepaymentScheduleTransactionProcessor.handleWriteOff(loanTransaction, loanCurrency(), - getRepaymentScheduleInstallments()); + loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, loanCurrency(), + getRepaymentScheduleInstallments(), getActiveCharges(), getTotalOverpaidAsMoney()); updateLoanSummaryDerivedFields(); } else if (totalOutstanding.isGreaterThanZero()) { @@ -5400,7 +5402,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategyCode); final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); - ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction( + ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions( getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); for (final Map.Entry mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { @@ -5478,6 +5480,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { return this.totalOverpaid; } + public Money getTotalOverpaidAsMoney() { + return Money.of(this.repaymentScheduleDetail().getCurrency(), this.totalOverpaid); + } + public LocalDate getOverpaidOnDate() { return this.overpaidOnDate; } @@ -5539,7 +5545,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategyCode); final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); - ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction( + ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions( getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); for (final Map.Entry mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { @@ -5782,7 +5788,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { for (LoanTransaction loanTransaction : allNonContraTransactionsPostDisbursement) { copyTransactions.add(LoanTransaction.copyTransactionProperties(loanTransaction)); } - loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), copyTransactions, getCurrency(), + loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(getDisbursementDate(), copyTransactions, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); updateLoanSummaryDerivedFields(); @@ -6382,11 +6388,11 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { // If is a refund if (adjustedTransaction == null) { - loanRepaymentScheduleTransactionProcessor.handleRefund(loanTransaction, getCurrency(), getRepaymentScheduleInstallments(), - getActiveCharges()); + loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanTransaction, getCurrency(), + getRepaymentScheduleInstallments(), getActiveCharges(), getTotalOverpaidAsMoney()); } else { final List allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsPostDisbursement(); - changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), + changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges()); for (final Map.Entry mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { mapEntry.getValue().updateLoan(this); @@ -6411,13 +6417,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategyCode); - final Money overpaidAmount = MathUtil.negativeToZero(calculateTotalOverpayment()); // Before Transaction - if (chargebackTransaction.isNotZero(loanCurrency())) { - addLoanTransaction(chargebackTransaction); - } - loanRepaymentScheduleTransactionProcessor.handleChargeback(chargebackTransaction, getCurrency(), overpaidAmount, - getRepaymentScheduleInstallments()); + addLoanTransaction(chargebackTransaction); + loanRepaymentScheduleTransactionProcessor.processLatestTransaction(chargebackTransaction, getCurrency(), + getRepaymentScheduleInstallments(), getActiveCharges(), getTotalOverpaidAsMoney()); updateLoanSummaryDerivedFields(); if (!doPostLoanTransactionChecks(chargebackTransaction.getTransactionDate(), loanLifecycleStateMachine)) { diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/LoanRepaymentScheduleTransactionProcessor.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/LoanRepaymentScheduleTransactionProcessor.java index 52dd30b4b..9131646ea 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/LoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/LoanRepaymentScheduleTransactionProcessor.java @@ -36,15 +36,22 @@ public interface LoanRepaymentScheduleTransactionProcessor { boolean accept(String s); - void handleTransaction(LoanTransaction loanTransaction, MonetaryCurrency currency, List installments, - Set charges); + /** + * Provides support for processing the latest transaction (which should be latest transaction) against the loan + * schedule. + */ + void processLatestTransaction(LoanTransaction loanTransaction, MonetaryCurrency currency, + List installments, Set charges, Money overpaidAmount); - ChangedTransactionDetail handleTransaction(LocalDate disbursementDate, List repaymentsOrWaivers, + /** + * Provides support for passing all {@link LoanTransaction}'s so it will completely re-process the entire loan + * schedule. This is required in cases where the {@link LoanTransaction} being processed is in the past and falls + * before existing transactions or and adjustment is made to an existing in which case the entire loan schedule + * needs to be re-processed. + */ + ChangedTransactionDetail reprocessLoanTranactions(LocalDate disbursementDate, List repaymentsOrWaivers, MonetaryCurrency currency, List repaymentScheduleInstallments, Set charges); - void handleWriteOff(LoanTransaction loanTransaction, MonetaryCurrency loanCurrency, - List repaymentScheduleInstallments); - Money handleRepaymentSchedule(List transactionsPostDisbursement, MonetaryCurrency currency, List installments, Set loanCharges); @@ -52,14 +59,4 @@ public interface LoanRepaymentScheduleTransactionProcessor { * Used in interest recalculation to introduce new interest only installment. */ boolean isInterestFirstRepaymentScheduleTransactionProcessor(); - - void handleRefund(LoanTransaction loanTransaction, MonetaryCurrency currency, List installments, - Set charges); - - void handleChargeback(LoanTransaction loanTransaction, MonetaryCurrency currency, Money overpaidAmount, - List installments); - - void processTransactionsFromDerivedFields(List transactionsPostDisbursement, MonetaryCurrency currency, - List installments, Set charges); - } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java index 3f448f738..7ac6dff24 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java @@ -63,15 +63,8 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen return getCode().equalsIgnoreCase(s) || getName().equalsIgnoreCase(s); } - /** - * Provides support for passing all {@link LoanTransaction}'s so it will completely re-process the entire loan - * schedule. This is required in cases where the {@link LoanTransaction} being processed is in the past and falls - * before existing transactions or and adjustment is made to an existing in which case the entire loan schedule - * needs to be re-processed. - */ - @Override - public ChangedTransactionDetail handleTransaction(final LocalDate disbursementDate, + public ChangedTransactionDetail reprocessLoanTranactions(final LocalDate disbursementDate, final List transactionsPostDisbursement, final MonetaryCurrency currency, final List installments, final Set charges) { @@ -155,20 +148,15 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen for (final LoanTransaction loanTransaction : transactionsToBeProcessed) { // TODO: analyze and remove this if (!loanTransaction.getTypeOf().equals(LoanTransactionType.REFUND_FOR_ACTIVE_LOAN)) { - final Comparator byDate = new Comparator() { - - @Override - public int compare(LoanRepaymentScheduleInstallment ord1, LoanRepaymentScheduleInstallment ord2) { - return ord1.getDueDate().compareTo(ord2.getDueDate()); - } - }; - Collections.sort(installments, byDate); + final Comparator byDate = Comparator + .comparing(LoanRepaymentScheduleInstallment::getDueDate); + installments.sort(byDate); } if (loanTransaction.isRepaymentLikeType() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) { // pass through for new transactions if (loanTransaction.getId() == null) { - handleTransaction(loanTransaction, currency, installments, charges); + processLatestTransaction(loanTransaction, currency, installments, charges, null); loanTransaction.adjustInterestComponent(currency); } else { /** @@ -179,7 +167,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen // Reset derived component of new loan transaction and // re-process transaction - handleTransaction(newLoanTransaction, currency, installments, charges); + processLatestTransaction(newLoanTransaction, currency, installments, charges, null); newLoanTransaction.adjustInterestComponent(currency); /** * Check if the transaction amounts have changed. If so, reverse the original transaction and update @@ -212,6 +200,134 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen return changedTransactionDetail; } + @Override + public void processLatestTransaction(final LoanTransaction loanTransaction, final MonetaryCurrency currency, + final List installments, final Set charges, Money overpaidAmount) { + switch (loanTransaction.getTypeOf()) { + case WRITEOFF -> handleWriteOff(loanTransaction, currency, installments); + case REFUND_FOR_ACTIVE_LOAN -> handleRefund(loanTransaction, currency, installments, charges); + case CHARGEBACK -> handleChargeback(loanTransaction, currency, overpaidAmount, installments); + default -> { + Money transactionAmountUnprocessed = handleTransactionAndCharges(loanTransaction, currency, installments, charges, null, + false); + if (transactionAmountUnprocessed.isGreaterThanZero()) { + if (loanTransaction.isWaiver()) { + loanTransaction.updateComponentsAndTotal(transactionAmountUnprocessed.zero(), transactionAmountUnprocessed.zero(), + transactionAmountUnprocessed.zero(), transactionAmountUnprocessed.zero()); + } else { + onLoanOverpayment(loanTransaction, transactionAmountUnprocessed); + loanTransaction.updateOverPayments(transactionAmountUnprocessed); + } + } + } + } + } + + @Override + public Money handleRepaymentSchedule(final List transactionsPostDisbursement, final MonetaryCurrency currency, + final List installments, Set loanCharges) { + Money unProcessed = Money.zero(currency); + for (final LoanTransaction loanTransaction : transactionsPostDisbursement) { + if (loanTransaction.isRepaymentLikeType() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) { + loanTransaction.resetDerivedComponents(); + } + if (loanTransaction.isInterestWaiver()) { + processTransaction(loanTransaction, currency, installments, loanCharges, null); + } else { + unProcessed = processTransaction(loanTransaction, currency, installments, loanCharges, null); + } + } + return unProcessed; + } + + @Override + public boolean isInterestFirstRepaymentScheduleTransactionProcessor() { + return false; + } + + // abstract interface + + /** + * For early/'in advance' repayments. + * + * @param transactionMappings + * TODO + * @param charges + */ + protected abstract Money handleTransactionThatIsPaymentInAdvanceOfInstallment(LoanRepaymentScheduleInstallment currentInstallment, + List installments, LoanTransaction loanTransaction, Money paymentInAdvance, + List transactionMappings, Set charges); + + /** + * For normal on-time repayments. + * + * @param transactionMappings + * TODO + * @param charges + */ + protected abstract Money handleTransactionThatIsOnTimePaymentOfInstallment(LoanRepaymentScheduleInstallment currentInstallment, + LoanTransaction loanTransaction, Money transactionAmountUnprocessed, + List transactionMappings, Set charges); + + /** + * For late repayments, how should components of installment be paid off + * + * @param transactionMappings + * TODO + * @param charges + */ + protected abstract Money handleTransactionThatIsALateRepaymentOfInstallment(LoanRepaymentScheduleInstallment currentInstallment, + List installments, LoanTransaction loanTransaction, Money transactionAmountUnprocessed, + List transactionMappings, Set charges); + + /** + * Invoked when a transaction results in an over-payment of the full loan. + * + * transaction amount is greater than the total expected principal and interest of the loan. + */ + @SuppressWarnings("unused") + protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) { + // empty implementation by default. + } + + /** + * Invoked when a there is a refund of an active loan or undo of an active loan + * + * Undoes principal, interest, fees and charges of this transaction based on the repayment strategy + * + * @param transactionMappings + * TODO + * + */ + protected abstract Money handleRefundTransactionPaymentOfInstallment(LoanRepaymentScheduleInstallment currentInstallment, + LoanTransaction loanTransaction, Money transactionAmountUnprocessed, + List transactionMappings); + + /** + * This method is responsible for checking if the current transaction is 'an advance/early payment' based on the + * details passed through. + * + * Default implementation is check transaction date is before installment due date. + */ + protected boolean isTransactionInAdvanceOfInstallment(final int installmentIndex, + final List installments, final LocalDate transactionDate) { + final LoanRepaymentScheduleInstallment currentInstallment = installments.get(installmentIndex); + return transactionDate.isBefore(currentInstallment.getDueDate()); + } + + /** + * This method is responsible for checking if the current transaction is 'an advance/early payment' based on the + * details passed through. + * + * Default implementation simply processes transactions as 'Late' if the transaction date is after the installment + * due date. + */ + protected boolean isTransactionALateRepaymentOnInstallment(final int installmentIndex, + final List installments, final LocalDate transactionDate) { + final LoanRepaymentScheduleInstallment currentInstallment = installments.get(installmentIndex); + return transactionDate.isAfter(currentInstallment.getDueDate()); + } + private void recalculateChargeOffTransaction(ChangedTransactionDetail changedTransactionDetail, LoanTransaction loanTransaction, MonetaryCurrency currency, List installments) { final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionProperties(loanTransaction); @@ -422,42 +538,9 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen } } - /** - * Provides support for processing the latest transaction (which should be latest transaction) against the loan - * schedule. - */ - @Override - public void handleTransaction(final LoanTransaction loanTransaction, final MonetaryCurrency currency, - final List installments, final Set charges) { - - final Money amountToProcess = null; - final boolean isChargeAmount = false; - handleTransaction(loanTransaction, currency, installments, charges, amountToProcess, isChargeAmount); - - } - - private void handleTransaction(final LoanTransaction loanTransaction, final MonetaryCurrency currency, - final List installments, final Set charges, final Money chargeAmountToProcess, - final boolean isFeeCharge) { - - Money transactionAmountUnprocessed = handleTransactionAndCharges(loanTransaction, currency, installments, charges, - chargeAmountToProcess, isFeeCharge); - - if (transactionAmountUnprocessed.isGreaterThanZero()) { - if (loanTransaction.isWaiver()) { - loanTransaction.updateComponentsAndTotal(transactionAmountUnprocessed.zero(), transactionAmountUnprocessed.zero(), - transactionAmountUnprocessed.zero(), transactionAmountUnprocessed.zero()); - } else { - onLoanOverpayment(loanTransaction, transactionAmountUnprocessed); - loanTransaction.updateOverPayments(transactionAmountUnprocessed); - } - } - } - private Money handleTransactionAndCharges(final LoanTransaction loanTransaction, final MonetaryCurrency currency, final List installments, final Set charges, final Money chargeAmountToProcess, final boolean isFeeCharge) { - // to. if (loanTransaction.isRepaymentLikeType() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) { loanTransaction.resetDerivedComponents(); } @@ -505,20 +588,13 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen for (final LoanRepaymentScheduleInstallment currentInstallment : installments) { if (transactionAmountUnprocessed.isGreaterThanZero()) { if (currentInstallment.isNotFullyPaidOff()) { - - // is this transaction early/late/on-time with respect to - // the - // current installment? if (isTransactionInAdvanceOfInstallment(installmentIndex, installments, transactionDate)) { transactionAmountUnprocessed = handleTransactionThatIsPaymentInAdvanceOfInstallment(currentInstallment, installments, loanTransaction, transactionAmountUnprocessed, transactionMappings, charges); } else if (isTransactionALateRepaymentOnInstallment(installmentIndex, installments, transactionDate)) { - // does this result in a late payment of existing - // installment? transactionAmountUnprocessed = handleTransactionThatIsALateRepaymentOfInstallment(currentInstallment, installments, loanTransaction, transactionAmountUnprocessed, transactionMappings, charges); } else { - // standard transaction transactionAmountUnprocessed = handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed, transactionMappings, charges); } @@ -611,8 +687,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen return earliestUnpaidCharge; } - @Override - public void handleWriteOff(final LoanTransaction loanTransaction, final MonetaryCurrency currency, + private void handleWriteOff(final LoanTransaction loanTransaction, final MonetaryCurrency currency, final List installments) { final LocalDate transactionDate = loanTransaction.getTransactionDate(); @@ -637,121 +712,16 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen loanTransaction.updateComponentsAndTotal(principalPortion, interestPortion, feeChargesPortion, penaltychargesPortion); } - // abstract interface - /** - * This method is responsible for checking if the current transaction is 'an advance/early payment' based on the - * details passed through. - * - * Default implementation simply processes transactions as 'Late' if the transaction date is after the installment - * due date. - */ - protected boolean isTransactionALateRepaymentOnInstallment(final int installmentIndex, - final List installments, final LocalDate transactionDate) { - - final LoanRepaymentScheduleInstallment currentInstallment = installments.get(installmentIndex); - - return transactionDate.isAfter(currentInstallment.getDueDate()); - } - - /** - * For late repayments, how should components of installment be paid off - * - * @param transactionMappings - * TODO - * @param charges - */ - protected abstract Money handleTransactionThatIsALateRepaymentOfInstallment(LoanRepaymentScheduleInstallment currentInstallment, - List installments, LoanTransaction loanTransaction, Money transactionAmountUnprocessed, - List transactionMappings, Set charges); - - /** - * This method is responsible for checking if the current transaction is 'an advance/early payment' based on the - * details passed through. - * - * Default implementation is check transaction date is before installment due date. - */ - protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex, - final List installments, final LocalDate transactionDate) { - - final LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex); - - return transactionDate.isBefore(currentInstallment.getDueDate()); - } - - /** - * For early/'in advance' repayments. - * - * @param transactionMappings - * TODO - * @param charges - */ - protected abstract Money handleTransactionThatIsPaymentInAdvanceOfInstallment(LoanRepaymentScheduleInstallment currentInstallment, - List installments, LoanTransaction loanTransaction, Money paymentInAdvance, - List transactionMappings, Set charges); - - /** - * For normal on-time repayments. - * - * @param transactionMappings - * TODO - * @param charges - */ - protected abstract Money handleTransactionThatIsOnTimePaymentOfInstallment(LoanRepaymentScheduleInstallment currentInstallment, - LoanTransaction loanTransaction, Money transactionAmountUnprocessed, - List transactionMappings, Set charges); - - /** - * Invoked when a transaction results in an over-payment of the full loan. - * - * transaction amount is greater than the total expected principal and interest of the loan. - */ - @SuppressWarnings("unused") - protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) { - // empty implementation by default. - } - - @Override - public Money handleRepaymentSchedule(final List transactionsPostDisbursement, final MonetaryCurrency currency, - final List installments, Set loanCharges) { - Money unProcessed = Money.zero(currency); - for (final LoanTransaction loanTransaction : transactionsPostDisbursement) { - Money amountToProcess = null; - if (loanTransaction.isRepaymentLikeType() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) { - loanTransaction.resetDerivedComponents(); - } - if (loanTransaction.isInterestWaiver()) { - processTransaction(loanTransaction, currency, installments, loanCharges, amountToProcess); - } else { - unProcessed = processTransaction(loanTransaction, currency, installments, loanCharges, amountToProcess); - } - } - return unProcessed; - } - - @Override - public boolean isInterestFirstRepaymentScheduleTransactionProcessor() { - return false; - } - - @Override - public void handleChargeback(LoanTransaction loanTransaction, MonetaryCurrency currency, Money overpaidAmount, + private void handleChargeback(LoanTransaction loanTransaction, MonetaryCurrency currency, Money overpaidAmount, List installments) { processCreditTransaction(loanTransaction, overpaidAmount, currency, installments); } - @Override - public void handleRefund(LoanTransaction loanTransaction, MonetaryCurrency currency, + private void handleRefund(LoanTransaction loanTransaction, MonetaryCurrency currency, List installments, final Set charges) { - // TODO Auto-generated method stub List transactionMappings = new ArrayList<>(); - final Comparator byDate = new Comparator() { - - @Override - public int compare(LoanRepaymentScheduleInstallment ord1, LoanRepaymentScheduleInstallment ord2) { - return ord1.getDueDate().compareTo(ord2.getDueDate()); - } - }; - Collections.sort(installments, Collections.reverseOrder(byDate)); + final Comparator byDate = Comparator.comparing(LoanRepaymentScheduleInstallment::getDueDate); + installments.sort(Collections.reverseOrder(byDate)); Money transactionAmountUnprocessed = loanTransaction.getAmount(currency); for (final LoanRepaymentScheduleInstallment currentInstallment : installments) { @@ -786,19 +756,6 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(transactionMappings); } - /** - * Invoked when a there is a refund of an active loan or undo of an active loan - * - * Undoes principal, interest, fees and charges of this transaction based on the repayment strategy - * - * @param transactionMappings - * TODO - * - */ - protected abstract Money handleRefundTransactionPaymentOfInstallment(LoanRepaymentScheduleInstallment currentInstallment, - LoanTransaction loanTransaction, Money transactionAmountUnprocessed, - List transactionMappings); - private void undoChargesPaidAmountBy(final LoanTransaction loanTransaction, final Money feeCharges, final Set charges, final Integer installmentNumber) { @@ -853,91 +810,4 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen return latestPaidCharge; } - - @Override - public void processTransactionsFromDerivedFields(List transactionsPostDisbursement, MonetaryCurrency currency, - List installments, final Set charges) { - for (final LoanTransaction loanTransaction : transactionsPostDisbursement) { - if (!loanTransaction.isAccrualTransaction()) { - processTransactionFromDerivedFields(loanTransaction, currency, installments, charges); - } - } - } - - private void processTransactionFromDerivedFields(final LoanTransaction loanTransaction, MonetaryCurrency currency, - List installments, final Set charges) { - Money principal = loanTransaction.getPrincipalPortion(currency); - Money interest = loanTransaction.getInterestPortion(currency); - if (loanTransaction.isInterestWaiver()) { - interest = loanTransaction.getAmount(currency); - } - Money feeCharges = loanTransaction.getFeeChargesPortion(currency); - Money penaltyCharges = loanTransaction.getPenaltyChargesPortion(currency); - final LocalDate transactionDate = loanTransaction.getTransactionDate(); - if (principal.isGreaterThanZero() || interest.isGreaterThanZero() || feeCharges.isGreaterThanZero() - || penaltyCharges.isGreaterThanZero()) { - for (final LoanRepaymentScheduleInstallment currentInstallment : installments) { - if (currentInstallment.isNotFullyPaidOff()) { - if (penaltyCharges.isGreaterThanZero()) { - Money penaltyChargesPortion = Money.zero(currency); - if (loanTransaction.isWaiver()) { - penaltyChargesPortion = currentInstallment.waivePenaltyChargesComponent(transactionDate, penaltyCharges); - } else { - penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionDate, penaltyCharges); - } - penaltyCharges = penaltyCharges.minus(penaltyChargesPortion); - } - - if (feeCharges.isGreaterThanZero()) { - Money feeChargesPortion = Money.zero(currency); - if (loanTransaction.isWaiver()) { - feeChargesPortion = currentInstallment.waiveFeeChargesComponent(transactionDate, feeCharges); - } else { - feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionDate, feeCharges); - } - feeCharges = feeCharges.minus(feeChargesPortion); - } - - if (interest.isGreaterThanZero()) { - Money interestPortion = Money.zero(currency); - if (loanTransaction.isWaiver()) { - interestPortion = currentInstallment.waiveInterestComponent(transactionDate, interest); - } else { - interestPortion = currentInstallment.payInterestComponent(transactionDate, interest); - } - interest = interest.minus(interestPortion); - } - - if (principal.isGreaterThanZero()) { - Money principalPortion = currentInstallment.payPrincipalComponent(transactionDate, principal); - principal = principal.minus(principalPortion); - } - } - if (!(principal.isGreaterThanZero() || interest.isGreaterThanZero() || feeCharges.isGreaterThanZero() - || penaltyCharges.isGreaterThanZero())) { - break; - } - } - } - - final Set loanFees = extractFeeCharges(charges); - final Set loanPenalties = extractPenaltyCharges(charges); - Integer installmentNumber = null; - if (loanTransaction.isChargePayment() && installments.size() == 1) { - installmentNumber = installments.get(0).getInstallmentNumber(); - } - - if (loanTransaction.isNotWaiver()) { - feeCharges = loanTransaction.getFeeChargesPortion(currency); - penaltyCharges = loanTransaction.getPenaltyChargesPortion(currency); - if (feeCharges.isGreaterThanZero()) { - updateChargesPaidAmountBy(loanTransaction, feeCharges, loanFees, installmentNumber); - } - - if (penaltyCharges.isGreaterThanZero()) { - updateChargesPaidAmountBy(loanTransaction, penaltyCharges, loanPenalties, installmentNumber); - } - } - } - } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java index 5fc2bc35c..aa236efde 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java @@ -18,10 +18,13 @@ */ package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl; +import java.time.LocalDate; import java.util.List; import java.util.Set; import org.apache.commons.lang3.NotImplementedException; +import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; +import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; @@ -69,4 +72,22 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep List transactionMappings) { throw new NotImplementedException(); } + + @Override + public ChangedTransactionDetail reprocessLoanTranactions(LocalDate disbursementDate, List transactionsPostDisbursement, + MonetaryCurrency currency, List installments, Set charges) { + throw new NotImplementedException(); + } + + @Override + public void processLatestTransaction(LoanTransaction loanTransaction, MonetaryCurrency currency, + List installments, Set charges, Money overpaidAmount) { + throw new NotImplementedException(); + } + + @Override + public Money handleRepaymentSchedule(List transactionsPostDisbursement, MonetaryCurrency currency, + List installments, Set loanCharges) { + return super.handleRepaymentSchedule(transactionsPostDisbursement, currency, installments, loanCharges); + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/CreocoreLoanRepaymentScheduleTransactionProcessor.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/CreocoreLoanRepaymentScheduleTransactionProcessor.java index 88719e6f5..501f93558 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/CreocoreLoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/CreocoreLoanRepaymentScheduleTransactionProcessor.java @@ -56,18 +56,6 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL return STRATEGY_NAME; } - /** - * For creocore, early is defined as any date before the installment due date - */ - @Override - protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex, - final List installments, final LocalDate transactionDate) { - - final LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex); - - return transactionDate.isBefore(currentInstallment.getDueDate()); - } - /** * For early/'in advance' repayments, pay off in the same way as on-time payments, interest first then principal. */ @@ -153,12 +141,6 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL return transactionAmountRemaining; } - @SuppressWarnings("unused") - @Override - protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) { - // dont do anything for with loan over-payment - } - @Override protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor.java index f9e038652..7916e894d 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor.java @@ -245,11 +245,6 @@ public class DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactio return transactionAmountRemaining; } - @Override - protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) { - // TODO - KW - dont do anything with loan over-payment for now - } - @Override protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor.java index 6a8bf1e6a..a16e7f15f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactionProcessor.java @@ -247,11 +247,6 @@ public class DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactio return transactionAmountRemaining; } - @Override - protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) { - // TODO - KW - dont do anything with loan over-payment for now - } - @Override protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/FineractStyleLoanRepaymentScheduleTransactionProcessor.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/FineractStyleLoanRepaymentScheduleTransactionProcessor.java index 11d73813b..fb3ab2f02 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/FineractStyleLoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/FineractStyleLoanRepaymentScheduleTransactionProcessor.java @@ -56,15 +56,6 @@ public class FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst return STRATEGY_NAME; } - @Override - protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex, - final List installments, final LocalDate transactionDate) { - - final LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex); - - return transactionDate.isBefore(currentInstallment.getDueDate()); - } - /** * For early/'in advance' repayments, pay off in the same way as on-time payments, interest first then principal. */ @@ -151,11 +142,6 @@ public class FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst return transactionAmountRemaining; } - @Override - protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) { - // TODO - KW - dont do anything with loan over-payment for now - } - @Override protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, @@ -164,10 +150,10 @@ public class FineractStyleLoanRepaymentScheduleTransactionProcessor extends Abst final LocalDate transactionDate = loanTransaction.getTransactionDate(); final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency(); Money transactionAmountRemaining = transactionAmountUnprocessed; - Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency()); - Money interestPortion = Money.zero(transactionAmountRemaining.getCurrency()); - Money feeChargesPortion = Money.zero(transactionAmountRemaining.getCurrency()); - Money penaltyChargesPortion = Money.zero(transactionAmountRemaining.getCurrency()); + Money principalPortion = Money.zero(currency); + Money interestPortion = Money.zero(currency); + Money feeChargesPortion = Money.zero(currency); + Money penaltyChargesPortion = Money.zero(currency); principalPortion = currentInstallment.unpayPrincipalComponent(transactionDate, transactionAmountRemaining); transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java index 3e327c5cd..e94cee83a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java @@ -216,9 +216,6 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst return transactionAmountRemaining; } - @Override - protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) {} - @Override protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/RBILoanRepaymentScheduleTransactionProcessor.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/RBILoanRepaymentScheduleTransactionProcessor.java index 97de4da3b..9987180b9 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/RBILoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/RBILoanRepaymentScheduleTransactionProcessor.java @@ -59,19 +59,6 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe return STRATEGY_NAME; } - /** - * For creocore, early is defined as any date before the installment due date - */ - @SuppressWarnings("unused") - @Override - protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex, - final List installments, final LocalDate transactionDate) { - - final LoanRepaymentScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex); - - return transactionDate.isBefore(currentInstallment.getDueDate()); - } - /** * For early/'in advance' repayments, pays off principal component only. */ @@ -259,12 +246,6 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe return transactionAmountRemaining; } - @SuppressWarnings("unused") - @Override - protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) { - // dont do anything for with loan over-payment - } - @Override public boolean isInterestFirstRepaymentScheduleTransactionProcessor() { return true; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java index d76f75e3f..8229dbddd 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java @@ -2808,8 +2808,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener loanRepaymentScheduleTransactionProcessor, onDate, calculateTill); List loanTransactions = loan.retrieveListOfTransactionsPostDisbursementExcludeAccruals(); - loanRepaymentScheduleTransactionProcessor.handleTransaction(loanApplicationTerms.getExpectedDisbursementDate(), loanTransactions, - currency, loanScheduleDTO.getInstallments(), loan.getActiveCharges()); + loanRepaymentScheduleTransactionProcessor.reprocessLoanTranactions(loanApplicationTerms.getExpectedDisbursementDate(), + loanTransactions, currency, loanScheduleDTO.getInstallments(), loan.getActiveCharges()); Money feeCharges = Money.zero(currency); Money penaltyCharges = Money.zero(currency); Money totalPrincipal = Money.zero(currency); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java index 342ca5583..83b4c0508 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java @@ -809,8 +809,8 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo defaultLoanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, loan); final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory .determineProcessor(loan.transactionProcessingStrategy()); - loanRepaymentScheduleTransactionProcessor.handleTransaction(loanChargeAdjustmentTransaction, loan.getCurrency(), - loan.getRepaymentScheduleInstallments(), loan.getActiveCharges()); + loanRepaymentScheduleTransactionProcessor.processLatestTransaction(loanChargeAdjustmentTransaction, loan.getCurrency(), + loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(), loan.getTotalOverpaidAsMoney()); loan.addLoanTransaction(loanChargeAdjustmentTransaction); loan.updateLoanSummaryAndStatus();