From f927dd4ff93f1a84c72bf47f3ec943158fa0805c Mon Sep 17 00:00:00 2001 From: Arnold Galovics Date: Wed, 15 Feb 2023 16:41:27 +0100 Subject: [PATCH] FINERACT-1724: Added Add periodic accrual as a Loan COB business step --- ...AddPeriodicAccrualEntriesBusinessStep.java | 54 +++++++++ .../service/LoanAccrualPlatformService.java | 3 + .../LoanAccrualPlatformServiceImpl.java | 8 ++ .../service/LoanReadPlatformService.java | 2 + .../service/LoanReadPlatformServiceImpl.java | 10 +- .../db/changelog/tenant/changelog-tenant.xml | 1 + ...periodic_accrual_entries_business_step.xml | 32 ++++++ ...eriodicAccrualEntriesBusinessStepTest.java | 107 ++++++++++++++++++ 8 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/cob/loan/AddPeriodicAccrualEntriesBusinessStep.java create mode 100644 fineract-provider/src/main/resources/db/changelog/tenant/parts/0092_add_periodic_accrual_entries_business_step.xml create mode 100644 fineract-provider/src/test/java/org/apache/fineract/cob/loan/AddPeriodicAccrualEntriesBusinessStepTest.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/AddPeriodicAccrualEntriesBusinessStep.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/AddPeriodicAccrualEntriesBusinessStep.java new file mode 100644 index 000000000..8d727e1ff --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/AddPeriodicAccrualEntriesBusinessStep.java @@ -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"; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualPlatformService.java index 32e4f9b2c..46111fbb6 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualPlatformService.java @@ -22,11 +22,14 @@ import java.time.LocalDate; import java.util.Collection; import org.apache.fineract.infrastructure.core.exception.MultiException; import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; public interface LoanAccrualPlatformService { void addPeriodicAccruals(LocalDate tilldate) throws MultiException; + void addPeriodicAccruals(LocalDate tilldate, Loan loan) throws MultiException; + void addPeriodicAccruals(LocalDate tilldate, Collection loanScheduleAccrualDatas) throws MultiException; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualPlatformServiceImpl.java index 590fbb56b..abe7867f3 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualPlatformServiceImpl.java @@ -28,6 +28,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException; import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.springframework.stereotype.Service; @Service @@ -45,6 +46,13 @@ public class LoanAccrualPlatformServiceImpl implements LoanAccrualPlatformServic addPeriodicAccruals(tillDate, loanScheduleAccrualDataList); } + @Override + public void addPeriodicAccruals(final LocalDate tillDate, Loan loan) throws JobExecutionException { + Collection loanScheduleAccrualDataList = this.loanReadPlatformService.retrievePeriodicAccrualData(tillDate, + loan); + addPeriodicAccruals(tillDate, loanScheduleAccrualDataList); + } + @Override public void addPeriodicAccruals(final LocalDate tillDate, Collection loanScheduleAccrualDataList) throws JobExecutionException { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java index 220141156..235b6b87f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java @@ -116,6 +116,8 @@ public interface LoanReadPlatformService { Collection retrievePeriodicAccrualData(LocalDate tillDate); + Collection retrievePeriodicAccrualData(LocalDate tillDate, Loan loan); + LoanTransactionData retrieveLoanChargeOffTemplate(Long loanId); Collection fetchLoansForInterestRecalculation(); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index 0e5acbb8b..3ea603324 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -1761,7 +1761,11 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { @Override public Collection retrievePeriodicAccrualData(final LocalDate tillDate) { + return retrievePeriodicAccrualData(tillDate, null); + } + @Override + public Collection retrievePeriodicAccrualData(final LocalDate tillDate, final Loan loan) { LoanSchedulePeriodicAccrualMapper mapper = new LoanSchedulePeriodicAccrualMapper(); LocalDate organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate(); 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(" 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))) "); - Map paramMap = new HashMap<>(4); + Map 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()); diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml index ec1c1fcd8..56d37e5a8 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml @@ -111,4 +111,5 @@ + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0092_add_periodic_accrual_entries_business_step.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0092_add_periodic_accrual_entries_business_step.xml new file mode 100644 index 000000000..d161b254c --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0092_add_periodic_accrual_entries_business_step.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + diff --git a/fineract-provider/src/test/java/org/apache/fineract/cob/loan/AddPeriodicAccrualEntriesBusinessStepTest.java b/fineract-provider/src/test/java/org/apache/fineract/cob/loan/AddPeriodicAccrualEntriesBusinessStepTest.java new file mode 100644 index 000000000..a77f4f0da --- /dev/null +++ b/fineract-provider/src/test/java/org/apache/fineract/cob/loan/AddPeriodicAccrualEntriesBusinessStepTest.java @@ -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); + } +}