FINERACT-2176: fix Interest portion is missing from Charge-off transaction
This commit is contained in:
+19
-10
@@ -1349,13 +1349,11 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
Money feeChargesPortion = Money.zero(transactionCtx.getCurrency());
|
Money feeChargesPortion = Money.zero(transactionCtx.getCurrency());
|
||||||
Money penaltychargesPortion = Money.zero(transactionCtx.getCurrency());
|
Money penaltychargesPortion = Money.zero(transactionCtx.getCurrency());
|
||||||
for (final LoanRepaymentScheduleInstallment currentInstallment : transactionCtx.getInstallments()) {
|
for (final LoanRepaymentScheduleInstallment currentInstallment : transactionCtx.getInstallments()) {
|
||||||
if (currentInstallment.isNotFullyPaidOff()) {
|
principalPortion = principalPortion.plus(currentInstallment.getPrincipalOutstanding(transactionCtx.getCurrency()));
|
||||||
principalPortion = principalPortion.plus(currentInstallment.getPrincipalOutstanding(transactionCtx.getCurrency()));
|
interestPortion = interestPortion.plus(currentInstallment.getInterestOutstanding(transactionCtx.getCurrency()));
|
||||||
interestPortion = interestPortion.plus(currentInstallment.getInterestOutstanding(transactionCtx.getCurrency()));
|
feeChargesPortion = feeChargesPortion.plus(currentInstallment.getFeeChargesOutstanding(transactionCtx.getCurrency()));
|
||||||
feeChargesPortion = feeChargesPortion.plus(currentInstallment.getFeeChargesOutstanding(transactionCtx.getCurrency()));
|
penaltychargesPortion = penaltychargesPortion
|
||||||
penaltychargesPortion = penaltychargesPortion
|
.plus(currentInstallment.getPenaltyChargesOutstanding(transactionCtx.getCurrency()));
|
||||||
.plus(currentInstallment.getPenaltyChargesOutstanding(transactionCtx.getCurrency()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loanTransaction.updateComponentsAndTotal(principalPortion, interestPortion, feeChargesPortion, penaltychargesPortion);
|
loanTransaction.updateComponentsAndTotal(principalPortion, interestPortion, feeChargesPortion, penaltychargesPortion);
|
||||||
@@ -1489,9 +1487,20 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
final Money amountToEditLastInstallment = loanTransaction.getLoan().getPrincipal().minus(installments.stream()
|
final Money amountToEditLastInstallment = loanTransaction.getLoan().getPrincipal().minus(installments.stream()
|
||||||
.filter(i -> !i.isAdditional()).map(LoanRepaymentScheduleInstallment::getPrincipal).reduce(ZERO, BigDecimal::add));
|
.filter(i -> !i.isAdditional()).map(LoanRepaymentScheduleInstallment::getPrincipal).reduce(ZERO, BigDecimal::add));
|
||||||
|
|
||||||
if (amountToEditLastInstallment.getAmount().signum() != 0) {
|
BigDecimal principalBalance = amountToEditLastInstallment.getAmount();
|
||||||
installments.stream().filter(i -> !i.isAdditional() && !i.isObligationsMet()).reduce((first, second) -> second)
|
for (int i = installments.size() - 1; i > 0 && BigDecimal.ZERO.compareTo(principalBalance) != 0; i--) {
|
||||||
.ifPresent(last -> last.updatePrincipal(last.getPrincipal().add(amountToEditLastInstallment.getAmount())));
|
final LoanRepaymentScheduleInstallment installment = installments.get(i);
|
||||||
|
if (!installment.isAdditional() && !installment.isObligationsMet()) {
|
||||||
|
final BigDecimal installmentPrincipal = MathUtil.nullToZero(installment.getPrincipal());
|
||||||
|
|
||||||
|
installment.updatePrincipal(MathUtil.negativeToZero(installmentPrincipal.add(principalBalance)));
|
||||||
|
if (MathUtil.isLessThanOrEqualTo(MathUtil.abs(principalBalance), installmentPrincipal)) {
|
||||||
|
principalBalance = BigDecimal.ZERO;
|
||||||
|
} else {
|
||||||
|
principalBalance = principalBalance.signum() < 0 ? principalBalance.add(installmentPrincipal)
|
||||||
|
: principalBalance.subtract(installmentPrincipal);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+55
@@ -28,6 +28,7 @@ import io.restassured.builder.ResponseSpecBuilder;
|
|||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
import io.restassured.specification.RequestSpecification;
|
import io.restassured.specification.RequestSpecification;
|
||||||
import io.restassured.specification.ResponseSpecification;
|
import io.restassured.specification.ResponseSpecification;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.format.DateTimeFormatterBuilder;
|
import java.time.format.DateTimeFormatterBuilder;
|
||||||
@@ -35,6 +36,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import org.apache.fineract.client.models.AdvancedPaymentData;
|
import org.apache.fineract.client.models.AdvancedPaymentData;
|
||||||
import org.apache.fineract.client.models.AllowAttributeOverrides;
|
import org.apache.fineract.client.models.AllowAttributeOverrides;
|
||||||
import org.apache.fineract.client.models.ChargeData;
|
import org.apache.fineract.client.models.ChargeData;
|
||||||
@@ -46,6 +48,7 @@ import org.apache.fineract.client.models.GetLoanTransactionRelation;
|
|||||||
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
||||||
import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
|
import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
|
||||||
import org.apache.fineract.client.models.JournalEntryTransactionItem;
|
import org.apache.fineract.client.models.JournalEntryTransactionItem;
|
||||||
|
import org.apache.fineract.client.models.PostClientsResponse;
|
||||||
import org.apache.fineract.client.models.PostLoanProductsRequest;
|
import org.apache.fineract.client.models.PostLoanProductsRequest;
|
||||||
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
||||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
|
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
|
||||||
@@ -541,6 +544,58 @@ public class LoanAccountChargeOffWithAdvancedPaymentAllocationTest extends BaseL
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProgressiveChargeOffWithEarlyRepaymentZeroInterest() {
|
||||||
|
final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
|
||||||
|
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||||
|
final PostLoanProductsResponse loanProductsResponse = loanProductHelper
|
||||||
|
.createLoanProduct(create4IProgressive().chargeOffBehaviour("ZERO_INTEREST"));
|
||||||
|
|
||||||
|
runAt("01 January 2024", () -> {
|
||||||
|
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "01 January 2024",
|
||||||
|
1000.0, 7.0, 6, null);
|
||||||
|
|
||||||
|
loanIdRef.set(loanId);
|
||||||
|
|
||||||
|
disburseLoan(loanId, BigDecimal.valueOf(1000), "01 January 2024");
|
||||||
|
|
||||||
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||||
|
validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 2024", 164.26, 0, 0, 5.83);
|
||||||
|
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2024", 165.21, 0, 0, 4.88);
|
||||||
|
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2024", 166.18, 0, 0, 3.91);
|
||||||
|
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2024", 167.15, 0, 0, 2.94);
|
||||||
|
validateFullyUnpaidRepaymentPeriod(loanDetails, 5, "01 June 2024", 168.12, 0, 0, 1.97);
|
||||||
|
validateFullyUnpaidRepaymentPeriod(loanDetails, 6, "01 July 2024", 169.08, 0, 0, 0.99);
|
||||||
|
|
||||||
|
verifyTransactions(loanId, transaction(1000.0d, "Disbursement", "01 January 2024"));
|
||||||
|
executeInlineCOB(loanId);
|
||||||
|
});
|
||||||
|
runAt("10 February 2024", () -> {
|
||||||
|
Long loanId = loanIdRef.get();
|
||||||
|
|
||||||
|
loanTransactionHelper.makeLoanRepayment(loanId, new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN)
|
||||||
|
.transactionDate("15 January 2024").locale("en").transactionAmount(170.09));
|
||||||
|
|
||||||
|
loanTransactionHelper.chargeOffLoan(loanId,
|
||||||
|
new PostLoansLoanIdTransactionsRequest().transactionDate("31 January 2024").locale("en").dateFormat("dd MMMM yyyy"));
|
||||||
|
|
||||||
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||||
|
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 1), 164.95, 167.46, -2.51, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.14,
|
||||||
|
2.63, 2.51, 170.09, 0.0);
|
||||||
|
validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 2024", 170.09, 0, 0, 0.0);
|
||||||
|
validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 2024", 170.09, 0, 0, 0.0);
|
||||||
|
validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2024", 170.09, 0, 0, 0.0);
|
||||||
|
validateFullyUnpaidRepaymentPeriod(loanDetails, 5, "01 June 2024", 170.09, 0, 0, 0.0);
|
||||||
|
validateFullyUnpaidRepaymentPeriod(loanDetails, 6, "01 July 2024", 154.69, 0, 0, 0.0);
|
||||||
|
|
||||||
|
verifyTransactions(loanId, //
|
||||||
|
transaction(1000.0d, "Disbursement", "01 January 2024", 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||||
|
transaction(170.09d, "Repayment", "15 January 2024", 832.54, 167.46, 2.63, 0.0, 0.0, 0.0, 0.0, false), //
|
||||||
|
transaction(835.05d, "Charge-off", "31 January 2024", 0.0, 832.54, 2.51, 0.0, 0.0, 0.0, 0.0, false) //
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void verifyJournalEntry(JournalEntryTransactionItem journalEntryTransactionItem, Double amount, LocalDate entryDate,
|
private void verifyJournalEntry(JournalEntryTransactionItem journalEntryTransactionItem, Double amount, LocalDate entryDate,
|
||||||
Account account, String type) {
|
Account account, String type) {
|
||||||
assertEquals(amount, journalEntryTransactionItem.getAmount());
|
assertEquals(amount, journalEntryTransactionItem.getAmount());
|
||||||
|
|||||||
+2
-4
@@ -33,9 +33,8 @@ import org.apache.fineract.client.models.PostClientsResponse;
|
|||||||
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
||||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
|
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
|
||||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class LoanCOBCreateAccrualsTest extends BaseLoanIntegrationTest {
|
public class LoanCOBCreateAccrualsTest extends BaseLoanIntegrationTest {
|
||||||
@@ -474,7 +473,7 @@ public class LoanCOBCreateAccrualsTest extends BaseLoanIntegrationTest {
|
|||||||
|
|
||||||
// Accruals around installment due dates are as expected
|
// Accruals around installment due dates are as expected
|
||||||
validateTransactionsExist(loanDetails, //
|
validateTransactionsExist(loanDetails, //
|
||||||
transaction(0.16, "Accrual", "20 January 2025", 0.0, 0.0, 0.16, 0.0, 0.0, 0.0, 0.0), //
|
transaction(0.17, "Accrual", "20 January 2025", 0.0, 0.0, 0.17, 0.0, 0.0, 0.0, 0.0), //
|
||||||
transaction(0.16, "Accrual", "21 January 2025", 0.0, 0.0, 0.16, 0.0, 0.0, 0.0, 0.0), //
|
transaction(0.16, "Accrual", "21 January 2025", 0.0, 0.0, 0.16, 0.0, 0.0, 0.0, 0.0), //
|
||||||
transaction(0.16, "Accrual", "20 February 2025", 0.0, 0.0, 0.16, 0.0, 0.0, 0.0, 0.0), //
|
transaction(0.16, "Accrual", "20 February 2025", 0.0, 0.0, 0.16, 0.0, 0.0, 0.0, 0.0), //
|
||||||
transaction(0.18, "Accrual", "21 February 2025", 0.0, 0.0, 0.18, 0.0, 0.0, 0.0, 0.0), //
|
transaction(0.18, "Accrual", "21 February 2025", 0.0, 0.0, 0.18, 0.0, 0.0, 0.0, 0.0), //
|
||||||
@@ -623,7 +622,6 @@ public class LoanCOBCreateAccrualsTest extends BaseLoanIntegrationTest {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore // TODO: enable when implementation is complete
|
|
||||||
@Test
|
@Test
|
||||||
public void testProgressiveChargeBackInterestRecalculation() {
|
public void testProgressiveChargeBackInterestRecalculation() {
|
||||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||||
|
|||||||
Reference in New Issue
Block a user