From c89e7c3f461ba0841a3987f411d9b0c9c1ffc05d Mon Sep 17 00:00:00 2001 From: Adam Saghy Date: Wed, 4 Oct 2023 17:38:52 -0400 Subject: [PATCH] FINERACT-1971: Adding charge on a closed loan after maturity date should create an N+1 installment --- .../LoanChargeWritePlatformServiceImpl.java | 8 +- ...pecificDueDateChargeAfterMaturityTest.java | 226 ++++++++++++++++-- 2 files changed, 203 insertions(+), 31 deletions(-) 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 83b4c0508..27c292d52 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 @@ -985,13 +985,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo } } - LocalDate loanMaturityDate = loan.getExpectedMaturityDate(); - - if (loan.getMaturityDate() != null) { - loanMaturityDate = loan.getMaturityDate(); - } - - if (!loan.isInterestBearing() && loanCharge.isSpecifiedDueDate() && loanCharge.getDueDate().isAfter(loanMaturityDate)) { + if (!loan.isInterestBearing() && loanCharge.isSpecifiedDueDate()) { LoanRepaymentScheduleInstallment latestRepaymentScheduleInstalment = loan.getRepaymentScheduleInstallments() .get(loan.getLoanRepaymentScheduleInstallmentsSize() - 1); if (loanCharge.getDueDate().isAfter(latestRepaymentScheduleInstalment.getDueDate())) { diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanSpecificDueDateChargeAfterMaturityTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanSpecificDueDateChargeAfterMaturityTest.java index 6945f57d1..7efe0be71 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanSpecificDueDateChargeAfterMaturityTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanSpecificDueDateChargeAfterMaturityTest.java @@ -19,6 +19,7 @@ package org.apache.fineract.integrationtests; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import io.restassured.builder.RequestSpecBuilder; import io.restassured.builder.ResponseSpecBuilder; @@ -26,13 +27,31 @@ import io.restassured.http.ContentType; import io.restassured.path.json.JsonPath; import io.restassured.specification.RequestSpecification; import io.restassured.specification.ResponseSpecification; +import java.math.BigDecimal; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.util.ArrayList; +import java.util.Calendar; import java.util.HashMap; import java.util.List; +import org.apache.fineract.client.models.BusinessDateRequest; +import org.apache.fineract.client.models.GetLoansLoanIdResponse; +import org.apache.fineract.client.models.PostChargesRequest; +import org.apache.fineract.client.models.PostChargesResponse; +import org.apache.fineract.client.models.PostClientsResponse; +import org.apache.fineract.client.models.PostLoansLoanIdChargesRequest; +import org.apache.fineract.client.models.PostLoansLoanIdChargesResponse; +import org.apache.fineract.client.models.PostLoansLoanIdRequest; +import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest; +import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse; +import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest; +import org.apache.fineract.client.models.PostLoansRequest; +import org.apache.fineract.client.models.PostLoansResponse; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; +import org.apache.fineract.integrationtests.common.BusinessDateHelper; import org.apache.fineract.integrationtests.common.ClientHelper; +import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper; import org.apache.fineract.integrationtests.common.Utils; import org.apache.fineract.integrationtests.common.accounting.Account; import org.apache.fineract.integrationtests.common.accounting.AccountHelper; @@ -44,7 +63,10 @@ import org.apache.fineract.integrationtests.common.loans.LoanStatusChecker; import org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension; import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper; import org.apache.fineract.integrationtests.common.savings.SavingsProductHelper; -import org.junit.jupiter.api.BeforeEach; +import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType; +import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode; +import org.apache.fineract.portfolio.charge.domain.ChargeTimeType; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.slf4j.Logger; @@ -54,9 +76,10 @@ import org.slf4j.LoggerFactory; @ExtendWith(LoanTestLifecycleExtension.class) public class LoanSpecificDueDateChargeAfterMaturityTest { - public static final String ACCOUNT_TYPE_INDIVIDUAL = "INDIVIDUAL"; - public static final String MINIMUM_OPENING_BALANCE = "1000.0"; - public static final String DEPOSIT_AMOUNT = "7000"; + private static final String DATETIME_PATTERN = "dd MMMM yyyy"; + private static final String ACCOUNT_TYPE_INDIVIDUAL = "INDIVIDUAL"; + private static final String MINIMUM_OPENING_BALANCE = "1000.0"; + private static final String DEPOSIT_AMOUNT = "7000"; private static final Logger LOG = LoggerFactory.getLogger(LoanSpecificDueDateChargeAfterMaturityTest.class); private static final String DATE_OF_JOINING = "01 January 2011"; private static final Float LP_PRINCIPAL = 10000.0f; @@ -66,12 +89,16 @@ public class LoanSpecificDueDateChargeAfterMaturityTest { private static final String LOAN_APPLICATION_SUBMISSION_DATE = "03 March 2011"; private static final String LOAN_TERM_FREQUENCY = "1"; private static final String INDIVIDUAL_LOAN = "individual"; + private static final BusinessDateHelper BUSINESS_DATE_HELPER = new BusinessDateHelper(); + private static final DateTimeFormatter DATE_FORMATTER = new DateTimeFormatterBuilder().appendPattern(DATETIME_PATTERN).toFormatter(); + private static final ChargesHelper CHARGES_HELPER = new ChargesHelper(); private static RequestSpecification requestSpec; private static ResponseSpecification responseSpec; - private LoanTransactionHelper loanTransactionHelper; - private AccountHelper accountHelper; - private PeriodicAccrualAccountingHelper periodicAccrualAccountingHelper; - private DateTimeFormatter dateFormatter = new DateTimeFormatterBuilder().appendPattern("dd MMMM yyyy").toFormatter(); + private static LoanTransactionHelper loanTransactionHelper; + private static AccountHelper accountHelper; + private static PeriodicAccrualAccountingHelper periodicAccrualAccountingHelper; + private static Integer commonLoanProductId; + private static PostClientsResponse client; public static Integer createSavingsProduct(final String minOpenningBalance, final Account... accounts) { LOG.info("------------------------------CREATING NEW SAVINGS PRODUCT ---------------------------------------"); @@ -82,17 +109,24 @@ public class LoanSpecificDueDateChargeAfterMaturityTest { return SavingsProductHelper.createSavingsProduct(savingsProductJSON, requestSpec, responseSpec); } - @BeforeEach - public void setup() { + @BeforeAll + public static void setupCommon() { Utils.initializeRESTAssured(); + ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec); requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); requestSpec.header("Fineract-Platform-TenantId", "default"); responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); - this.loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec); - this.accountHelper = new AccountHelper(requestSpec, responseSpec); - this.periodicAccrualAccountingHelper = new PeriodicAccrualAccountingHelper(requestSpec, responseSpec); + loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec); + accountHelper = new AccountHelper(requestSpec, responseSpec); + periodicAccrualAccountingHelper = new PeriodicAccrualAccountingHelper(requestSpec, responseSpec); + final Account assetAccount = accountHelper.createAssetAccount(); + final Account incomeAccount = accountHelper.createIncomeAccount(); + final Account expenseAccount = accountHelper.createExpenseAccount(); + final Account overpaymentAccount = accountHelper.createLiabilityAccount(); + commonLoanProductId = createLoanProduct("500", "15", "4", assetAccount, incomeAccount, expenseAccount, overpaymentAccount); + client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()); } @Test @@ -132,7 +166,7 @@ public class LoanSpecificDueDateChargeAfterMaturityTest { LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap); LocalDate targetDate = LocalDate.of(2011, 3, 4); - final String loanDisbursementDate = dateFormatter.format(targetDate); + final String loanDisbursementDate = DATE_FORMATTER.format(targetDate); String loanDetails = this.loanTransactionHelper.getLoanDetails(requestSpec, responseSpec, loanID); loanStatusHashMap = this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount(loanDisbursementDate, loanID, @@ -148,7 +182,7 @@ public class LoanSpecificDueDateChargeAfterMaturityTest { assertEquals(10000.0f, loanSchedule.get(1).get("totalDueForPeriod")); assertEquals(10000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod")); targetDate = LocalDate.of(2011, 4, 5); - final String penaltyCharge1AddedDate = dateFormatter.format(targetDate); + final String penaltyCharge1AddedDate = DATE_FORMATTER.format(targetDate); Integer penalty1LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanID, LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(flatSpecifiedDueDate), penaltyCharge1AddedDate, String.valueOf(PENALTY_PORTION))); @@ -173,7 +207,7 @@ public class LoanSpecificDueDateChargeAfterMaturityTest { this.loanTransactionHelper.checkAccrualTransactionForRepayment(runOnDate, 0.0f, 0.0f, PENALTY_PORTION, loanID); targetDate = LocalDate.of(2011, 4, 6); - final String feeCharge1AddedDate = dateFormatter.format(targetDate); + final String feeCharge1AddedDate = DATE_FORMATTER.format(targetDate); Integer fee1LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanID, LoanTransactionHelper .getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(flat), feeCharge1AddedDate, String.valueOf(FEE_PORTION))); @@ -189,7 +223,7 @@ public class LoanSpecificDueDateChargeAfterMaturityTest { (int) ((List) loanSchedule.get(2).get("dueDate")).get(1), (int) ((List) loanSchedule.get(2).get("dueDate")).get(2))); targetDate = LocalDate.of(2011, 4, 7); - final String penaltyCharge2AddedDate = dateFormatter.format(targetDate); + final String penaltyCharge2AddedDate = DATE_FORMATTER.format(targetDate); Integer penalty2LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanID, LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(flatSpecifiedDueDateNext), penaltyCharge2AddedDate, String.valueOf(NEXT_PENALTY_PORTION))); @@ -206,7 +240,7 @@ public class LoanSpecificDueDateChargeAfterMaturityTest { (int) ((List) loanSchedule.get(2).get("dueDate")).get(1), (int) ((List) loanSchedule.get(2).get("dueDate")).get(2))); targetDate = LocalDate.of(2011, 4, 8); - final String feeCharge2AddedDate = dateFormatter.format(targetDate); + final String feeCharge2AddedDate = DATE_FORMATTER.format(targetDate); Integer fee2LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanID, LoanTransactionHelper .getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(flatNext), feeCharge2AddedDate, String.valueOf(NEXT_FEE_PORTION))); @@ -265,7 +299,7 @@ public class LoanSpecificDueDateChargeAfterMaturityTest { LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap); LocalDate targetDate = LocalDate.of(2011, 3, 4); - final String loanDisbursementDate = dateFormatter.format(targetDate); + final String loanDisbursementDate = DATE_FORMATTER.format(targetDate); String loanDetails = this.loanTransactionHelper.getLoanDetails(requestSpec, responseSpec, loanID); loanStatusHashMap = this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount(loanDisbursementDate, loanID, @@ -282,14 +316,14 @@ public class LoanSpecificDueDateChargeAfterMaturityTest { assertEquals(10000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod")); targetDate = LocalDate.of(2011, 3, 10); - String repaymentDateStr = dateFormatter.format(targetDate); + String repaymentDateStr = DATE_FORMATTER.format(targetDate); loanTransactionHelper.makeRepayment(repaymentDateStr, 10000.0f, loanID); loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID); LoanStatusChecker.verifyLoanAccountIsClosed(loanStatusHashMap); targetDate = LocalDate.of(2011, 4, 13); - final String penaltyCharge1AddedDate = dateFormatter.format(targetDate); + final String penaltyCharge1AddedDate = DATE_FORMATTER.format(targetDate); Integer penalty1LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanID, LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(flatSpecifiedDueDate), penaltyCharge1AddedDate, String.valueOf(PENALTY_PORTION))); @@ -309,7 +343,7 @@ public class LoanSpecificDueDateChargeAfterMaturityTest { (int) ((List) loanSchedule.get(2).get("dueDate")).get(1), (int) ((List) loanSchedule.get(2).get("dueDate")).get(2))); targetDate = LocalDate.of(2011, 4, 14); - String runOnDateStr = dateFormatter.format(targetDate); + String runOnDateStr = DATE_FORMATTER.format(targetDate); this.periodicAccrualAccountingHelper.runPeriodicAccrualAccounting(runOnDateStr); // Transaction date will be the due date of the instalment (in case of N+1 scenario) @@ -335,7 +369,7 @@ public class LoanSpecificDueDateChargeAfterMaturityTest { (int) ((List) loanSchedule.get(2).get("dueDate")).get(1), (int) ((List) loanSchedule.get(2).get("dueDate")).get(2))); targetDate = LocalDate.of(2011, 4, 14); - String penaltyCharge2AddedDate = dateFormatter.format(targetDate); + String penaltyCharge2AddedDate = DATE_FORMATTER.format(targetDate); Integer penalty2LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanID, LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(flatSpecifiedDueDate), penaltyCharge2AddedDate, String.valueOf(PENALTY_PORTION))); @@ -357,7 +391,7 @@ public class LoanSpecificDueDateChargeAfterMaturityTest { LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap); targetDate = LocalDate.of(2011, 4, 15); - repaymentDateStr = dateFormatter.format(targetDate); + repaymentDateStr = DATE_FORMATTER.format(targetDate); loanTransactionHelper.makeRepayment(repaymentDateStr, PENALTY_PORTION, loanID); loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID); @@ -399,6 +433,85 @@ public class LoanSpecificDueDateChargeAfterMaturityTest { (int) ((List) loanSchedule.get(2).get("dueDate")).get(1), (int) ((List) loanSchedule.get(2).get("dueDate")).get(2))); } + @Test + public void addChargeAfterLoanMaturity() { + try { + GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE); + BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName()) + .date("01 September 2023").dateFormat(DATETIME_PATTERN).locale("en")); + + PostChargesResponse penaltyCharge = CHARGES_HELPER.createCharges(new PostChargesRequest().penalty(true).amount(10.0) + .chargeCalculationType(ChargeCalculationType.FLAT.getValue()) + .chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE.getValue()).chargePaymentMode(ChargePaymentMode.REGULAR.getValue()) + .currencyCode("USD").name(Utils.randomStringGenerator("PENALTY_" + Calendar.getInstance().getTimeInMillis(), 5)) + .chargeAppliesTo(1).locale("en").active(true)); + + final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId, 1000L, 30, 30, 1, 0, + "01 September 2023", "01 September 2023"); + + loanTransactionHelper.approveLoan(loanResponse.getLoanId(), + new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)).dateFormat(DATETIME_PATTERN) + .approvedOnDate("01 September 2023").locale("en")); + + loanTransactionHelper.disburseLoan(loanResponse.getLoanId(), + new PostLoansLoanIdRequest().actualDisbursementDate("01 September 2023").dateFormat(DATETIME_PATTERN) + .transactionAmount(BigDecimal.valueOf(1000)).locale("en")); + + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId()); + validateLoanSummaryBalances(loanDetails, 1000.0, 0.0, 1000.0, 0.0, null); + validateRepaymentPeriod(loanDetails, 1, 1000.0, 0.0, 1000.0, 0.0, 0.0); + validateLoanTransaction(loanDetails, 0, 1000.0, 0.0, 0.0, 1000.0); + assertTrue(loanDetails.getStatus().getActive()); + + BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName()) + .date("01 October 2023").dateFormat(DATETIME_PATTERN).locale("en")); + + PostLoansLoanIdTransactionsResponse repaymentTransaction = loanTransactionHelper.makeLoanRepayment(loanResponse.getLoanId(), + new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN).transactionDate("01 October 2023").locale("en") + .transactionAmount(1000.0)); + + loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId()); + validateLoanSummaryBalances(loanDetails, 0.0, 1000.0, 0.0, 1000.0, null); + validateRepaymentPeriod(loanDetails, 1, 1000.0, 1000.0, 0.0, 0.0, 0.0); + validateLoanTransaction(loanDetails, 1, 1000.0, 1000.0, 0.0, 0.0); + assertTrue(loanDetails.getStatus().getClosedObligationsMet()); + + BUSINESS_DATE_HELPER.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName()) + .date("04 October 2023").dateFormat(DATETIME_PATTERN).locale("en")); + + loanTransactionHelper.makeMerchantIssuedRefund(loanResponse.getLoanId(), new PostLoansLoanIdTransactionsRequest() + .dateFormat(DATETIME_PATTERN).transactionDate("04 October 2023").locale("en").transactionAmount(1000.0)); + + loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId()); + validateLoanSummaryBalances(loanDetails, 0.0, 1000.0, 0.0, 1000.0, 1000.0); + validateRepaymentPeriod(loanDetails, 1, 1000.0, 1000.0, 0.0, 0.0, 0.0); + validateLoanTransaction(loanDetails, 2, 1000.0, 0.0, 1000.0, 0.0); + assertTrue(loanDetails.getStatus().getOverpaid()); + + loanTransactionHelper.reverseLoanTransaction(loanResponse.getLoanId(), repaymentTransaction.getResourceId(), + new PostLoansLoanIdTransactionsTransactionIdRequest().dateFormat(DATETIME_PATTERN).transactionDate("04 October 2023") + .transactionAmount(0.0).locale("en")); + + loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId()); + validateLoanSummaryBalances(loanDetails, 0.0, 1000.0, 0.0, 1000.0, null); + validateRepaymentPeriod(loanDetails, 1, 1000.0, 1000.0, 0.0, 0.0, 1000.0); + validateLoanTransaction(loanDetails, 2, 1000.0, 1000.0, 0.0, 0.0); + assertTrue(loanDetails.getStatus().getClosedObligationsMet()); + + PostLoansLoanIdChargesResponse penaltyLoanChargeResult = loanTransactionHelper.addChargesForLoan(loanResponse.getLoanId(), + new PostLoansLoanIdChargesRequest().chargeId(penaltyCharge.getResourceId()).dateFormat(DATETIME_PATTERN).locale("en") + .amount(10.0).dueDate("04 October 2023")); + + loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId()); + validateLoanSummaryBalances(loanDetails, 10.0, 1000.0, 0.0, 1000.0, null); + validateRepaymentPeriod(loanDetails, 1, 1000.0, 1000.0, 0.0, 0.0, 1000.0); + validateRepaymentPeriod(loanDetails, 2, 0.0, 0.0, 0.0, 10.0, 0.0, 10.0, 0.0, 0.0); + assertTrue(loanDetails.getStatus().getActive()); + } finally { + GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE); + } + } + private Integer applyForLoanApplication(final Integer clientID, final Integer loanProductID, String interestRate) { LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------"); final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal(LP_PRINCIPAL.toString()) @@ -421,4 +534,69 @@ public class LoanSpecificDueDateChargeAfterMaturityTest { return this.loanTransactionHelper.getLoanProductId(loanProductJSON); } + private static Integer createLoanProduct(final String principal, final String repaymentAfterEvery, final String numberOfRepayments, + final Account... accounts) { + LOG.info("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------"); + final String loanProductJSON = new LoanProductTestBuilder().withMinPrincipal(principal).withPrincipal(principal) + .withRepaymentTypeAsDays().withRepaymentAfterEvery(repaymentAfterEvery).withNumberOfRepayments(numberOfRepayments) + .withinterestRatePerPeriod("0").withInterestRateFrequencyTypeAsMonths() + .withRepaymentStrategy( + LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY) + .withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsFlat().withAccountingRulePeriodicAccrual(accounts) + .withDaysInMonth("30").withDaysInYear("365").withMoratorium("0", "0").build(null); + return loanTransactionHelper.getLoanProductId(loanProductJSON); + } + + private static PostLoansResponse applyForLoanApplication(final Long clientId, final Integer loanProductId, final Long principal, + final int loanTermFrequency, final int repaymentAfterEvery, final int numberOfRepayments, final int interestRate, + final String expectedDisbursementDate, final String submittedOnDate) { + LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------"); + return loanTransactionHelper.applyLoan(new PostLoansRequest().clientId(clientId).productId(loanProductId.longValue()) + .expectedDisbursementDate(expectedDisbursementDate).dateFormat(DATETIME_PATTERN) + .transactionProcessingStrategyCode( + LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY) + .locale("en").submittedOnDate(submittedOnDate).amortizationType(1).interestRatePerPeriod(interestRate) + .interestCalculationPeriodType(1).interestType(0).repaymentFrequencyType(0).repaymentEvery(repaymentAfterEvery) + .repaymentFrequencyType(0).numberOfRepayments(numberOfRepayments).loanTermFrequency(loanTermFrequency) + .loanTermFrequencyType(0).principal(BigDecimal.valueOf(principal)).loanType("individual")); + } + + private static void validateLoanTransaction(GetLoansLoanIdResponse loanDetails, int index, double transactionAmount, + double principalPortion, double overPaidPortion, double loanBalance) { + assertEquals(transactionAmount, loanDetails.getTransactions().get(index).getAmount()); + assertEquals(principalPortion, loanDetails.getTransactions().get(index).getPrincipalPortion()); + assertEquals(overPaidPortion, loanDetails.getTransactions().get(index).getOverpaymentPortion()); + assertEquals(loanBalance, loanDetails.getTransactions().get(index).getOutstandingLoanBalance()); + } + + private static void validateRepaymentPeriod(GetLoansLoanIdResponse loanDetails, int index, double principalDue, double principalPaid, + double principalOutstanding, double paidInAdvance, double paidLate) { + assertEquals(principalDue, loanDetails.getRepaymentSchedule().getPeriods().get(index).getPrincipalDue()); + assertEquals(principalPaid, loanDetails.getRepaymentSchedule().getPeriods().get(index).getPrincipalPaid()); + assertEquals(principalOutstanding, loanDetails.getRepaymentSchedule().getPeriods().get(index).getPrincipalOutstanding()); + assertEquals(paidInAdvance, loanDetails.getRepaymentSchedule().getPeriods().get(index).getTotalPaidInAdvanceForPeriod()); + assertEquals(paidLate, loanDetails.getRepaymentSchedule().getPeriods().get(index).getTotalPaidLateForPeriod()); + } + + private static void validateRepaymentPeriod(GetLoansLoanIdResponse loanDetails, int index, double principalDue, double principalPaid, + double principalOutstanding, double penaltyDue, double penaltyPaid, double penaltyOutstanding, double paidInAdvance, + double paidLate) { + assertEquals(principalDue, loanDetails.getRepaymentSchedule().getPeriods().get(index).getPrincipalDue()); + assertEquals(principalPaid, loanDetails.getRepaymentSchedule().getPeriods().get(index).getPrincipalPaid()); + assertEquals(principalOutstanding, loanDetails.getRepaymentSchedule().getPeriods().get(index).getPrincipalOutstanding()); + assertEquals(penaltyDue, loanDetails.getRepaymentSchedule().getPeriods().get(index).getPenaltyChargesDue()); + assertEquals(penaltyPaid, loanDetails.getRepaymentSchedule().getPeriods().get(index).getPenaltyChargesPaid()); + assertEquals(penaltyOutstanding, loanDetails.getRepaymentSchedule().getPeriods().get(index).getPenaltyChargesOutstanding()); + assertEquals(paidInAdvance, loanDetails.getRepaymentSchedule().getPeriods().get(index).getTotalPaidInAdvanceForPeriod()); + assertEquals(paidLate, loanDetails.getRepaymentSchedule().getPeriods().get(index).getTotalPaidLateForPeriod()); + } + + private static void validateLoanSummaryBalances(GetLoansLoanIdResponse loanDetails, Double totalOutstanding, Double totalRepayment, + Double principalOutstanding, Double principalPaid, Double totalOverpaid) { + assertEquals(totalOutstanding, loanDetails.getSummary().getTotalOutstanding()); + assertEquals(totalRepayment, loanDetails.getSummary().getTotalRepayment()); + assertEquals(principalOutstanding, loanDetails.getSummary().getPrincipalOutstanding()); + assertEquals(principalPaid, loanDetails.getSummary().getPrincipalPaid()); + assertEquals(totalOverpaid, loanDetails.getTotalOverpaid()); + } }