FINERACT-1724: Create accrual entry for specific due date charge even on disbursement date
This commit is contained in:
+2
-1
@@ -375,7 +375,8 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (loanCharge.getDueDate().isAfter(startDate) && !loanCharge.getDueDate().isAfter(endDate)) {
|
||||
} else if (((accrualData.getInstallmentNumber() == 1 && loanCharge.getDueDate().isEqual(startDate))
|
||||
|| loanCharge.getDueDate().isAfter(startDate)) && !loanCharge.getDueDate().isAfter(endDate)) {
|
||||
chargeAmount = loanCharge.getAmount();
|
||||
if (loanCharge.getAmountUnrecognized() != null) {
|
||||
chargeAmount = chargeAmount.subtract(loanCharge.getAmountUnrecognized());
|
||||
|
||||
+2
-1
@@ -1778,7 +1778,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
.append(" or (ls.penalty_charges_amount <> COALESCE(ls.accrual_penalty_charges_derived, 0))")
|
||||
.append(" or (ls.interest_amount <> COALESCE(ls.accrual_interest_derived, 0)))")
|
||||
.append(" and loan.loan_status_id=:active and mpl.accounting_type=:type and (loan.closedon_date <= :tillDate or loan.closedon_date is null)")
|
||||
.append(" and loan.is_npa=false and (ls.duedate <= :tillDate or (ls.duedate > :tillDate and ls.fromdate < :tillDate))) ");
|
||||
.append(" and loan.is_npa=false and (ls.duedate <= :tillDate or (ls.duedate > :tillDate and ls.fromdate < :tillDate)")
|
||||
.append(" or (ls.installment = 1 and ls.fromdate = :tillDate))) ");
|
||||
Map<String, Object> paramMap = new HashMap<>(5);
|
||||
if (organisationStartDate != null) {
|
||||
sqlBuilder.append(" and ls.duedate > :organisationStartDate ");
|
||||
|
||||
+42
-2
@@ -87,7 +87,8 @@ public class LoanChargeSpecificDueDateTest {
|
||||
|
||||
// Client and Loan account creation
|
||||
final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012");
|
||||
final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper, null);
|
||||
final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProductWithPeriodicAccrual(loanTransactionHelper,
|
||||
null);
|
||||
assertNotNull(getLoanProductsProductResponse);
|
||||
|
||||
// Older date to have more than one overdue installment
|
||||
@@ -120,6 +121,25 @@ public class LoanChargeSpecificDueDateTest {
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
validateLoanAccount(getLoansLoanIdResponse, Double.valueOf(principalAmount), Double.valueOf("10.00"), false);
|
||||
|
||||
// Run Accruals
|
||||
log.info("Running Periodic Accrual for date {}", transactionDate);
|
||||
periodicAccrualAccountingHelper.runPeriodicAccrualAccounting(operationDate);
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
|
||||
final Long transactionId = loanTransactionHelper.evaluateLastLoanTransactionData(getLoansLoanIdResponse,
|
||||
"loanTransactionType.accrual", operationDate, Double.valueOf("10.00"));
|
||||
assertNotNull(transactionId);
|
||||
log.info("transactionId {}", transactionId);
|
||||
|
||||
final GetJournalEntriesTransactionIdResponse journalEntriesResponse = journalEntryHelper.getJournalEntries("L" + transactionId);
|
||||
assertNotNull(journalEntriesResponse);
|
||||
final List<JournalEntryTransactionItem> journalEntries = journalEntriesResponse.getPageItems();
|
||||
assertEquals(2, journalEntries.size());
|
||||
assertEquals(10, journalEntries.get(0).getAmount());
|
||||
assertEquals(10, journalEntries.get(1).getAmount());
|
||||
assertEquals(transactionDate, journalEntries.get(0).getTransactionDate());
|
||||
assertEquals(transactionDate, journalEntries.get(1).getTransactionDate());
|
||||
|
||||
// Make a full repayment to close the Loan
|
||||
Float amount = Float.valueOf("1010.00");
|
||||
PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = loanTransactionHelper.makeLoanRepayment(operationDate, amount,
|
||||
@@ -140,7 +160,8 @@ public class LoanChargeSpecificDueDateTest {
|
||||
|
||||
// Client and Loan account creation
|
||||
final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012");
|
||||
final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper, null);
|
||||
final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProductWithPeriodicAccrual(loanTransactionHelper,
|
||||
null);
|
||||
assertNotNull(getLoanProductsProductResponse);
|
||||
|
||||
// Older date to have more than one overdue installment
|
||||
@@ -174,6 +195,25 @@ public class LoanChargeSpecificDueDateTest {
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
validateLoanAccount(getLoansLoanIdResponse, Double.valueOf(principalAmount), Double.valueOf("10.00"), true);
|
||||
|
||||
// Run Accruals
|
||||
log.info("Running Periodic Accrual for date {}", transactionDate);
|
||||
periodicAccrualAccountingHelper.runPeriodicAccrualAccounting(operationDate);
|
||||
getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
|
||||
|
||||
final Long transactionId = loanTransactionHelper.evaluateLastLoanTransactionData(getLoansLoanIdResponse,
|
||||
"loanTransactionType.accrual", operationDate, Double.valueOf("10.00"));
|
||||
assertNotNull(transactionId);
|
||||
log.info("transactionId {}", transactionId);
|
||||
|
||||
final GetJournalEntriesTransactionIdResponse journalEntriesResponse = journalEntryHelper.getJournalEntries("L" + transactionId);
|
||||
assertNotNull(journalEntriesResponse);
|
||||
final List<JournalEntryTransactionItem> journalEntries = journalEntriesResponse.getPageItems();
|
||||
assertEquals(2, journalEntries.size());
|
||||
assertEquals(10, journalEntries.get(0).getAmount());
|
||||
assertEquals(10, journalEntries.get(1).getAmount());
|
||||
assertEquals(transactionDate, journalEntries.get(0).getTransactionDate());
|
||||
assertEquals(transactionDate, journalEntries.get(1).getTransactionDate());
|
||||
|
||||
// Make a full repayment to close the Loan
|
||||
Float amount = Float.valueOf("1010.00");
|
||||
PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = loanTransactionHelper.makeLoanRepayment(operationDate, amount,
|
||||
|
||||
+95
-6
@@ -19,6 +19,8 @@
|
||||
package org.apache.fineract.integrationtests;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import com.google.common.truth.Truth;
|
||||
import io.restassured.builder.RequestSpecBuilder;
|
||||
@@ -48,7 +50,9 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import org.apache.fineract.client.models.BusinessDateRequest;
|
||||
import org.apache.fineract.client.models.GetJournalEntriesTransactionIdResponse;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
||||
import org.apache.fineract.client.models.JournalEntryTransactionItem;
|
||||
import org.apache.fineract.client.models.PostClientsResponse;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
|
||||
import org.apache.fineract.client.models.PutJobsJobIDRequest;
|
||||
@@ -661,11 +665,15 @@ public class SchedulerJobsTestResults {
|
||||
ChargesHelper.getLoanOverdueFeeJSONWithCalculationTypePercentage("1"));
|
||||
Assertions.assertNotNull(overdueFeeChargeId);
|
||||
|
||||
Integer fee = ChargesHelper.createCharges(requestSpec, responseSpec,
|
||||
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false));
|
||||
Assertions.assertNotNull(fee);
|
||||
|
||||
final Integer loanProductID = createLoanProduct(overdueFeeChargeId.toString());
|
||||
Assertions.assertNotNull(loanProductID);
|
||||
List<Integer> loanIDs = new ArrayList<>();
|
||||
HashMap loanStatusHashMap;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
final Integer loanID = applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "10 January 2020");
|
||||
|
||||
Assertions.assertNotNull(loanID);
|
||||
@@ -682,6 +690,8 @@ public class SchedulerJobsTestResults {
|
||||
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||
loanIDs.add(loanID);
|
||||
|
||||
this.loanTransactionHelper.addChargesForLoan(loanID,
|
||||
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(fee), "02 March 2020", "10", null));
|
||||
}
|
||||
|
||||
String jobName = "Loan COB";
|
||||
@@ -690,18 +700,82 @@ public class SchedulerJobsTestResults {
|
||||
List<HashMap> repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec,
|
||||
loanId);
|
||||
|
||||
Assertions.assertEquals(10.00f, (Float) repaymentScheduleDataAfter.get(1).get("feeChargesDue"),
|
||||
"Verifying From Fee Charges due for first Repayment after Successful completion of Scheduler Job");
|
||||
Assertions.assertEquals(39.39f, (Float) repaymentScheduleDataAfter.get(1).get("penaltyChargesDue"),
|
||||
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
|
||||
"Verifying From Penalty Charges due for first Repayment after Successful completion of Scheduler Job");
|
||||
Assertions.assertEquals(39.39f, (Float) repaymentScheduleDataAfter.get(2).get("penaltyChargesDue"),
|
||||
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
|
||||
"Verifying From Penalty Charges due for first Repayment after Successful completion of Scheduler Job");
|
||||
Assertions.assertEquals(39.39f, (Float) repaymentScheduleDataAfter.get(3).get("penaltyChargesDue"),
|
||||
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
|
||||
"Verifying From Penalty Charges due for first Repayment after Successful completion of Scheduler Job");
|
||||
Assertions.assertEquals(39.39f, (Float) repaymentScheduleDataAfter.get(4).get("penaltyChargesDue"),
|
||||
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
|
||||
"Verifying From Penalty Charges due for first Repayment after Successful completion of Scheduler Job");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoanCOBJobOutcomeWhileAddingFeeOnDisbursementDate() {
|
||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
|
||||
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.COB_DATE, LocalDate.of(2020, 3, 2));
|
||||
this.savingsAccountHelper = new SavingsAccountHelper(requestSpec, responseSpec);
|
||||
this.loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec);
|
||||
|
||||
final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec);
|
||||
Assertions.assertNotNull(clientID);
|
||||
|
||||
Integer fee = ChargesHelper.createCharges(requestSpec, responseSpec,
|
||||
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false));
|
||||
Assertions.assertNotNull(fee);
|
||||
|
||||
final Integer loanProductID = createLoanProductWithPeriodicAccrual(null);
|
||||
Assertions.assertNotNull(loanProductID);
|
||||
|
||||
final Integer loanID = applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "10 January 2020");
|
||||
|
||||
Assertions.assertNotNull(loanID);
|
||||
|
||||
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
|
||||
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
|
||||
|
||||
loanStatusHashMap = this.loanTransactionHelper.approveLoan("01 March 2020", loanID);
|
||||
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||
|
||||
String loanDetails = this.loanTransactionHelper.getLoanDetails(requestSpec, responseSpec, loanID);
|
||||
loanStatusHashMap = this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount("02 March 2020", loanID,
|
||||
JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
|
||||
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||
|
||||
this.loanTransactionHelper.addChargesForLoan(loanID,
|
||||
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(fee), "02 March 2020", "10", null));
|
||||
|
||||
String jobName = "Loan COB";
|
||||
this.schedulerJobHelper.executeAndAwaitJob(jobName);
|
||||
|
||||
List<HashMap> repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID);
|
||||
|
||||
Assertions.assertEquals(10.00f, (Float) repaymentScheduleDataAfter.get(1).get("feeChargesDue"),
|
||||
"Verifying From Fee Charges due for first Repayment after Successful completion of Scheduler Job");
|
||||
|
||||
GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanID);
|
||||
// First accrual transaction
|
||||
assertTrue(getLoansLoanIdResponse.getTransactions().get(1).getType().getAccrual());
|
||||
assertEquals(10.0f, getLoansLoanIdResponse.getTransactions().get(1).getFeeChargesPortion());
|
||||
assertEquals(LocalDate.of(2020, 3, 2), getLoansLoanIdResponse.getTransactions().get(1).getDate());
|
||||
Long transactionId = getLoansLoanIdResponse.getTransactions().get(1).getId();
|
||||
|
||||
final GetJournalEntriesTransactionIdResponse journalEntriesResponse = journalEntryHelper.getJournalEntries("L" + transactionId);
|
||||
assertNotNull(journalEntriesResponse);
|
||||
final List<JournalEntryTransactionItem> journalEntries = journalEntriesResponse.getPageItems();
|
||||
assertEquals(2, journalEntries.size());
|
||||
assertEquals(10, journalEntries.get(0).getAmount());
|
||||
assertEquals(10, journalEntries.get(1).getAmount());
|
||||
assertEquals(LocalDate.of(2020, 3, 2), journalEntries.get(1).getTransactionDate());
|
||||
assertEquals(LocalDate.of(2020, 3, 2), journalEntries.get(0).getTransactionDate());
|
||||
|
||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoanCOBRunsOnlyOnLoansOneDayBehind() {
|
||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
|
||||
@@ -934,7 +1008,7 @@ public class SchedulerJobsTestResults {
|
||||
String JobName = "Update Non Performing Assets";
|
||||
this.schedulerJobHelper.executeAndAwaitJob(JobName);
|
||||
final Boolean isNPAAfter = (Boolean) this.loanTransactionHelper.getLoanDetail(requestSpec, responseSpec, loanID, "isNPA");
|
||||
Assertions.assertTrue(isNPAAfter);
|
||||
assertTrue(isNPAAfter);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1090,6 +1164,21 @@ public class SchedulerJobsTestResults {
|
||||
return this.loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||
}
|
||||
|
||||
private Integer createLoanProductWithPeriodicAccrual(final String chargeId) {
|
||||
final Account assetAccount = this.accountHelper.createAssetAccount();
|
||||
final Account assetFeeAndPenaltyAccount = this.accountHelper.createAssetAccount();
|
||||
final Account incomeAccount = this.accountHelper.createIncomeAccount();
|
||||
final Account expenseAccount = this.accountHelper.createExpenseAccount();
|
||||
final Account overpaymentAccount = this.accountHelper.createLiabilityAccount();
|
||||
|
||||
final String loanProductJSON = new LoanProductTestBuilder().withPrincipal("15,000.00").withNumberOfRepayments("4")
|
||||
.withRepaymentAfterEvery("1").withRepaymentTypeAsMonth().withinterestRatePerPeriod("1")
|
||||
.withAccountingRulePeriodicAccrual(new Account[] { assetAccount, incomeAccount, expenseAccount, overpaymentAccount })
|
||||
.withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualInstallments().withInterestTypeAsDecliningBalance()
|
||||
.withFeeAndPenaltyAssetAccount(assetFeeAndPenaltyAccount).build(chargeId);
|
||||
return this.loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||
}
|
||||
|
||||
private void addCollaterals(List<HashMap> collaterals, Integer collateralId, BigDecimal quantity) {
|
||||
collaterals.add(collaterals(collateralId, quantity));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user