FINERACT-1905-Charges-Accrual-entries-on-created-date

This commit is contained in:
Ruchi Dhamankar
2023-05-16 11:53:32 +02:00
committed by Arnold Gálovics
parent 216b39ce0c
commit aaa94007ee
8 changed files with 859 additions and 6 deletions
@@ -136,4 +136,7 @@ public interface ConfigurationDomainService {
Long retrieveExternalEventBatchSize();
String retrieveReportExportS3FolderName();
String getAccrualDateConfigForCharge();
}
@@ -51,6 +51,7 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
private static final String REPORT_EXPORT_S3_FOLDER_NAME = "report-export-s3-folder-name";
public static final String CHARGE_ACCRUAL_DATE_CRITERIA = "charge-accrual-date";
private final PermissionRepository permissionRepository;
private final GlobalConfigurationRepositoryWrapper globalConfigurationRepository;
private final PlatformCacheRepository cacheTypeRepository;
@@ -501,4 +502,16 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
final GlobalConfigurationPropertyData property = getGlobalConfigurationPropertyData(REPORT_EXPORT_S3_FOLDER_NAME);
return property.getStringValue();
}
@Override
public String getAccrualDateConfigForCharge() {
String defaultValue = "due-date";
final GlobalConfigurationPropertyData property = getGlobalConfigurationPropertyData(CHARGE_ACCRUAL_DATE_CRITERIA);
String value = property.getStringValue();
if (StringUtils.isBlank(value)) {
return defaultValue;
}
return value;
}
}
@@ -31,6 +31,7 @@ import java.util.Map;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanAccrualTransactionCreatedBusinessEvent;
@@ -61,6 +62,8 @@ import org.springframework.transaction.annotation.Transactional;
@RequiredArgsConstructor
public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlatformService {
private static final String ACCRUAL_ON_CHARGE_DUE_DATE = "due-date";
private static final String ACCRUAL_ON_CHARGE_SUBMITTED_ON_DATE = "submitted-date";
private final LoanReadPlatformService loanReadPlatformService;
private final LoanChargeReadPlatformService loanChargeReadPlatformService;
private final JdbcTemplate jdbcTemplate;
@@ -71,6 +74,7 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
private final BusinessEventNotifierService businessEventNotifierService;
private final LoanTransactionRepository loanTransactionRepository;
private final LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService;
private final ConfigurationDomainService configurationDomainService;
@Override
@Transactional
@@ -244,8 +248,14 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
}
}
if (amount.compareTo(BigDecimal.ZERO) > 0) {
addAccrualAccounting(scheduleAccrualData, amount, interestPortion, totalAccInterest, feePortion, totalAccFee, penaltyPortion,
totalAccPenalty, scheduleAccrualData.getDueDateAsLocaldate());
final String chargeAccrualDateCriteria = configurationDomainService.getAccrualDateConfigForCharge();
if (chargeAccrualDateCriteria.equalsIgnoreCase(ACCRUAL_ON_CHARGE_DUE_DATE)) {
addAccrualAccounting(scheduleAccrualData, amount, interestPortion, totalAccInterest, feePortion, totalAccFee,
penaltyPortion, totalAccPenalty, scheduleAccrualData.getDueDateAsLocaldate());
} else if (chargeAccrualDateCriteria.equalsIgnoreCase(ACCRUAL_ON_CHARGE_SUBMITTED_ON_DATE)) {
addAccrualAccounting(scheduleAccrualData, amount, interestPortion, totalAccInterest, feePortion, totalAccFee,
penaltyPortion, totalAccPenalty, DateUtils.getBusinessLocalDate());
}
}
}
@@ -356,7 +366,62 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
private void updateCharges(final Collection<LoanChargeData> chargesData, final LoanScheduleAccrualData accrualData,
final LocalDate startDate, final LocalDate endDate) {
final String chargeAccrualDateCriteria = configurationDomainService.getAccrualDateConfigForCharge();
if (chargeAccrualDateCriteria.equalsIgnoreCase(ACCRUAL_ON_CHARGE_DUE_DATE)) {
updateChargeForDueDate(chargesData, accrualData, startDate, endDate);
} else if (chargeAccrualDateCriteria.equalsIgnoreCase(ACCRUAL_ON_CHARGE_SUBMITTED_ON_DATE)) {
updateChargeForSubmittedOnDate(chargesData, accrualData, startDate, endDate);
}
}
private void updateChargeForSubmittedOnDate(Collection<LoanChargeData> chargesData, LoanScheduleAccrualData accrualData,
LocalDate startDate, LocalDate endDate) {
final Map<LoanChargeData, BigDecimal> applicableCharges = new HashMap<>();
BigDecimal submittedDateFeeIncome = BigDecimal.ZERO;
BigDecimal submittedDatePenaltyIncome = BigDecimal.ZERO;
LocalDate scheduleEndDate = accrualData.getDueDateAsLocaldate();
for (LoanChargeData loanCharge : chargesData) {
BigDecimal chargeAmount = BigDecimal.ZERO;
if (((accrualData.getInstallmentNumber() == 1 && loanCharge.getSubmittedOnDate().isEqual(startDate))
|| loanCharge.getSubmittedOnDate().isBefore(startDate) || loanCharge.getSubmittedOnDate().isEqual(startDate)
|| loanCharge.getSubmittedOnDate().isAfter(startDate)) && !loanCharge.getSubmittedOnDate().isAfter(endDate)
&& !loanCharge.getDueDate().isBefore(startDate) && !loanCharge.getDueDate().isAfter(scheduleEndDate)) {
chargeAmount = loanCharge.getAmount();
if (loanCharge.getAmountUnrecognized() != null) {
chargeAmount = chargeAmount.subtract(loanCharge.getAmountUnrecognized());
}
boolean canAddCharge = chargeAmount.compareTo(BigDecimal.ZERO) > 0;
if (canAddCharge && (loanCharge.getAmountAccrued() == null || chargeAmount.compareTo(loanCharge.getAmountAccrued()) != 0)) {
BigDecimal amountForAccrual = chargeAmount;
if (loanCharge.getAmountAccrued() != null) {
amountForAccrual = chargeAmount.subtract(loanCharge.getAmountAccrued());
}
applicableCharges.put(loanCharge, amountForAccrual);
}
}
if (loanCharge.isPenalty()) {
submittedDatePenaltyIncome = submittedDatePenaltyIncome.add(chargeAmount);
} else {
submittedDateFeeIncome = submittedDateFeeIncome.add(chargeAmount);
}
}
if (submittedDateFeeIncome.compareTo(BigDecimal.ZERO) == 0) {
submittedDateFeeIncome = null;
}
if (submittedDatePenaltyIncome.compareTo(BigDecimal.ZERO) == 0) {
submittedDatePenaltyIncome = null;
}
accrualData.updateChargeDetails(applicableCharges, submittedDateFeeIncome, submittedDatePenaltyIncome);
}
private void updateChargeForDueDate(Collection<LoanChargeData> chargesData, LoanScheduleAccrualData accrualData, LocalDate startDate,
LocalDate endDate) {
final Map<LoanChargeData, BigDecimal> applicableCharges = new HashMap<>();
BigDecimal dueDateFeeIncome = BigDecimal.ZERO;
BigDecimal dueDatePenaltyIncome = BigDecimal.ZERO;
@@ -146,6 +146,7 @@ import org.springframework.util.CollectionUtils;
@Transactional(readOnly = true)
public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
private static final String ACCRUAL_ON_CHARGE_SUBMITTED_ON_DATE = "submitted-date";
private final JdbcTemplate jdbcTemplate;
private final PlatformSecurityContext context;
private final LoanRepositoryWrapper loanRepositoryWrapper;
@@ -1739,9 +1740,17 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
@Override
public Collection<LoanScheduleAccrualData> retriveScheduleAccrualData() {
final String chargeAccrualDateCriteria = configurationDomainService.getAccrualDateConfigForCharge();
if (chargeAccrualDateCriteria.equalsIgnoreCase(ACCRUAL_ON_CHARGE_SUBMITTED_ON_DATE)) {
return retrieveScheduleAccrualDataForChargeSubmittedDateProcessing();
}
return retrieveScheduleAccrualDataForDefaultProcessing();
}
LoanScheduleAccrualMapper mapper = new LoanScheduleAccrualMapper();
private Collection<LoanScheduleAccrualData> retrieveScheduleAccrualDataForDefaultProcessing() {
LocalDate organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate();
LoanScheduleAccrualMapper mapper = new LoanScheduleAccrualMapper();
Map<String, Object> paramMap = new HashMap<>(3);
final StringBuilder sqlBuilder = new StringBuilder(400);
sqlBuilder.append("select ").append(mapper.schema()).append(
" where (recaldet.is_compounding_to_be_posted_as_transaction is null or recaldet.is_compounding_to_be_posted_as_transaction = false) ")
@@ -1749,16 +1758,40 @@ 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.is_npa=false and ls.duedate <= :currentDate) ");
if (organisationStartDate != null) {
sqlBuilder.append(" and ls.duedate > :organisationStartDate ");
}
sqlBuilder.append(" order by loan.id,ls.duedate ");
Map<String, Object> paramMap = new HashMap<>(3);
paramMap.put("active", LoanStatus.ACTIVE.getValue());
paramMap.put("type", AccountingRuleType.ACCRUAL_PERIODIC.getValue());
paramMap.put("organisationStartDate", (organisationStartDate == null) ? DateUtils.getBusinessLocalDate() : organisationStartDate);
paramMap.put("currentDate", DateUtils.getBusinessLocalDate());
return this.namedParameterJdbcTemplate.query(sqlBuilder.toString(), paramMap, mapper);
}
private Collection<LoanScheduleAccrualData> retrieveScheduleAccrualDataForChargeSubmittedDateProcessing() {
LocalDate organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate();
LoanScheduleAccrualMapper mapper = new LoanScheduleAccrualMapper();
Map<String, Object> paramMap = new HashMap<>(3);
final StringBuilder sqlBuilder = new StringBuilder(400);
sqlBuilder.append("select ").append(mapper.schema()).append(
" where (recaldet.is_compounding_to_be_posted_as_transaction is null or recaldet.is_compounding_to_be_posted_as_transaction = false) ")
.append(" and (((ls.fee_charges_amount <> COALESCE(ls.accrual_fee_charges_derived, 0))")
.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.is_npa=false) ");
if (organisationStartDate != null) {
sqlBuilder.append(" and ls.duedate > :organisationStartDate ");
}
sqlBuilder.append(" order by loan.id,ls.duedate ");
paramMap.put("active", LoanStatus.ACTIVE.getValue());
paramMap.put("type", AccountingRuleType.ACCRUAL_PERIODIC.getValue());
paramMap.put("organisationStartDate", (organisationStartDate == null) ? DateUtils.getBusinessLocalDate() : organisationStartDate);
return this.namedParameterJdbcTemplate.query(sqlBuilder.toString(), paramMap, mapper);
}
@@ -1769,6 +1802,14 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
@Override
public Collection<LoanScheduleAccrualData> retrievePeriodicAccrualData(final LocalDate tillDate, final Loan loan) {
final String chargeAccrualDateCriteria = configurationDomainService.getAccrualDateConfigForCharge();
if (chargeAccrualDateCriteria.equalsIgnoreCase(ACCRUAL_ON_CHARGE_SUBMITTED_ON_DATE)) {
return retrievePeriodicAccrualDataForChargeSubmittedDateProcessing(tillDate, loan);
}
return retrievePeriodicAccrualDataForDefaultProcessing(tillDate, loan);
}
private Collection<LoanScheduleAccrualData> retrievePeriodicAccrualDataForDefaultProcessing(final LocalDate tillDate, final Loan loan) {
LoanSchedulePeriodicAccrualMapper mapper = new LoanSchedulePeriodicAccrualMapper();
LocalDate organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate();
final StringBuilder sqlBuilder = new StringBuilder(400);
@@ -1793,7 +1834,34 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
paramMap.put("active", LoanStatus.ACTIVE.getValue());
paramMap.put("type", AccountingRuleType.ACCRUAL_PERIODIC.getValue());
paramMap.put("tillDate", tillDate);
return this.namedParameterJdbcTemplate.query(sqlBuilder.toString(), paramMap, mapper);
}
private Collection<LoanScheduleAccrualData> retrievePeriodicAccrualDataForChargeSubmittedDateProcessing(final LocalDate tillDate,
final Loan loan) {
LoanSchedulePeriodicAccrualMapper mapper = new LoanSchedulePeriodicAccrualMapper();
LocalDate organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate();
final StringBuilder sqlBuilder = new StringBuilder(400);
sqlBuilder.append("select ").append(mapper.schema()).append(
" where (recaldet.is_compounding_to_be_posted_as_transaction is null or recaldet.is_compounding_to_be_posted_as_transaction = false) ")
.append(" and (((ls.fee_charges_amount <> COALESCE(ls.accrual_fee_charges_derived, 0))")
.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)");
Map<String, Object> paramMap = new HashMap<>(5);
if (organisationStartDate != null) {
sqlBuilder.append(" and ls.duedate > :organisationStartDate ");
paramMap.put("organisationStartDate", organisationStartDate);
}
if (loan != null) {
sqlBuilder.append(" and loan.id= :loanId ");
paramMap.put("loanId", loan.getId());
}
sqlBuilder.append(" order by loan.id,ls.duedate ");
paramMap.put("active", LoanStatus.ACTIVE.getValue());
paramMap.put("type", AccountingRuleType.ACCRUAL_PERIODIC.getValue());
paramMap.put("tillDate", tillDate);
return this.namedParameterJdbcTemplate.query(sqlBuilder.toString(), paramMap, mapper);
}
@@ -126,4 +126,5 @@
<include file="parts/0104_loan_product_add_repayment_overdue_days_config.xml" relativeToChangelogFile="true" />
<include file="parts/0105_add_indices_loan_table.xml" relativeToChangelogFile="true" />
<include file="parts/0106_new_repayment_strategy.xml" relativeToChangelogFile="true" />
<include file="parts/0107_add_configuration_charges_accrual_date.xml" relativeToChangelogFile="true" />
</databaseChangeLog>
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
<changeSet author="fineract" id="1" context="postgresql">
<sql>
SELECT SETVAL('c_configuration_id_seq', COALESCE(MAX(id), 0)+1, false ) FROM c_configuration;
</sql>
</changeSet>
<changeSet author="fineract" id="2">
<insert tableName="c_configuration">
<column name="name" value="charge-accrual-date"/>
<column name="value"/>
<column name="date_value"/>
<column name="string_value" value="due-date"/>
<column name="enabled" valueBoolean="true"/>
<column name="is_trap_door" valueBoolean="false"/>
<column name="description" value="due-date: default for due-date, Use comma seperated values for due-date, submitted-date"/>
</insert>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,639 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
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 io.restassured.builder.RequestSpecBuilder;
import io.restassured.builder.ResponseSpecBuilder;
import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.UUID;
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
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.SchedulerJobHelper;
import org.apache.fineract.integrationtests.common.Utils;
import org.apache.fineract.integrationtests.common.accounting.Account;
import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
import org.apache.fineract.integrationtests.common.accounting.PeriodicAccrualAccountingHelper;
import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class LoanAccrualTransactionOnChargeSubmittedDateTest {
private ResponseSpecification responseSpec;
private RequestSpecification requestSpec;
private LoanTransactionHelper loanTransactionHelper;
private ClientHelper clientHelper;
private DateTimeFormatter dateFormatter = new DateTimeFormatterBuilder().appendPattern("dd MMMM yyyy").toFormatter();
private PeriodicAccrualAccountingHelper periodicAccrualAccountingHelper;
private AccountHelper accountHelper;
@BeforeEach
public void setup() {
Utils.initializeRESTAssured();
this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
this.clientHelper = new ClientHelper(this.requestSpec, this.responseSpec);
this.periodicAccrualAccountingHelper = new PeriodicAccrualAccountingHelper(this.requestSpec, this.responseSpec);
this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec);
}
@Test
public void loanAccrualTransactionOnChargeSubmittedTest_Accrual_Accounting_Api() {
try {
// Accounts oof periodic accrual
final Account assetAccount = this.accountHelper.createAssetAccount();
final Account incomeAccount = this.accountHelper.createIncomeAccount();
final Account expenseAccount = this.accountHelper.createExpenseAccount();
final Account overpaymentAccount = this.accountHelper.createLiabilityAccount();
// Set business date
LocalDate currentDate = LocalDate.of(2023, 03, 3);
final String accrualRunTillDate = dateFormatter.format(currentDate);
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, currentDate);
GlobalConfigurationHelper.updateChargeAccrualDateConfiguration(this.requestSpec, this.responseSpec, "submitted-date");
// Loan ExternalId
String loanExternalIdStr = UUID.randomUUID().toString();
// Client and Loan account creation
final Integer clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper, assetAccount,
incomeAccount, expenseAccount, overpaymentAccount);
assertNotNull(getLoanProductsProductResponse);
final Integer loanId = createLoanAccount(clientId, getLoanProductsProductResponse.getId(), loanExternalIdStr);
// Add Charge Penalty
Integer penalty = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", true));
LocalDate targetDate = LocalDate.of(2023, 3, 10);
final String penaltyCharge1AddedDate = dateFormatter.format(targetDate);
Integer penalty1LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty), penaltyCharge1AddedDate, "10"));
assertNotNull(penalty1LoanChargeId);
// Add Charge Fee
Integer feeCharge = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false));
targetDate = LocalDate.of(2023, 3, 14);
final String feeChargeAddedDate = dateFormatter.format(targetDate);
Integer feeLoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge), feeChargeAddedDate, "10"));
assertNotNull(feeLoanChargeId);
// Run accrual for charge created date
this.periodicAccrualAccountingHelper.runPeriodicAccrualAccounting(accrualRunTillDate);
// verify accrual transaction created for charges create date
checkAccrualTransaction(currentDate, 0.0f, 10.0f, 10.0f, loanId);
// Set business date
LocalDate futureDate = LocalDate.of(2023, 03, 4);
final String nextAccrualRunDate = dateFormatter.format(futureDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, futureDate);
// make repayment
final PostLoansLoanIdTransactionsResponse repaymentTransaction_1 = loanTransactionHelper.makeLoanRepayment(loanExternalIdStr,
new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("4 March 2023").locale("en")
.transactionAmount(100.0));
// Add Charge
Integer feeCharge_1 = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false));
targetDate = LocalDate.of(2023, 3, 21);
final String feeChargeAddedDate_1 = dateFormatter.format(targetDate);
Integer feeLoanChargeId_1 = this.loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge_1), feeChargeAddedDate_1, "10"));
assertNotNull(feeLoanChargeId_1);
// Run accrual for charge created date
this.periodicAccrualAccountingHelper.runPeriodicAccrualAccounting(nextAccrualRunDate);
// verify accrual transaction created for charges create date
checkAccrualTransaction(futureDate, 0.0f, 10.0f, 0.0f, loanId);
} finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
GlobalConfigurationHelper.updateChargeAccrualDateConfiguration(this.requestSpec, this.responseSpec, "due-date");
}
}
@Test
public void loanAccrualTransactionOnChargeSubmittedTest_Add_Periodic_Accrual_Transactions_Job() {
try {
final SchedulerJobHelper schedulerJobHelper = new SchedulerJobHelper(requestSpec);
// Accounts oof periodic accrual
final Account assetAccount = this.accountHelper.createAssetAccount();
final Account incomeAccount = this.accountHelper.createIncomeAccount();
final Account expenseAccount = this.accountHelper.createExpenseAccount();
final Account overpaymentAccount = this.accountHelper.createLiabilityAccount();
// Set business date
LocalDate currentDate = LocalDate.of(2023, 03, 3);
final String accrualRunTillDate = dateFormatter.format(currentDate);
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, currentDate);
GlobalConfigurationHelper.updateChargeAccrualDateConfiguration(this.requestSpec, this.responseSpec, "submitted-date");
// Loan ExternalId
String loanExternalIdStr = UUID.randomUUID().toString();
// Client and Loan account creation
final Integer clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper, assetAccount,
incomeAccount, expenseAccount, overpaymentAccount);
assertNotNull(getLoanProductsProductResponse);
final Integer loanId = createLoanAccount(clientId, getLoanProductsProductResponse.getId(), loanExternalIdStr);
// Add Charge Penalty
Integer penalty = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", true));
LocalDate targetDate = LocalDate.of(2023, 3, 10);
final String penaltyCharge1AddedDate = dateFormatter.format(targetDate);
Integer penalty1LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty), penaltyCharge1AddedDate, "10"));
assertNotNull(penalty1LoanChargeId);
// Add Charge Fee
Integer feeCharge = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false));
targetDate = LocalDate.of(2023, 3, 14);
final String feeChargeAddedDate = dateFormatter.format(targetDate);
Integer feeLoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge), feeChargeAddedDate, "10"));
assertNotNull(feeLoanChargeId);
// Run periodic accrual job for business date
final String jobName = "Add Periodic Accrual Transactions";
schedulerJobHelper.executeAndAwaitJob(jobName);
// verify accrual transaction created for charges create date
checkAccrualTransaction(currentDate, 0.0f, 10.0f, 10.0f, loanId);
// Set business date
LocalDate futureDate = LocalDate.of(2023, 03, 4);
final String nextAccrualRunDate = dateFormatter.format(futureDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, futureDate);
// make repayment
final PostLoansLoanIdTransactionsResponse repaymentTransaction_1 = loanTransactionHelper.makeLoanRepayment(loanExternalIdStr,
new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("4 March 2023").locale("en")
.transactionAmount(100.0));
// Add Charge
Integer feeCharge_1 = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false));
targetDate = LocalDate.of(2023, 3, 21);
final String feeChargeAddedDate_1 = dateFormatter.format(targetDate);
Integer feeLoanChargeId_1 = this.loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge_1), feeChargeAddedDate_1, "10"));
assertNotNull(feeLoanChargeId_1);
// Run periodic accrual job for business date
schedulerJobHelper.executeAndAwaitJob(jobName);
// verify accrual transaction created for charges create date
checkAccrualTransaction(futureDate, 0.0f, 10.0f, 0.0f, loanId);
} finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
GlobalConfigurationHelper.updateChargeAccrualDateConfiguration(this.requestSpec, this.responseSpec, "due-date");
}
}
@Test
public void loanAccrualTransactionOnChargeSubmittedTest_Loan_COB_AddPeriodicAccrualEntriesBusinessStep() {
try {
final SchedulerJobHelper schedulerJobHelper = new SchedulerJobHelper(requestSpec);
// Accounts oof periodic accrual
final Account assetAccount = this.accountHelper.createAssetAccount();
final Account incomeAccount = this.accountHelper.createIncomeAccount();
final Account expenseAccount = this.accountHelper.createExpenseAccount();
final Account overpaymentAccount = this.accountHelper.createLiabilityAccount();
// Set business date
LocalDate currentDate = LocalDate.of(2023, 03, 3);
final String accrualRunTillDate = dateFormatter.format(currentDate);
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, currentDate);
GlobalConfigurationHelper.updateChargeAccrualDateConfiguration(this.requestSpec, this.responseSpec, "submitted-date");
// Loan ExternalId
String loanExternalIdStr = UUID.randomUUID().toString();
// Client and Loan account creation
final Integer clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper, assetAccount,
incomeAccount, expenseAccount, overpaymentAccount);
assertNotNull(getLoanProductsProductResponse);
final Integer loanId = createLoanAccount(clientId, getLoanProductsProductResponse.getId(), loanExternalIdStr);
// Add Charge Penalty
Integer penalty = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", true));
LocalDate targetDate = LocalDate.of(2023, 3, 10);
final String penaltyCharge1AddedDate = dateFormatter.format(targetDate);
Integer penalty1LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty), penaltyCharge1AddedDate, "10"));
assertNotNull(penalty1LoanChargeId);
// Add Charge Fee
Integer feeCharge = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false));
targetDate = LocalDate.of(2023, 3, 14);
final String feeChargeAddedDate = dateFormatter.format(targetDate);
Integer feeLoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge), feeChargeAddedDate, "10"));
assertNotNull(feeLoanChargeId);
// Run cob job for business date + 1
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, currentDate.plusDays(1));
final String jobName = "Loan COB";
schedulerJobHelper.executeAndAwaitJob(jobName);
// verify accrual transaction created for charges create date
checkAccrualTransaction(currentDate, 0.0f, 10.0f, 10.0f, loanId);
// Set business date
LocalDate futureDate = LocalDate.of(2023, 03, 4);
final String nextAccrualRunDate = dateFormatter.format(futureDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, futureDate);
// make repayment
final PostLoansLoanIdTransactionsResponse repaymentTransaction_1 = loanTransactionHelper.makeLoanRepayment(loanExternalIdStr,
new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("4 March 2023").locale("en")
.transactionAmount(100.0));
// Add Charge
Integer feeCharge_1 = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false));
targetDate = LocalDate.of(2023, 3, 21);
final String feeChargeAddedDate_1 = dateFormatter.format(targetDate);
Integer feeLoanChargeId_1 = this.loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge_1), feeChargeAddedDate_1, "10"));
assertNotNull(feeLoanChargeId_1);
// Run cob job for business date + 1
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, futureDate.plusDays(1));
schedulerJobHelper.executeAndAwaitJob(jobName);
// verify accrual transaction created for charges create date
checkAccrualTransaction(futureDate, 0.0f, 10.0f, 0.0f, loanId);
} finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
GlobalConfigurationHelper.updateChargeAccrualDateConfiguration(this.requestSpec, this.responseSpec, "due-date");
}
}
@Test
public void loanAccrualTransactionOnChargeSubmittedTest_Add_Accrual_Transactions_Job() {
try {
final SchedulerJobHelper schedulerJobHelper = new SchedulerJobHelper(requestSpec);
// Accounts oof periodic accrual
final Account assetAccount = this.accountHelper.createAssetAccount();
final Account incomeAccount = this.accountHelper.createIncomeAccount();
final Account expenseAccount = this.accountHelper.createExpenseAccount();
final Account overpaymentAccount = this.accountHelper.createLiabilityAccount();
// Set business date
LocalDate currentDate = LocalDate.of(2023, 03, 3);
final String accrualRunTillDate = dateFormatter.format(currentDate);
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, currentDate);
GlobalConfigurationHelper.updateChargeAccrualDateConfiguration(this.requestSpec, this.responseSpec, "submitted-date");
// Loan ExternalId
String loanExternalIdStr = UUID.randomUUID().toString();
// Client and Loan account creation
final Integer clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper, assetAccount,
incomeAccount, expenseAccount, overpaymentAccount);
assertNotNull(getLoanProductsProductResponse);
final Integer loanId = createLoanAccount(clientId, getLoanProductsProductResponse.getId(), loanExternalIdStr);
// Add Charge Penalty
Integer penalty = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", true));
LocalDate targetDate = LocalDate.of(2023, 3, 10);
final String penaltyCharge1AddedDate = dateFormatter.format(targetDate);
Integer penalty1LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty), penaltyCharge1AddedDate, "10"));
assertNotNull(penalty1LoanChargeId);
// Add Charge Fee
Integer feeCharge = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false));
targetDate = LocalDate.of(2023, 3, 14);
final String feeChargeAddedDate = dateFormatter.format(targetDate);
Integer feeLoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge), feeChargeAddedDate, "10"));
assertNotNull(feeLoanChargeId);
// Run accrual entries job for business date
final String jobName = "Add Accrual Transactions";
schedulerJobHelper.executeAndAwaitJob(jobName);
// verify accrual transaction created for charges create date
checkAccrualTransaction(currentDate, 0.0f, 10.0f, 10.0f, loanId);
// Set business date
LocalDate futureDate = LocalDate.of(2023, 03, 4);
final String nextAccrualRunDate = dateFormatter.format(futureDate);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, futureDate);
// make repayment
final PostLoansLoanIdTransactionsResponse repaymentTransaction_1 = loanTransactionHelper.makeLoanRepayment(loanExternalIdStr,
new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("4 March 2023").locale("en")
.transactionAmount(100.0));
// Add Charge
Integer feeCharge_1 = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false));
targetDate = LocalDate.of(2023, 3, 21);
final String feeChargeAddedDate_1 = dateFormatter.format(targetDate);
Integer feeLoanChargeId_1 = this.loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge_1), feeChargeAddedDate_1, "10"));
assertNotNull(feeLoanChargeId_1);
// Run accrual entries job for business date
schedulerJobHelper.executeAndAwaitJob(jobName);
// verify accrual transaction created for charges create date
checkAccrualTransaction(futureDate, 0.0f, 10.0f, 0.0f, loanId);
} finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
GlobalConfigurationHelper.updateChargeAccrualDateConfiguration(this.requestSpec, this.responseSpec, "due-date");
}
}
@Test
public void loanAccrualTransactionOnChargeSubmitted_With_Multiple_Repayments_Test_Add_Periodic_Accrual_Transactions_Job() {
try {
final SchedulerJobHelper schedulerJobHelper = new SchedulerJobHelper(requestSpec);
// Accounts oof periodic accrual
final Account assetAccount = this.accountHelper.createAssetAccount();
final Account incomeAccount = this.accountHelper.createIncomeAccount();
final Account expenseAccount = this.accountHelper.createExpenseAccount();
final Account overpaymentAccount = this.accountHelper.createLiabilityAccount();
// Set business date
LocalDate currentDate = LocalDate.of(2023, 03, 3);
final String accrualRunTillDate = dateFormatter.format(currentDate);
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, currentDate);
GlobalConfigurationHelper.updateChargeAccrualDateConfiguration(this.requestSpec, this.responseSpec, "submitted-date");
// Loan ExternalId
String loanExternalIdStr = UUID.randomUUID().toString();
// Client and Loan account creation
final Integer clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProductMultipleRepayments(
loanTransactionHelper, assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
assertNotNull(getLoanProductsProductResponse);
final Integer loanId = createLoanAccountMultipleRepayments(clientId, getLoanProductsProductResponse.getId(), loanExternalIdStr);
// Add Charge Penalty
Integer penalty = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", true));
// Due for future date in one of the schedule
LocalDate targetDate = LocalDate.of(2023, 3, 10);
final String penaltyCharge1AddedDate = dateFormatter.format(targetDate);
Integer penalty1LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty), penaltyCharge1AddedDate, "10"));
assertNotNull(penalty1LoanChargeId);
// Add Charge Penalty
Integer penalty_1 = ChargesHelper.createCharges(requestSpec, responseSpec,
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", true));
// Due for future date in different of the schedule
targetDate = LocalDate.of(2023, 3, 17);
final String penaltyChargeAddedDate = dateFormatter.format(targetDate);
Integer penaltyLoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty_1), penaltyChargeAddedDate, "10"));
assertNotNull(penaltyLoanChargeId);
// Run periodic accrual job for business date
final String jobName = "Add Periodic Accrual Transactions";
schedulerJobHelper.executeAndAwaitJob(jobName);
// verify multiple accrual transactions are created on charge created date according to repayment schedule
// to which charge due date falls
checkAccrualTransactionsForMultipleRepaymentSchedulesChargeDueDate(currentDate, loanId);
} finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
GlobalConfigurationHelper.updateChargeAccrualDateConfiguration(this.requestSpec, this.responseSpec, "due-date");
}
}
private void checkAccrualTransactionsForMultipleRepaymentSchedulesChargeDueDate(LocalDate transactionDate, Integer loanId) {
ArrayList<HashMap> transactions = (ArrayList<HashMap>) loanTransactionHelper.getLoanTransactions(this.requestSpec,
this.responseSpec, loanId);
boolean isTransactionFound = false;
for (int i = 0; i < transactions.size(); i++) {
HashMap transactionType = (HashMap) transactions.get(i).get("type");
boolean isAccrualTransaction = (Boolean) transactionType.get("accrual");
if (isAccrualTransaction) {
ArrayList<Integer> accrualEntryDateAsArray = (ArrayList<Integer>) transactions.get(i).get("date");
LocalDate accrualEntryDate = LocalDate.of(accrualEntryDateAsArray.get(0), accrualEntryDateAsArray.get(1),
accrualEntryDateAsArray.get(2));
if (transactionDate.isEqual(accrualEntryDate)) {
isTransactionFound = true;
verifyAmounts(0.0f, 0.0f, 10.0f, Float.valueOf(String.valueOf(transactions.get(i).get("interestPortion"))),
Float.valueOf(String.valueOf(transactions.get(i).get("feeChargesPortion"))),
Float.valueOf(String.valueOf(transactions.get(i).get("penaltyChargesPortion"))));
}
}
}
assertTrue(isTransactionFound, "No Accrual entries are posted");
}
private void verifyAmounts(final Float expectedInterestPortion, final Float expectedFeePortion, final Float expectedPenaltyPortion,
final Float actualInterestPortion, final Float actualFeePortion, final Float actualPenaltyPortion) {
assertEquals(expectedInterestPortion, actualInterestPortion, "Mismatch in transaction amounts");
assertEquals(expectedFeePortion, actualFeePortion, "Mismatch in transaction amounts");
assertEquals(expectedPenaltyPortion, actualPenaltyPortion, "Mismatch in transaction amounts");
}
private GetLoanProductsProductIdResponse createLoanProduct(final LoanTransactionHelper loanTransactionHelper,
final Account... accounts) {
final String loanProductJSON = new LoanProductTestBuilder().withPrincipal("1000").withRepaymentAfterEvery("1")
.withNumberOfRepayments("1").withRepaymentTypeAsMonth().withinterestRatePerPeriod("0")
.withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsFlat()
.withAccountingRulePeriodicAccrual(accounts).withDaysInMonth("30").withDaysInYear("365").withMoratorium("0", "0")
.build(null);
final Integer loanProductId = loanTransactionHelper.getLoanProductId(loanProductJSON);
return loanTransactionHelper.getLoanProduct(loanProductId);
}
private Integer createLoanAccount(final Integer clientID, final Long loanProductID, final String externalId) {
String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("1")
.withLoanTermFrequencyAsMonths().withNumberOfRepayments("1").withRepaymentEveryAfter("1")
.withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("0").withInterestTypeAsFlatBalance()
.withAmortizationTypeAsEqualPrincipalPayments().withInterestCalculationPeriodTypeSameAsRepaymentPeriod()
.withExpectedDisbursementDate("03 March 2023").withSubmittedOnDate("03 March 2023").withLoanType("individual")
.withExternalId(externalId).build(clientID.toString(), loanProductID.toString(), null);
final Integer loanId = loanTransactionHelper.getLoanId(loanApplicationJSON);
loanTransactionHelper.approveLoan("03 March 2023", "1000", loanId, null);
loanTransactionHelper.disburseLoanWithNetDisbursalAmount("03 March 2023", loanId, "1000");
return loanId;
}
private GetLoanProductsProductIdResponse createLoanProductMultipleRepayments(final LoanTransactionHelper loanTransactionHelper,
final Account... accounts) {
final String loanProductJSON = new LoanProductTestBuilder().withPrincipal("1000").withRepaymentAfterEvery("1")
.withNumberOfRepayments("1").withRepaymentTypeAsDays().withinterestRatePerPeriod("0")
.withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsFlat()
.withAccountingRulePeriodicAccrual(accounts).withDaysInMonth("30").withDaysInYear("365").withMoratorium("0", "0")
.build(null);
final Integer loanProductId = loanTransactionHelper.getLoanProductId(loanProductJSON);
return loanTransactionHelper.getLoanProduct(loanProductId);
}
private Integer createLoanAccountMultipleRepayments(final Integer clientID, final Long loanProductID, final String externalId) {
String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("30")
.withLoanTermFrequencyAsDays().withNumberOfRepayments("10").withRepaymentEveryAfter("3").withRepaymentFrequencyTypeAsDays()
.withInterestRatePerPeriod("0").withInterestTypeAsFlatBalance().withAmortizationTypeAsEqualPrincipalPayments()
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod().withExpectedDisbursementDate("03 March 2023")
.withSubmittedOnDate("03 March 2023").withLoanType("individual").withExternalId(externalId)
.build(clientID.toString(), loanProductID.toString(), null);
final Integer loanId = loanTransactionHelper.getLoanId(loanApplicationJSON);
loanTransactionHelper.approveLoan("03 March 2023", "1000", loanId, null);
loanTransactionHelper.disburseLoanWithNetDisbursalAmount("03 March 2023", loanId, "1000");
return loanId;
}
private void checkAccrualTransaction(final LocalDate transactionDate, final Float interestPortion, final Float feePortion,
final Float penaltyPortion, final Integer loanID) {
ArrayList<HashMap> transactions = (ArrayList<HashMap>) loanTransactionHelper.getLoanTransactions(this.requestSpec,
this.responseSpec, loanID);
boolean isTransactionFound = false;
for (int i = 0; i < transactions.size(); i++) {
HashMap transactionType = (HashMap) transactions.get(i).get("type");
boolean isAccrualTransaction = (Boolean) transactionType.get("accrual");
if (isAccrualTransaction) {
ArrayList<Integer> accrualEntryDateAsArray = (ArrayList<Integer>) transactions.get(i).get("date");
LocalDate accrualEntryDate = LocalDate.of(accrualEntryDateAsArray.get(0), accrualEntryDateAsArray.get(1),
accrualEntryDateAsArray.get(2));
if (transactionDate.isEqual(accrualEntryDate)) {
isTransactionFound = true;
assertEquals(interestPortion, Float.valueOf(String.valueOf(transactions.get(i).get("interestPortion"))),
"Mismatch in transaction amounts");
assertEquals(feePortion, Float.valueOf(String.valueOf(transactions.get(i).get("feeChargesPortion"))),
"Mismatch in transaction amounts");
assertEquals(penaltyPortion, Float.valueOf(String.valueOf(transactions.get(i).get("penaltyChargesPortion"))),
"Mismatch in transaction amounts");
break;
}
}
}
assertTrue(isTransactionFound, "No Accrual entries are posted");
}
}
@@ -120,8 +120,8 @@ public class GlobalConfigurationHelper {
ArrayList<HashMap> actualGlobalConfigurations = getAllGlobalConfigurations(requestSpec, responseSpec);
// There are currently 50 global configurations.
Assertions.assertEquals(50, expectedGlobalConfigurations.size());
Assertions.assertEquals(50, actualGlobalConfigurations.size());
Assertions.assertEquals(51, expectedGlobalConfigurations.size());
Assertions.assertEquals(51, actualGlobalConfigurations.size());
for (int i = 0; i < expectedGlobalConfigurations.size(); i++) {
@@ -556,6 +556,16 @@ public class GlobalConfigurationHelper {
loanArrearsDelinquencyDisplayData.put("enabled", true);
loanArrearsDelinquencyDisplayData.put("trapDoor", false);
defaults.add(loanArrearsDelinquencyDisplayData);
HashMap<String, Object> accrualForChargeDate = new HashMap<>();
accrualForChargeDate.put("id", 56);
accrualForChargeDate.put("name", "charge-accrual-date");
accrualForChargeDate.put("value", 0);
accrualForChargeDate.put("enabled", true);
accrualForChargeDate.put("trapDoor", false);
accrualForChargeDate.put("string_value", "due-date");
defaults.add(accrualForChargeDate);
return defaults;
}
@@ -667,4 +677,17 @@ public class GlobalConfigurationHelper {
return updateEnabledFlagForGlobalConfiguration(requestSpec, responseSpec, configId, enabled);
}
public static Integer updateChargeAccrualDateConfiguration(final RequestSpecification requestSpec,
final ResponseSpecification responseSpec, final String stringValue) {
long configId = 56;
final HashMap<String, String> map = new HashMap<>();
map.put("stringValue", stringValue);
log.info("map : {}", map);
final String configValue = GSON.toJson(map);
final String GLOBAL_CONFIG_UPDATE_URL = "/fineract-provider/api/v1/configurations/" + configId + "?" + Utils.TENANT_IDENTIFIER;
log.info("---------------------------------UPDATE VALUE FOR GLOBAL CONFIG---------------------------------------------");
return Utils.performServerPut(requestSpec, responseSpec, GLOBAL_CONFIG_UPDATE_URL, configValue, "resourceId");
}
}