diff --git a/fineract-avro-schemas/src/main/avro/loan/v1/CollectionDataV1.avsc b/fineract-avro-schemas/src/main/avro/loan/v1/CollectionDataV1.avsc index e424e78e5..36256d227 100644 --- a/fineract-avro-schemas/src/main/avro/loan/v1/CollectionDataV1.avsc +++ b/fineract-avro-schemas/src/main/avro/loan/v1/CollectionDataV1.avsc @@ -82,6 +82,17 @@ "null", "bigdecimal" ] + }, + { + "default": null, + "name": "delinquencyPausePeriods", + "type": [ + "null", + { + "type": "array", + "items": "org.apache.fineract.avro.loan.v1.DelinquencyPausePeriodV1" + } + ] } ] } diff --git a/fineract-avro-schemas/src/main/avro/loan/v1/DelinquencyPausePeriodV1.avsc b/fineract-avro-schemas/src/main/avro/loan/v1/DelinquencyPausePeriodV1.avsc new file mode 100644 index 000000000..8d5d33434 --- /dev/null +++ b/fineract-avro-schemas/src/main/avro/loan/v1/DelinquencyPausePeriodV1.avsc @@ -0,0 +1,31 @@ +{ + "name": "DelinquencyPausePeriodV1", + "namespace": "org.apache.fineract.avro.loan.v1", + "type": "record", + "fields": [ + { + "default": null, + "name": "active", + "type": [ + "null", + "boolean" + ] + }, + { + "default": null, + "name": "pausePeriodStart", + "type": [ + "null", + "string" + ] + }, + { + "default": null, + "name": "pausePeriodEnd", + "type": [ + "null", + "string" + ] + } + ] +} diff --git a/fineract-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/LoanAccountDelinquencyPauseChangedBusinessEvent.java b/fineract-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/LoanAccountDelinquencyPauseChangedBusinessEvent.java new file mode 100644 index 000000000..5b3419d53 --- /dev/null +++ b/fineract-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/LoanAccountDelinquencyPauseChangedBusinessEvent.java @@ -0,0 +1,35 @@ +/** + * 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.infrastructure.event.business.domain.loan; + +import org.apache.fineract.portfolio.loanaccount.domain.Loan; + +public class LoanAccountDelinquencyPauseChangedBusinessEvent extends LoanBusinessEvent { + + private static final String TYPE = "LoanAccountDelinquencyPauseChangedBusinessEvent"; + + public LoanAccountDelinquencyPauseChangedBusinessEvent(Loan value) { + super(value); + } + + @Override + public String getType() { + return TYPE; + } +} diff --git a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml index 25da71bb5..b08ead78b 100644 --- a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml +++ b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml @@ -35,4 +35,5 @@ + diff --git a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1013_add_loan_account_delinquency_pause_changed_event.xml b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1013_add_loan_account_delinquency_pause_changed_event.xml new file mode 100644 index 000000000..03851e4ff --- /dev/null +++ b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1013_add_loan_account_delinquency_pause_changed_event.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/loan/LoanAccountDataMapper.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/loan/LoanAccountDataMapper.java index 0db349fc8..03b144614 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/loan/LoanAccountDataMapper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/loan/LoanAccountDataMapper.java @@ -24,7 +24,8 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanAccountData; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -@Mapper(config = AvroMapperConfig.class, uses = { LoanTransactionDataMapper.class, LoanChargeDataMapper.class }) +@Mapper(config = AvroMapperConfig.class, uses = { LoanTransactionDataMapper.class, LoanChargeDataMapper.class, + LoanDelinquencyPausePeriodMapper.class }) public interface LoanAccountDataMapper { // TODO: avoid prefix "is" in class attributes; I would recommend to fix this also in the Avro structures diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/loan/LoanDelinquencyPausePeriodMapper.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/loan/LoanDelinquencyPausePeriodMapper.java new file mode 100644 index 000000000..c4b99d34e --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/loan/LoanDelinquencyPausePeriodMapper.java @@ -0,0 +1,31 @@ +/** + * 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.infrastructure.event.external.service.serialization.mapper.loan; + +import org.apache.fineract.avro.loan.v1.DelinquencyPausePeriodV1; +import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.support.AvroMapperConfig; +import org.apache.fineract.portfolio.loanaccount.data.DelinquencyPausePeriod; +import org.mapstruct.Mapper; + +@Mapper(config = AvroMapperConfig.class) +public interface LoanDelinquencyPausePeriodMapper { + + DelinquencyPausePeriodV1 map(DelinquencyPausePeriod source); + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java index bfbe2b480..d0192d168 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java @@ -30,7 +30,6 @@ import java.util.Optional; import java.util.stream.Collector; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; -import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData; @@ -52,6 +51,7 @@ import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyRangeMapper; import org.apache.fineract.portfolio.delinquency.mapper.LoanDelinquencyTagMapper; import org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData; import org.apache.fineract.portfolio.loanaccount.data.CollectionData; +import org.apache.fineract.portfolio.loanaccount.data.DelinquencyPausePeriod; import org.apache.fineract.portfolio.loanaccount.data.InstallmentLevelDelinquency; import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; @@ -205,23 +205,20 @@ public class DelinquencyReadPlatformServiceImpl implements DelinquencyReadPlatfo effective.add(loanDelinquencyActionData); } - // order them by start date, filter out the future items - Optional last = effective.stream() - .sorted(Comparator.comparing(LoanDelinquencyActionData::getStartDate)) - .filter(e -> !DateUtils.isAfter(e.getStartDate(), businessDate)) // keep only the present and the - // past ones - .reduce((first, second) -> second); - - // enrich collectionData - last.ifPresent(action -> { - collectionData - .setDelinquencyCalculationPaused(!action.startDate.isAfter(businessDate) && !businessDate.isAfter(action.endDate)); - collectionData.setDelinquencyPausePeriodStartDate(action.getStartDate()); - collectionData.setDelinquencyPausePeriodEndDate(action.getEndDate()); - }); + // order them by start date, and convert to DelinquencyPausePeriod objects + List result = effective.stream() // + .sorted(Comparator.comparing(LoanDelinquencyActionData::getStartDate)) // + .map(lda -> toDelinquencyPausePeriod(businessDate, lda)).toList(); // + collectionData.setDelinquencyPausePeriods(result); } } + @NotNull + private static DelinquencyPausePeriod toDelinquencyPausePeriod(LocalDate businessDate, LoanDelinquencyActionData lda) { + return new DelinquencyPausePeriod(!lda.getStartDate().isAfter(businessDate) && !businessDate.isAfter(lda.getEndDate()), + lda.getStartDate(), lda.getEndDate()); + } + private Optional findMatchingResume(LoanDelinquencyAction pause, List resumes) { if (resumes != null && resumes.size() > 0) { for (LoanDelinquencyAction resume : resumes) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java index f31ee95da..75bcf3f7d 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java @@ -33,6 +33,7 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.infrastructure.event.business.domain.loan.LoanAccountDelinquencyPauseChangedBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.loan.LoanDelinquencyRangeChangeBusinessEvent; import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; import org.apache.fineract.portfolio.delinquency.api.DelinquencyApiConstants; @@ -62,6 +63,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository; +import org.springframework.transaction.annotation.Transactional; @RequiredArgsConstructor @Slf4j @@ -205,6 +207,7 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat } @Override + @Transactional public CommandProcessingResult createDelinquencyAction(Long loanId, JsonCommand command) { final Loan loan = this.loanRepository.findOneWithNotFoundDetection(loanId); final LocalDate businessDate = DateUtils.getBusinessLocalDate(); @@ -216,7 +219,7 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat parsedDelinquencyAction.setLoan(loan); LoanDelinquencyAction saved = loanDelinquencyActionRepository.saveAndFlush(parsedDelinquencyAction); - + businessEventNotifierService.notifyPostBusinessEvent(new LoanAccountDelinquencyPauseChangedBusinessEvent(loan)); return new CommandProcessingResultBuilder().withCommandId(command.commandId()) // .withEntityId(saved.getId()) // .withOfficeId(loan.getOfficeId()) // diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java index 0f86f1544..a06bcb780 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java @@ -990,20 +990,27 @@ final class LoansApiResourceSwagger { @Schema(example = "100.000000") public Double lastRepaymentAmount; - @Schema(example = "true") - public Boolean delinquencyCalculationPaused; - - @Schema(example = "[2022, 07, 05]") - public LocalDate delinquencyPausePeriodStartDate; - - @Schema(example = "[2022, 07, 10]") - public LocalDate delinquencyPausePeriodEndDate; + @Schema(description = "List of GetLoansLoanIdDelinquencyPausePeriod") + public List delinquencyPausePeriods; @Schema(description = "List of GetLoansLoanIdLoanInstallmentLevelDelinquency") public List installmentLevelDelinquency; } + static final class GetLoansLoanIdDelinquencyPausePeriod { + + @Schema(example = "true") + public Boolean active; + + @Schema(example = "[2022, 07, 05]") + public LocalDate pausePeriodStart; + + @Schema(example = "[2022, 07, 10]") + public LocalDate pausePeriodEnd; + + } + static final class GetLoansLoanIdLoanInstallmentLevelDelinquency { private GetLoansLoanIdLoanInstallmentLevelDelinquency() {} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java index 1b51d221c..f872d6d27 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java @@ -44,14 +44,12 @@ public final class CollectionData { private LocalDate lastRepaymentDate; private BigDecimal lastRepaymentAmount; - public boolean delinquencyCalculationPaused; - public LocalDate delinquencyPausePeriodStartDate; - public LocalDate delinquencyPausePeriodEndDate; + public Collection delinquencyPausePeriods; public Collection installmentLevelDelinquency; public static CollectionData template() { final BigDecimal zero = BigDecimal.ZERO; - return new CollectionData(zero, 0L, null, 0L, null, zero, null, zero, null, zero, false, null, null, null); + return new CollectionData(zero, 0L, null, 0L, null, zero, null, zero, null, zero, null, null); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/DelinquencyPausePeriod.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/DelinquencyPausePeriod.java new file mode 100644 index 000000000..0aef796b7 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/DelinquencyPausePeriod.java @@ -0,0 +1,36 @@ +/** + * 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.portfolio.loanaccount.data; + +import java.time.LocalDate; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@AllArgsConstructor +@ToString +@Getter +@EqualsAndHashCode +public class DelinquencyPausePeriod { + + private boolean active; + private LocalDate pausePeriodStart; + private LocalDate pausePeriodEnd; +} diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/ExternalEventConfigurationValidationServiceTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/ExternalEventConfigurationValidationServiceTest.java index 7c6c443dc..ca46595cf 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/ExternalEventConfigurationValidationServiceTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/ExternalEventConfigurationValidationServiceTest.java @@ -97,7 +97,8 @@ public class ExternalEventConfigurationValidationServiceTest { "LoanAccountsStayedLockedBusinessEvent", "MockBusinessEvent", "LoanChargeOffPreBusinessEvent", "LoanChargeOffPostBusinessEvent", "LoanUndoChargeOffBusinessEvent", "LoanAccrualTransactionCreatedBusinessEvent", "LoanRescheduledDueAdjustScheduleBusinessEvent", "LoanOwnershipTransferBusinessEvent", "LoanAccountSnapshotBusinessEvent", - "LoanTransactionDownPaymentPostBusinessEvent", "LoanTransactionDownPaymentPreBusinessEvent"); + "LoanTransactionDownPaymentPostBusinessEvent", "LoanTransactionDownPaymentPreBusinessEvent", + "LoanAccountDelinquencyPauseChangedBusinessEvent"); List tenants = Arrays .asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null)); @@ -176,7 +177,8 @@ public class ExternalEventConfigurationValidationServiceTest { "LoanAccountsStayedLockedBusinessEvent", "LoanChargeOffPreBusinessEvent", "LoanChargeOffPostBusinessEvent", "LoanUndoChargeOffBusinessEvent", "LoanAccrualTransactionCreatedBusinessEvent", "LoanRescheduledDueAdjustScheduleBusinessEvent", "LoanOwnershipTransferBusinessEvent", "LoanAccountSnapshotBusinessEvent", - "LoanTransactionDownPaymentPostBusinessEvent", "LoanTransactionDownPaymentPreBusinessEvent"); + "LoanTransactionDownPaymentPostBusinessEvent", "LoanTransactionDownPaymentPreBusinessEvent", + "LoanAccountDelinquencyPauseChangedBusinessEvent"); List tenants = Arrays .asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null)); diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImplTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImplTest.java index 544ec9498..ea6a186df 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImplTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImplTest.java @@ -23,6 +23,7 @@ import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.RESUME; import java.time.LocalDate; +import java.util.Arrays; import java.util.Collection; import java.util.List; import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository; @@ -35,6 +36,7 @@ import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyBucketMapper; import org.apache.fineract.portfolio.delinquency.mapper.DelinquencyRangeMapper; import org.apache.fineract.portfolio.delinquency.mapper.LoanDelinquencyTagMapper; import org.apache.fineract.portfolio.loanaccount.data.CollectionData; +import org.apache.fineract.portfolio.loanaccount.data.DelinquencyPausePeriod; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -85,10 +87,7 @@ class DelinquencyReadPlatformServiceImplTest { // when underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 12)); - // then - Assertions.assertFalse(collectionData.isDelinquencyCalculationPaused()); - Assertions.assertNull(collectionData.getDelinquencyPausePeriodStartDate()); - Assertions.assertNull(collectionData.getDelinquencyPausePeriodEndDate()); + Assertions.assertNull(collectionData.getDelinquencyPausePeriods()); } @Test @@ -104,9 +103,11 @@ class DelinquencyReadPlatformServiceImplTest { underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 12)); // then - Assertions.assertTrue(collectionData.isDelinquencyCalculationPaused()); - Assertions.assertEquals(LocalDate.of(2023, JANUARY, 12), collectionData.getDelinquencyPausePeriodStartDate()); - Assertions.assertEquals(LocalDate.of(2023, JANUARY, 13), collectionData.getDelinquencyPausePeriodEndDate()); + verifyPausePeriods(collectionData, // + pausePeriod(false, "2023-01-10", "2023-01-11"), // + pausePeriod(true, "2023-01-12", "2023-01-13"), // + pausePeriod(false, "2023-01-15", "2023-01-20") // + ); } @Test @@ -121,10 +122,11 @@ class DelinquencyReadPlatformServiceImplTest { // when underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 13)); - // then - Assertions.assertTrue(collectionData.isDelinquencyCalculationPaused()); - Assertions.assertEquals(LocalDate.of(2023, JANUARY, 12), collectionData.getDelinquencyPausePeriodStartDate()); - Assertions.assertEquals(LocalDate.of(2023, JANUARY, 13), collectionData.getDelinquencyPausePeriodEndDate()); + verifyPausePeriods(collectionData, // + pausePeriod(false, "2023-01-10", "2023-01-11"), // + pausePeriod(true, "2023-01-12", "2023-01-13"), // + pausePeriod(false, "2023-01-15", "2023-01-20") // + ); } @Test @@ -139,10 +141,11 @@ class DelinquencyReadPlatformServiceImplTest { // when underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 13)); - // then - Assertions.assertTrue(collectionData.isDelinquencyCalculationPaused()); - Assertions.assertEquals(LocalDate.of(2023, JANUARY, 12), collectionData.getDelinquencyPausePeriodStartDate()); - Assertions.assertEquals(LocalDate.of(2023, JANUARY, 14), collectionData.getDelinquencyPausePeriodEndDate()); + verifyPausePeriods(collectionData, // + pausePeriod(false, "2023-01-10", "2023-01-11"), // + pausePeriod(true, "2023-01-12", "2023-01-14"), // + pausePeriod(false, "2023-01-15", "2023-01-20") // + ); } @Test @@ -158,9 +161,11 @@ class DelinquencyReadPlatformServiceImplTest { underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 12)); // then - Assertions.assertFalse(collectionData.isDelinquencyCalculationPaused()); - Assertions.assertEquals(LocalDate.of(2023, JANUARY, 10), collectionData.getDelinquencyPausePeriodStartDate()); - Assertions.assertEquals(LocalDate.of(2023, JANUARY, 11), collectionData.getDelinquencyPausePeriodEndDate()); + verifyPausePeriods(collectionData, // + pausePeriod(false, "2023-01-10", "2023-01-11"), // + pausePeriod(false, "2023-01-13", "2023-01-14"), // + pausePeriod(false, "2023-01-15", "2023-01-20") // + ); } @Test @@ -177,9 +182,11 @@ class DelinquencyReadPlatformServiceImplTest { underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 12)); // then - Assertions.assertFalse(collectionData.isDelinquencyCalculationPaused()); - Assertions.assertEquals(LocalDate.of(2023, JANUARY, 10), collectionData.getDelinquencyPausePeriodStartDate()); - Assertions.assertEquals(LocalDate.of(2023, JANUARY, 11), collectionData.getDelinquencyPausePeriodEndDate()); + verifyPausePeriods(collectionData, // + pausePeriod(false, "2023-01-10", "2023-01-11"), // + pausePeriod(false, "2023-01-13", "2023-01-14"), // + pausePeriod(false, "2023-01-15", "2023-01-20") // + ); } @Test @@ -196,9 +203,23 @@ class DelinquencyReadPlatformServiceImplTest { underTest.enrichWithDelinquencyPausePeriodInfo(collectionData, delinquencyActions, LocalDate.of(2023, JANUARY, 11)); // then - Assertions.assertTrue(collectionData.isDelinquencyCalculationPaused()); - Assertions.assertEquals(LocalDate.of(2023, JANUARY, 10), collectionData.getDelinquencyPausePeriodStartDate()); - Assertions.assertEquals(LocalDate.of(2023, JANUARY, 11), collectionData.getDelinquencyPausePeriodEndDate()); + verifyPausePeriods(collectionData, // + pausePeriod(true, "2023-01-10", "2023-01-11"), // + pausePeriod(false, "2023-01-13", "2023-01-14"), // + pausePeriod(false, "2023-01-15", "2023-01-20") // + ); + } + + private void verifyPausePeriods(CollectionData collectionData, DelinquencyPausePeriod... pausePeriods) { + if (pausePeriods.length > 0) { + Assertions.assertEquals(Arrays.asList(pausePeriods), collectionData.getDelinquencyPausePeriods()); + } else { + Assertions.assertNull(collectionData.getDelinquencyPausePeriods()); + } + } + + private DelinquencyPausePeriod pausePeriod(boolean active, String startDate, String endDate) { + return new DelinquencyPausePeriod(active, LocalDate.parse(startDate), LocalDate.parse(endDate)); } } diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java index 15e542ab5..977cea5e6 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java @@ -127,7 +127,7 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest { LoanScheduleDelinquencyData loanScheduleDelinquencyData = new LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L, loanForProcessing); CollectionData collectionData = new CollectionData(BigDecimal.ZERO, 2L, null, 2L, overDueSinceDate, BigDecimal.ZERO, null, null, - null, null, false, null, null, null); + null, null, null, null); Map installmentsCollection = new HashMap<>(); @@ -239,10 +239,10 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest { LoanScheduleDelinquencyData loanScheduleDelinquencyData = new LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L, loanForProcessing); CollectionData collectionData = new CollectionData(BigDecimal.ZERO, 2L, null, 2L, overDueSinceDate, BigDecimal.ZERO, null, null, - null, null, false, null, null, null); + null, null, null, null); CollectionData installmentCollectionData = new CollectionData(BigDecimal.ZERO, 2L, null, 2L, overDueSinceDate, - installmentPrincipalAmount, null, null, null, null, false, null, null, null); + installmentPrincipalAmount, null, null, null, null, null, null); Map installmentsCollection = new HashMap<>(); installmentsCollection.put(1L, installmentCollectionData); @@ -305,10 +305,10 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest { LoanScheduleDelinquencyData loanScheduleDelinquencyData = new LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L, loanForProcessing); CollectionData collectionData = new CollectionData(BigDecimal.ZERO, 29L, null, 29L, overDueSinceDate, BigDecimal.ZERO, null, null, - null, null, false, null, null, null); + null, null, null, null); CollectionData installmentCollectionData = new CollectionData(BigDecimal.ZERO, 29L, null, 29L, overDueSinceDate, - installmentPrincipalAmount, null, null, null, null, false, null, null, null); + installmentPrincipalAmount, null, null, null, null, null, null); Map installmentsCollection = new HashMap<>(); installmentsCollection.put(1L, installmentCollectionData); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyActionIntegrationTests.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyActionIntegrationTests.java index aaea011da..1d1fc3fa7 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyActionIntegrationTests.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyActionIntegrationTests.java @@ -29,12 +29,14 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigDecimal; import java.time.LocalDate; +import java.util.Arrays; import java.util.List; import java.util.UUID; import lombok.extern.slf4j.Slf4j; import org.apache.fineract.client.models.BusinessDateRequest; import org.apache.fineract.client.models.GetDelinquencyActionsResponse; import org.apache.fineract.client.models.GetLoanProductsProductIdResponse; +import org.apache.fineract.client.models.GetLoansLoanIdDelinquencyPausePeriod; import org.apache.fineract.client.models.GetLoansLoanIdResponse; import org.apache.fineract.client.models.PostLoanProductsRequest; import org.apache.fineract.client.models.PostLoanProductsResponse; @@ -172,26 +174,29 @@ public class DelinquencyActionIntegrationTests extends BaseLoanIntegrationTest { .dateFormat(DATETIME_PATTERN).locale("en")); // Validate Loan Delinquency Pause Period on Loan - validateLoanDelinquencyPausePerios(loanId, "10 January 2023", "15 January 2023", true); + validateLoanDelinquencyPausePeriods(loanId, pausePeriods("10 January 2023", "15 January 2023", true)); // Create a Resume for the Loan for the current business date, it is still expected to be in pause loanTransactionHelper.createLoanDelinquencyAction(loanId, RESUME, "14 January 2023"); // Validate Loan Delinquency Pause Period on Loan - validateLoanDelinquencyPausePerios(loanId, "10 January 2023", "14 January 2023", true); + validateLoanDelinquencyPausePeriods(loanId, pausePeriods("10 January 2023", "14 January 2023", true)); // Update business date to 15 January 2023 businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BUSINESS_DATE.getName()).date("15 January 2023") .dateFormat(DATETIME_PATTERN).locale("en")); // Validate Loan Delinquency Pause Period on Loan - validateLoanDelinquencyPausePerios(loanId, "10 January 2023", "14 January 2023", false); + validateLoanDelinquencyPausePeriods(loanId, pausePeriods("10 January 2023", "14 January 2023", false)); // Create a new pause action for the future loanTransactionHelper.createLoanDelinquencyAction(loanId, PAUSE, "20 January 2023", "25 January 2023"); // Validate Loan Delinquency Pause Period on Loan - validateLoanDelinquencyPausePerios(loanId, "10 January 2023", "14 January 2023", false); + validateLoanDelinquencyPausePeriods(loanId, // + pausePeriods("10 January 2023", "14 January 2023", false), // + pausePeriods("20 January 2023", "25 January 2023", false) // + ); }); } @@ -217,13 +222,22 @@ public class DelinquencyActionIntegrationTests extends BaseLoanIntegrationTest { }); } - private void validateLoanDelinquencyPausePerios(Long loanId, String expectedStart, String expectedEnd, Boolean expectedPauseState) { + private void validateLoanDelinquencyPausePeriods(Long loanId, GetLoansLoanIdDelinquencyPausePeriod... pausePeriods) { GetLoansLoanIdResponse loan = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue()); Assertions.assertNotNull(loan.getDelinquent()); - Assertions.assertEquals(expectedPauseState, loan.getDelinquent().getDelinquencyCalculationPaused()); - Assertions.assertEquals(LocalDate.parse(expectedStart, dateTimeFormatter), - loan.getDelinquent().getDelinquencyPausePeriodStartDate()); - Assertions.assertEquals(LocalDate.parse(expectedEnd, dateTimeFormatter), loan.getDelinquent().getDelinquencyPausePeriodEndDate()); + if (pausePeriods.length > 0) { + Assertions.assertEquals(Arrays.asList(pausePeriods), loan.getDelinquent().getDelinquencyPausePeriods()); + } else { + Assertions.assertNull(loan.getDelinquent().getDelinquencyPausePeriods()); + } + } + + private GetLoansLoanIdDelinquencyPausePeriod pausePeriods(String startDate, String endDate, boolean active) { + GetLoansLoanIdDelinquencyPausePeriod pausePeriod = new GetLoansLoanIdDelinquencyPausePeriod(); + pausePeriod.setActive(active); + pausePeriod.setPausePeriodStart(LocalDate.parse(startDate, dateTimeFormatter)); + pausePeriod.setPausePeriodEnd(LocalDate.parse(endDate, dateTimeFormatter)); + return pausePeriod; } private Long createLoanProductWith25PctDownPayment(boolean autoDownPaymentEnabled, boolean multiDisburseEnabled) { diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ExternalEventConfigurationHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ExternalEventConfigurationHelper.java index b3c7ad60b..29bdab718 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ExternalEventConfigurationHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ExternalEventConfigurationHelper.java @@ -490,6 +490,11 @@ public class ExternalEventConfigurationHelper { loanTransactionDownPaymentPostBusinessEvent.put("enabled", false); defaults.add(loanTransactionDownPaymentPostBusinessEvent); + Map loanAccountDelinquencyPauseChangedBusinessEvent = new HashMap<>(); + loanAccountDelinquencyPauseChangedBusinessEvent.put("type", "LoanAccountDelinquencyPauseChangedBusinessEvent"); + loanAccountDelinquencyPauseChangedBusinessEvent.put("enabled", false); + defaults.add(loanAccountDelinquencyPauseChangedBusinessEvent); + return defaults; }