diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/AbstractLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/AbstractLoanRepaymentScheduleTransactionProcessor.java index a6e62ea25..7412242bc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/AbstractLoanRepaymentScheduleTransactionProcessor.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/AbstractLoanRepaymentScheduleTransactionProcessor.java @@ -60,18 +60,27 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen handleTransaction(loanTransaction, currency, installments, charges); } else { /** - * reverse existing transactions and create new transactions - * in their Lieu + * For existing transactions, check if the re-payment + * breakup (principal, interest, fees, penalties) has + * changed.
**/ LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionProperties(loanTransaction); - // Reverse the original loan transaction - loanTransaction.reverse(); - changedTransactionDetail.getReversedTransactions().add(loanTransaction); - // Reset derived component of new loan transaction + // Reset derived component of new loan transaction and + // re-process transaction newLoanTransaction.resetDerivedComponents(); - changedTransactionDetail.getNewTransactions().add(newLoanTransaction); handleTransaction(newLoanTransaction, currency, installments, charges); + + /** + * Check if the transaction amounts have changed. If so, + * reverse the original transaction and update + * changedTransactionDetail accordingly + **/ + if (!LoanTransaction.transactionAmountsMatch(currency, loanTransaction, newLoanTransaction)) { + loanTransaction.reverse(); + changedTransactionDetail.getReversedTransactions().add(loanTransaction); + changedTransactionDetail.getNewTransactions().add(newLoanTransaction); + } } } else if (loanTransaction.isWriteOff()) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/ChangedTransactionDetail.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/ChangedTransactionDetail.java index db73b3ec0..576fb115c 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/ChangedTransactionDetail.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/ChangedTransactionDetail.java @@ -3,6 +3,12 @@ package org.mifosplatform.portfolio.loanaccount.domain; import java.util.ArrayList; import java.util.List; +/** + * Stores details of {@link LoanTransaction}'s that were reversed or newly + * created + * + * + */ public class ChangedTransactionDetail { private List newTransactions = new ArrayList(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index 50e0e23c2..72fd18df4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -520,6 +520,14 @@ public class Loan extends AbstractPersistable { final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategy); if (!loanCharge.isDueAtDisbursement() && loanCharge.isPaidOrPartiallyPaid(loanCurrency())) { + /**** + * TODO Vishwas Currently we do not allow removing a loan charge + * after a loan is approved (hence there is no need to adjust any + * loan transactions). + * + * Consider removing this block of code or logically completing it + * for the future by getting the list of affected Transactions + ***/ final List allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments, setOfLoanCharges()); @@ -570,6 +578,14 @@ public class Loan extends AbstractPersistable { final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategy); if (!loanCharge.isDueAtDisbursement()) { + /**** + * TODO Vishwas Currently we do not allow waiving updating loan + * charge after a loan is approved (hence there is no need to adjust + * any loan transactions). + * + * Consider removing this block of code or logically completing it + * for the future by getting the list of affected Transactions + ***/ final List allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments, setOfLoanCharges()); @@ -619,7 +635,7 @@ public class Loan extends AbstractPersistable { .determineProcessor(this.transactionProcessingStrategy); if (!loanCharge.isDueAtDisbursement() && loanCharge.isPaidOrPartiallyPaid(loanCurrency())) { /**** - * TODO vishwas Currently we do not allow waiving fully paid loan + * TODO Vishwas Currently we do not allow waiving fully paid loan * charge and waiving partially paid loan charges only waives the * remaining amount. * @@ -717,6 +733,15 @@ public class Loan extends AbstractPersistable { // if the loan application/contract is modified when repayments are // already against it - then need to re-process it + + /**** + * TODO Vishwas Currently we do not allow modifying a Loan contract once + * the loan is approved (hence there is no need to adjust any loan + * transactions). + * + * Consider removing this block of code or logically completing it for + * the future by getting the list of affected Transactions + ***/ final List repaymentsOrWaivers = retreiveListOfTransactionsPostDisbursement(); final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory @@ -1390,21 +1415,17 @@ public class Loan extends AbstractPersistable { updateLoanSummaryDerivedFields(); } - public LoanTransaction waiveInterest(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine, - final List existingTransactionIds, final List existingReversedTransactionIds) { - - final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); - final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); - - final Money amountToWaive = Money.of(loanCurrency(), transactionAmount); - final LoanTransaction waiveInterestTransaction = LoanTransaction.waiver(this, amountToWaive, transactionDate); + public ChangedTransactionDetail waiveInterest(final LoanTransaction waiveInterestTransaction, + final LoanLifecycleStateMachine loanLifecycleStateMachine, final List existingTransactionIds, + final List existingReversedTransactionIds) { existingTransactionIds.addAll(findExistingTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); - handleRepaymentOrWaiverTransaction(waiveInterestTransaction, loanLifecycleStateMachine, null); + ChangedTransactionDetail changedTransactionDetail = handleRepaymentOrWaiverTransaction(waiveInterestTransaction, + loanLifecycleStateMachine, null); - return waiveInterestTransaction; + return changedTransactionDetail; } public ChangedTransactionDetail makeRepayment(final LoanTransaction repaymentTransaction, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java index 9b4dbe03d..698f2b273 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java @@ -100,6 +100,16 @@ public final class LoanTransaction extends AbstractPersistable { return applyCharge; } + public static boolean transactionAmountsMatch(MonetaryCurrency currency, LoanTransaction loanTransaction, + LoanTransaction newLoanTransaction) { + if (loanTransaction.getAmount(currency).isEqualTo(newLoanTransaction.getAmount(currency)) + && (loanTransaction.getPrincipalPortion(currency).isEqualTo(newLoanTransaction.getPrincipalPortion(currency))) + && (loanTransaction.getInterestPortion(currency).isEqualTo(newLoanTransaction.getInterestPortion(currency))) + && (loanTransaction.getFeeChargesPortion(currency).isEqualTo(newLoanTransaction.getFeeChargesPortion(currency))) + && (loanTransaction.getPenaltyChargesPortion(currency).isEqualTo(newLoanTransaction.getPenaltyChargesPortion(currency)))) { return true; } + return false; + } + private LoanTransaction(Loan loan, Integer typeOf, Date dateOf, BigDecimal amount, BigDecimal principalPortion, BigDecimal interestPortion, BigDecimal feeChargesPortion, BigDecimal penaltyChargesPortion, boolean reversed) { super(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 147b92751..3bf6e86d5 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -292,8 +292,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf * TODO Vishwas Batch save is giving me a * HibernateOptimisticLockingFailureException, looping and saving for * the time being, not a major issue for now as this loop is entered - * only in edge cases (when a payment is made before the latest payment - * recorded against the loan) + * only in edge cases (when a adjustment is made before the latest + * payment recorded against the loan) ***/ if (changedTransactionDetail != null) { for (LoanTransaction loanTransaction : changedTransactionDetail.getNewTransactions()) { @@ -336,21 +336,41 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf changes.put("transactionAmount", command.stringValueOfParameterNamed("transactionAmount")); changes.put("locale", command.locale()); changes.put("dateFormat", command.dateFormat()); + final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); + final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); final Loan loan = retrieveLoanBy(loanId); final List existingTransactionIds = new ArrayList(); final List existingReversedTransactionIds = new ArrayList(); - final LoanTransaction waiveTransaction = loan.waiveInterest(command, defaultLoanLifecycleStateMachine(), existingTransactionIds, - existingReversedTransactionIds); - this.loanTransactionRepository.save(waiveTransaction); + final Money transactionAmountAsMoney = Money.of(loan.getCurrency(), transactionAmount); + LoanTransaction waiveInterestTransaction = LoanTransaction.waiver(loan, transactionAmountAsMoney, transactionDate); + + final ChangedTransactionDetail changedTransactionDetail = loan.waiveInterest(waiveInterestTransaction, + defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds); + + this.loanTransactionRepository.save(waiveInterestTransaction); + + /*** + * TODO Vishwas Batch save is giving me a + * HibernateOptimisticLockingFailureException, looping and saving for + * the time being, not a major issue for now as this loop is entered + * only in edge cases (when a waiver is made before the latest payment + * recorded against the loan) + ***/ + if (changedTransactionDetail != null) { + for (LoanTransaction loanTransaction : changedTransactionDetail.getNewTransactions()) { + this.loanTransactionRepository.save(loanTransaction); + } + } + this.loanRepository.save(loan); final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { changes.put("note", noteText); - final Note note = Note.loanTransactionNote(loan, waiveTransaction, noteText); + final Note note = Note.loanTransactionNote(loan, waiveInterestTransaction, noteText); this.noteRepository.save(note); } @@ -358,7 +378,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // - .withEntityId(waiveTransaction.getId()) // + .withEntityId(waiveInterestTransaction.getId()) // .withOfficeId(loan.getOfficeId()) // .withClientId(loan.getClientId()) // .withGroupId(loan.getGroupId()) //