diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java index 18ef161cf7..922f057a37 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java @@ -1046,47 +1046,52 @@ public Money payInterestComponent(final LocalDate transactionDate, final Money t Money interestDue = Money.zero(currency); - if (this.isMigratedInstallment) { - interestDue = getInterestOutstanding(currency); - } else if (isOn(transactionDate, this.getDueDate())) { - interestDue = getInterestOutstanding(currency); - } else if (isOnOrBetween(transactionDate) && getInterestOutstanding(currency).isGreaterThanZero()) { - final RoundingMode roundingMode = RoundingMode.HALF_UP; - - BigDecimal numberOfDaysForInterestCalculation = BigDecimal.ZERO; - if (this.interestRecalculatedOnDate != null) { - if (this.interestRecalculatedOnDate.isAfter(transactionDate)) { // This should only be true if the - // repayment is reversed + if (loanTransaction.interestPaidByOriginalTransaction().compareTo(BigDecimal.ZERO) > 0) { + // SU-533 Avoid reprocessing of transaction paying different amount than originally paid. + interestDue = interestDue.plus(loanTransaction.interestPaidByOriginalTransaction()); + } else { + if (this.isMigratedInstallment) { + interestDue = getInterestOutstanding(currency); + } else if (isOn(transactionDate, this.getDueDate())) { + interestDue = getInterestOutstanding(currency); + } else if (isOnOrBetween(transactionDate) && getInterestOutstanding(currency).isGreaterThanZero()) { + final RoundingMode roundingMode = RoundingMode.HALF_UP; + + BigDecimal numberOfDaysForInterestCalculation = BigDecimal.ZERO; + if (this.interestRecalculatedOnDate != null) { + if (this.interestRecalculatedOnDate.isAfter(transactionDate)) { // This should only be true if the + // repayment is reversed + numberOfDaysForInterestCalculation = BigDecimal.valueOf(ChronoUnit.DAYS.between(this.fromDate, transactionDate)); + } else { + numberOfDaysForInterestCalculation = BigDecimal + .valueOf(ChronoUnit.DAYS.between(this.interestRecalculatedOnDate, transactionDate)); + } + } else { numberOfDaysForInterestCalculation = BigDecimal.valueOf(ChronoUnit.DAYS.between(this.fromDate, transactionDate)); + } + BigDecimal numberOfDaysInPeriod = BigDecimal.valueOf(ChronoUnit.DAYS.between(this.fromDate, this.dueDate)); + BigDecimal oneDayOfInterest = this.interestCharged.divide(numberOfDaysInPeriod, RoundingMode.HALF_UP); + oneDayOfInterest = oneDayOfInterest.setScale(5, roundingMode); + interestDue = Money.of(currency, oneDayOfInterest.multiply(numberOfDaysForInterestCalculation)); + if (interestDue.isGreaterThan(getInterestOutstanding(currency))) { + interestDue = getInterestOutstanding(currency); + } + + //// Update installment interest charged if principal is fully paid during the accrual period + // Keep the original interest charged in case the transaction is rollbacked and interest charged needs + //// to be moved to original amount. + this.interestRecalculatedOnDate = transactionDate; + if (this.getPrincipalOutstanding(currency).isZero()) { + this.originalInterestChargedAmount = this.interestCharged; + this.interestCharged = getInterestPaid(currency).plus(getInterestWaived(currency)).plus(getInterestWrittenOff(currency)) + .plus(interestDue).getAmount(); } else { - numberOfDaysForInterestCalculation = BigDecimal - .valueOf(ChronoUnit.DAYS.between(this.interestRecalculatedOnDate, transactionDate)); + this.originalInterestChargedAmount = BigDecimal.ZERO; } - } else { - numberOfDaysForInterestCalculation = BigDecimal.valueOf(ChronoUnit.DAYS.between(this.fromDate, transactionDate)); - } - BigDecimal numberOfDaysInPeriod = BigDecimal.valueOf(ChronoUnit.DAYS.between(this.fromDate, this.dueDate)); - BigDecimal oneDayOfInterest = this.interestCharged.divide(numberOfDaysInPeriod, RoundingMode.HALF_UP); - oneDayOfInterest = oneDayOfInterest.setScale(5, roundingMode); - interestDue = Money.of(currency, oneDayOfInterest.multiply(numberOfDaysForInterestCalculation)); - if (interestDue.isGreaterThan(getInterestOutstanding(currency))) { - interestDue = getInterestOutstanding(currency); - } - //// Update installment interest charged if principal is fully paid during the accrual period - // Keep the original interest charged in case the transaction is rollbacked and interest charged needs - //// to be moved to original amount. - this.interestRecalculatedOnDate = transactionDate; - if (this.getPrincipalOutstanding(currency).isZero()) { - this.originalInterestChargedAmount = this.interestCharged; - this.interestCharged = getInterestPaid(currency).plus(getInterestWaived(currency)).plus(getInterestWrittenOff(currency)) - .plus(interestDue).getAmount(); } else { - this.originalInterestChargedAmount = BigDecimal.ZERO; + interestDue = getInterestOutstanding(currency); } - - } else { - interestDue = getInterestOutstanding(currency); } if (transactionAmountRemaining.isGreaterThanOrEqualTo(interestDue)) { diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java index 82afed9031..4e71ba2839 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java @@ -163,6 +163,9 @@ protected LoanTransaction() {} @Transient List chargesPaidByOriginalTransaction; + @Transient + private BigDecimal interestPaidByOriginalTransaction; + public static LoanTransaction incomePosting(final Loan loan, final Office office, final LocalDate dateOf, final BigDecimal amount, final BigDecimal interestPortion, final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion, final ExternalId externalId) { @@ -329,6 +332,7 @@ public static LoanTransaction copyTransactionProperties(final LoanTransaction lo // SU-533 set amounts paid by original transaction so that copied transaction also pays the same amounts // to avoid rollbacks newTransaction.setChargesPaidByOriginalTransaction(getLoanChargePaidByDataList(loanTransaction.getLoanChargesPaid())); + newTransaction.setInterestPaidByOriginalTransaction(loanTransaction.getInterestPortion()); return newTransaction; } @@ -1160,6 +1164,14 @@ public void setChargesPaidByOriginalTransaction(List charg this.chargesPaidByOriginalTransaction = chargesPaidByOriginalTransaction; } + public BigDecimal interestPaidByOriginalTransaction() { + return Objects.requireNonNullElse(interestPaidByOriginalTransaction, BigDecimal.ZERO); + } + + public void setInterestPaidByOriginalTransaction(BigDecimal interestPaidByOriginalTransaction) { + this.interestPaidByOriginalTransaction = interestPaidByOriginalTransaction; + } + private static List getLoanChargePaidByDataList(Set originalList) { List list = new ArrayList<>(); if (originalList != null && !originalList.isEmpty()) {