FINERACT-1724: Added Add periodic accrual as a Loan COB business step
This commit is contained in:
committed by
taskain7
parent
3744ad54ee
commit
f927dd4ff9
+54
@@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* 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.cob.loan;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.cob.exceptions.BusinessStepException;
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.MultiException;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualPlatformService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AddPeriodicAccrualEntriesBusinessStep implements LoanCOBBusinessStep {
|
||||||
|
|
||||||
|
private final LoanAccrualPlatformService loanAccrualPlatformService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Loan execute(Loan loan) {
|
||||||
|
try {
|
||||||
|
loanAccrualPlatformService.addPeriodicAccruals(DateUtils.getBusinessLocalDate(), loan);
|
||||||
|
} catch (MultiException e) {
|
||||||
|
throw new BusinessStepException(String.format("Fail to process period accrual for loan id [%s]", loan.getId()), e);
|
||||||
|
}
|
||||||
|
return loan;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEnumStyledName() {
|
||||||
|
return "ADD_PERIODIC_ACCRUAL_ENTRIES";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHumanReadableName() {
|
||||||
|
return "Add periodic accrual entries";
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -22,11 +22,14 @@ import java.time.LocalDate;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import org.apache.fineract.infrastructure.core.exception.MultiException;
|
import org.apache.fineract.infrastructure.core.exception.MultiException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
|
|
||||||
public interface LoanAccrualPlatformService {
|
public interface LoanAccrualPlatformService {
|
||||||
|
|
||||||
void addPeriodicAccruals(LocalDate tilldate) throws MultiException;
|
void addPeriodicAccruals(LocalDate tilldate) throws MultiException;
|
||||||
|
|
||||||
|
void addPeriodicAccruals(LocalDate tilldate, Loan loan) throws MultiException;
|
||||||
|
|
||||||
void addPeriodicAccruals(LocalDate tilldate, Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas) throws MultiException;
|
void addPeriodicAccruals(LocalDate tilldate, Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas) throws MultiException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+8
@@ -28,6 +28,7 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
|
import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@@ -45,6 +46,13 @@ public class LoanAccrualPlatformServiceImpl implements LoanAccrualPlatformServic
|
|||||||
addPeriodicAccruals(tillDate, loanScheduleAccrualDataList);
|
addPeriodicAccruals(tillDate, loanScheduleAccrualDataList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addPeriodicAccruals(final LocalDate tillDate, Loan loan) throws JobExecutionException {
|
||||||
|
Collection<LoanScheduleAccrualData> loanScheduleAccrualDataList = this.loanReadPlatformService.retrievePeriodicAccrualData(tillDate,
|
||||||
|
loan);
|
||||||
|
addPeriodicAccruals(tillDate, loanScheduleAccrualDataList);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addPeriodicAccruals(final LocalDate tillDate, Collection<LoanScheduleAccrualData> loanScheduleAccrualDataList)
|
public void addPeriodicAccruals(final LocalDate tillDate, Collection<LoanScheduleAccrualData> loanScheduleAccrualDataList)
|
||||||
throws JobExecutionException {
|
throws JobExecutionException {
|
||||||
|
|||||||
+2
@@ -116,6 +116,8 @@ public interface LoanReadPlatformService {
|
|||||||
|
|
||||||
Collection<LoanScheduleAccrualData> retrievePeriodicAccrualData(LocalDate tillDate);
|
Collection<LoanScheduleAccrualData> retrievePeriodicAccrualData(LocalDate tillDate);
|
||||||
|
|
||||||
|
Collection<LoanScheduleAccrualData> retrievePeriodicAccrualData(LocalDate tillDate, Loan loan);
|
||||||
|
|
||||||
LoanTransactionData retrieveLoanChargeOffTemplate(Long loanId);
|
LoanTransactionData retrieveLoanChargeOffTemplate(Long loanId);
|
||||||
|
|
||||||
Collection<Long> fetchLoansForInterestRecalculation();
|
Collection<Long> fetchLoansForInterestRecalculation();
|
||||||
|
|||||||
+9
-1
@@ -1761,7 +1761,11 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<LoanScheduleAccrualData> retrievePeriodicAccrualData(final LocalDate tillDate) {
|
public Collection<LoanScheduleAccrualData> retrievePeriodicAccrualData(final LocalDate tillDate) {
|
||||||
|
return retrievePeriodicAccrualData(tillDate, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<LoanScheduleAccrualData> retrievePeriodicAccrualData(final LocalDate tillDate, final Loan loan) {
|
||||||
LoanSchedulePeriodicAccrualMapper mapper = new LoanSchedulePeriodicAccrualMapper();
|
LoanSchedulePeriodicAccrualMapper mapper = new LoanSchedulePeriodicAccrualMapper();
|
||||||
LocalDate organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate();
|
LocalDate organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate();
|
||||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||||
@@ -1772,11 +1776,15 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
.append(" or (ls.interest_amount <> COALESCE(ls.accrual_interest_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.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))) ");
|
||||||
Map<String, Object> paramMap = new HashMap<>(4);
|
Map<String, Object> paramMap = new HashMap<>(5);
|
||||||
if (organisationStartDate != null) {
|
if (organisationStartDate != null) {
|
||||||
sqlBuilder.append(" and ls.duedate > :organisationStartDate ");
|
sqlBuilder.append(" and ls.duedate > :organisationStartDate ");
|
||||||
paramMap.put("organisationStartDate", 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 ");
|
sqlBuilder.append(" order by loan.id,ls.duedate ");
|
||||||
paramMap.put("active", LoanStatus.ACTIVE.getValue());
|
paramMap.put("active", LoanStatus.ACTIVE.getValue());
|
||||||
paramMap.put("type", AccountingRuleType.ACCRUAL_PERIODIC.getValue());
|
paramMap.put("type", AccountingRuleType.ACCRUAL_PERIODIC.getValue());
|
||||||
|
|||||||
@@ -111,4 +111,5 @@
|
|||||||
<include file="parts/0089_add_update_loan_arrears_aging_business_step.xml" relativeToChangelogFile="true" />
|
<include file="parts/0089_add_update_loan_arrears_aging_business_step.xml" relativeToChangelogFile="true" />
|
||||||
<include file="parts/0090_add_report_export_s3_folder_configuration.xml" relativeToChangelogFile="true" />
|
<include file="parts/0090_add_report_export_s3_folder_configuration.xml" relativeToChangelogFile="true" />
|
||||||
<include file="parts/0091_modify_parameter_value_type_in_job_parameters_table.xml" relativeToChangelogFile="true" />
|
<include file="parts/0091_modify_parameter_value_type_in_job_parameters_table.xml" relativeToChangelogFile="true" />
|
||||||
|
<include file="parts/0092_add_periodic_accrual_entries_business_step.xml" relativeToChangelogFile="true" />
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
<?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.1.xsd">
|
||||||
|
<changeSet id="1" author="fineract">
|
||||||
|
<insert tableName="m_batch_business_steps">
|
||||||
|
<column name="job_name" value="LOAN_CLOSE_OF_BUSINESS"/>
|
||||||
|
<column name="step_name" value="ADD_PERIODIC_ACCRUAL_ENTRIES"/>
|
||||||
|
<column name="step_order" value="6"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
/**
|
||||||
|
* 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.cob.loan;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.apache.commons.lang3.RandomUtils;
|
||||||
|
import org.apache.fineract.cob.exceptions.BusinessStepException;
|
||||||
|
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
||||||
|
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.MultiException;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualPlatformService;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
public class AddPeriodicAccrualEntriesBusinessStepTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private LoanAccrualPlatformService loanAccrualPlatformService;
|
||||||
|
|
||||||
|
private AddPeriodicAccrualEntriesBusinessStep underTest;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() {
|
||||||
|
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
|
||||||
|
ThreadLocalContextUtil
|
||||||
|
.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, LocalDate.now(ZoneId.systemDefault()))));
|
||||||
|
underTest = new AddPeriodicAccrualEntriesBusinessStep(loanAccrualPlatformService);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenLoanWithAccrual() throws MultiException {
|
||||||
|
// given
|
||||||
|
final Loan loanForProcessing = Mockito.mock(Loan.class);
|
||||||
|
// when
|
||||||
|
final Loan processedLoan = underTest.execute(loanForProcessing);
|
||||||
|
// then
|
||||||
|
verify(loanAccrualPlatformService, times(1)).addPeriodicAccruals(any(LocalDate.class), eq(loanForProcessing));
|
||||||
|
assertEquals(processedLoan, loanForProcessing);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenLoanWithAccrualThrowException() throws MultiException {
|
||||||
|
// given
|
||||||
|
final Long loanId = RandomUtils.nextLong();
|
||||||
|
final Loan loanForProcessing = Mockito.mock(Loan.class);
|
||||||
|
when(loanForProcessing.getId()).thenReturn(loanId);
|
||||||
|
doThrow(new MultiException(Collections.singletonList(new RuntimeException()))).when(loanAccrualPlatformService)
|
||||||
|
.addPeriodicAccruals(any(LocalDate.class), eq(loanForProcessing));
|
||||||
|
// when
|
||||||
|
final BusinessStepException businessStepException = Assert.assertThrows(BusinessStepException.class,
|
||||||
|
() -> underTest.execute(loanForProcessing));
|
||||||
|
// then
|
||||||
|
verify(loanAccrualPlatformService, times(1)).addPeriodicAccruals(any(LocalDate.class), eq(loanForProcessing));
|
||||||
|
assertEquals(String.format("Fail to process period accrual for loan id [%s]", loanId), businessStepException.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetEnumStyledNameSuccessScenario() {
|
||||||
|
final String actualEnumName = underTest.getEnumStyledName();
|
||||||
|
assertNotNull(actualEnumName);
|
||||||
|
assertEquals("ADD_PERIODIC_ACCRUAL_ENTRIES", actualEnumName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetHumanReadableNameSuccessScenario() {
|
||||||
|
final String actualEnumName = underTest.getHumanReadableName();
|
||||||
|
assertNotNull(actualEnumName);
|
||||||
|
assertEquals("Add periodic accrual entries", actualEnumName);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user