diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/loan/LoanRepaymentPastDueDataMapper.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/loan/LoanRepaymentPastDueDataMapper.java new file mode 100644 index 000000000..d8af82dda --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/loan/LoanRepaymentPastDueDataMapper.java @@ -0,0 +1,30 @@ +/** + * 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.RepaymentPastDueDataV1; +import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.support.AvroMapperConfig; +import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentPastDueData; +import org.mapstruct.Mapper; + +@Mapper(config = AvroMapperConfig.class) +public interface LoanRepaymentPastDueDataMapper { + + RepaymentPastDueDataV1 map(LoanRepaymentPastDueData source); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializer.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializer.java index d196e609c..384b0172f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializer.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializer.java @@ -28,11 +28,14 @@ import org.apache.fineract.avro.loan.v1.RepaymentDueDataV1; import org.apache.fineract.avro.loan.v1.RepaymentPastDueDataV1; import org.apache.fineract.infrastructure.event.business.domain.BusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.loan.repayment.LoanRepaymentBusinessEvent; +import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.loan.LoanRepaymentPastDueDataMapper; import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.support.AvroDateTimeMapper; import org.apache.fineract.infrastructure.event.external.service.serialization.serializer.AbstractBusinessEventSerializer; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentPastDueData; import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; +import org.apache.fineract.portfolio.loanaccount.service.LoanCalculateRepaymentPastDueService; import org.springframework.stereotype.Component; @Component @@ -40,6 +43,8 @@ import org.springframework.stereotype.Component; public class LoanRepaymentBusinessEventSerializer extends AbstractBusinessEventSerializer { private final AvroDateTimeMapper dataTimeMapper; + private final LoanRepaymentPastDueDataMapper pastDueDataMapper; + private final LoanCalculateRepaymentPastDueService pastDueService; @Override protected ByteBufferSerializable toAvroDTO(BusinessEvent rawEvent) { @@ -55,6 +60,18 @@ public class LoanRepaymentBusinessEventSerializer extends AbstractBusinessEventS CurrencyDataV1 currency = CurrencyDataV1.newBuilder().setCode(loanCurrency.getCode()) .setDecimalPlaces(loanCurrency.getDigitsAfterDecimal()).setInMultiplesOf(loanCurrency.getCurrencyInMultiplesOf()).build(); + RepaymentDueDataV1 repaymentDue = getRepaymentDueData(repaymentInstallment, loanCurrency); + + LoanRepaymentPastDueData pastDueData = pastDueService.retrieveLoanRepaymentPastDueAmountTillDate(loan); + + RepaymentPastDueDataV1 pastDue = pastDueDataMapper.map(pastDueData); + + LoanRepaymentDueDataV1 loanRepaymentDueDataV1 = LoanRepaymentDueDataV1.newBuilder().setLoanId(id).setLoanAccountNo(accountNo) + .setLoanExternalId(externalId).setCurrency(currency).setInstallment(repaymentDue).setPastDueAmount(pastDue).build(); + return loanRepaymentDueDataV1; + } + + private RepaymentDueDataV1 getRepaymentDueData(LoanRepaymentScheduleInstallment repaymentInstallment, MonetaryCurrency loanCurrency) { Integer installmentNumber = repaymentInstallment.getInstallmentNumber(); String dueDate = dataTimeMapper.mapLocalDate(repaymentInstallment.getDueDate()); BigDecimal principalAmountDue = repaymentInstallment.getPrincipalOutstanding(loanCurrency).getAmount(); @@ -65,13 +82,7 @@ public class LoanRepaymentBusinessEventSerializer extends AbstractBusinessEventS RepaymentDueDataV1 repaymentDue = new RepaymentDueDataV1(installmentNumber, dueDate, principalAmountDue, interestAmountDue, feeChargeAmountDue, penaltyChargeAmountDue, totalAmountDue); - - RepaymentPastDueDataV1 pastDue = new RepaymentPastDueDataV1(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, - BigDecimal.ZERO); - - LoanRepaymentDueDataV1 loanRepaymentDueDataV1 = LoanRepaymentDueDataV1.newBuilder().setLoanId(id).setLoanAccountNo(accountNo) - .setLoanExternalId(externalId).setCurrency(currency).setInstallment(repaymentDue).setPastDueAmount(pastDue).build(); - return loanRepaymentDueDataV1; + return repaymentDue; } @Override diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanRepaymentPastDueData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanRepaymentPastDueData.java new file mode 100644 index 000000000..68d07db23 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanRepaymentPastDueData.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.portfolio.loanaccount.data; + +import java.math.BigDecimal; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class LoanRepaymentPastDueData { + + private final BigDecimal totalAmount; + private final BigDecimal principalAmount; + private final BigDecimal interestAmount; + private final BigDecimal feeAmount; + private final BigDecimal penaltyAmount; + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanCalculateRepaymentPastDueService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanCalculateRepaymentPastDueService.java new file mode 100644 index 000000000..15cf05d8a --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanCalculateRepaymentPastDueService.java @@ -0,0 +1,72 @@ +/** + * 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.service; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.List; +import java.util.stream.Collectors; +import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentPastDueData; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; +import org.springframework.stereotype.Component; + +@Component +public class LoanCalculateRepaymentPastDueService { + + public LoanRepaymentPastDueData retrieveLoanRepaymentPastDueAmountTillDate(Loan loan) { + List pastDueRepayments = getPastDueRepayments(loan); + MonetaryCurrency loanCurrency = loan.getCurrency(); + LoanRepaymentPastDueData pastDueData = calculatePastDueAmountsForRepayments(pastDueRepayments, loanCurrency); + return pastDueData; + } + + private LoanRepaymentPastDueData calculatePastDueAmountsForRepayments(List pastDueRepayments, + MonetaryCurrency currency) { + BigDecimal totalAmount = BigDecimal.ZERO; + BigDecimal principalAmount = BigDecimal.ZERO; + BigDecimal interestAmount = BigDecimal.ZERO; + BigDecimal feeAmount = BigDecimal.ZERO; + BigDecimal penaltyAmount = BigDecimal.ZERO; + if (!pastDueRepayments.isEmpty()) { + totalAmount = pastDueRepayments.stream().map(repayment -> repayment.getTotalOutstanding(currency).getAmount()) + .reduce(BigDecimal.ZERO, BigDecimal::add); + principalAmount = pastDueRepayments.stream().map(repayment -> repayment.getPrincipalOutstanding(currency).getAmount()) + .reduce(BigDecimal.ZERO, BigDecimal::add); + interestAmount = pastDueRepayments.stream().map(repayment -> repayment.getInterestOutstanding(currency).getAmount()) + .reduce(BigDecimal.ZERO, BigDecimal::add); + feeAmount = pastDueRepayments.stream().map(repayment -> repayment.getFeeChargesOutstanding(currency).getAmount()) + .reduce(BigDecimal.ZERO, BigDecimal::add); + penaltyAmount = pastDueRepayments.stream().map(repayment -> repayment.getPenaltyChargesOutstanding(currency).getAmount()) + .reduce(BigDecimal.ZERO, BigDecimal::add); + } + return new LoanRepaymentPastDueData(totalAmount, principalAmount, interestAmount, feeAmount, penaltyAmount); + } + + private List getPastDueRepayments(Loan loan) { + List loanRepayments = loan.getRepaymentScheduleInstallments(); + LocalDate currentBusinessDate = DateUtils.getBusinessLocalDate(); + return loanRepayments.stream() + .filter((repayment) -> (!repayment.isObligationsMet() + && (repayment.getDueDate().isBefore(currentBusinessDate) || repayment.getDueDate().isEqual(currentBusinessDate)))) + .collect(Collectors.toList()); + } +} diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializerTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializerTest.java index 4fddeac00..c78c714d1 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializerTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializerTest.java @@ -44,13 +44,16 @@ import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.apache.fineract.infrastructure.event.business.domain.loan.repayment.LoanRepaymentDueBusinessEvent; +import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.loan.LoanRepaymentPastDueDataMapper; import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.support.AvroDateTimeMapper; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MoneyHelper; import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.apache.fineract.portfolio.loanaccount.domain.LoanSummary; +import org.apache.fineract.portfolio.loanaccount.service.LoanCalculateRepaymentPastDueService; import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -68,17 +71,32 @@ public class LoanRepaymentBusinessEventSerializerTest { @Mock private AvroDateTimeMapper mapper; + @Mock + private LoanRepaymentPastDueDataMapper pastDueDataMapper; + + @Mock + private LoanCalculateRepaymentPastDueService pastDueService; + + private MockedStatic moneyHelper = Mockito.mockStatic(MoneyHelper.class); + @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())))); + moneyHelper.when(() -> MoneyHelper.getRoundingMode()).thenReturn(RoundingMode.UP); + } + + @AfterEach + public void reset() { + moneyHelper.close(); } @Test public void testLoanRepaymentEventPayloadSerialization() throws IOException { // given - LoanRepaymentBusinessEventSerializer serializer = new LoanRepaymentBusinessEventSerializer(mapper); + LoanRepaymentBusinessEventSerializer serializer = new LoanRepaymentBusinessEventSerializer(mapper, pastDueDataMapper, + pastDueService); LocalDate loanInstallmentRepaymentDueDate = DateUtils.getBusinessLocalDate().plusDays(1); @@ -86,13 +104,15 @@ public class LoanRepaymentBusinessEventSerializerTest { LoanProduct loanProduct = Mockito.mock(LoanProduct.class); LoanSummary loanSummary = Mockito.mock(LoanSummary.class); MonetaryCurrency loanCurrency = Mockito.mock(MonetaryCurrency.class); - MockedStatic moneyHelper = Mockito.mockStatic(MoneyHelper.class); LoanRepaymentScheduleInstallment repaymentInstallment = new LoanRepaymentScheduleInstallment(loanForProcessing, 1, LocalDate.now(ZoneId.systemDefault()), loanInstallmentRepaymentDueDate, BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), false, new HashSet<>(), BigDecimal.valueOf(0.0)); LoanRepaymentDueBusinessEvent event = new LoanRepaymentDueBusinessEvent(repaymentInstallment); + RepaymentPastDueDataV1 pastDueAmount = new RepaymentPastDueDataV1(BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), + BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0)); + when(loanForProcessing.getId()).thenReturn(1L); when(loanForProcessing.getAccountNumber()).thenReturn("0001"); when(loanForProcessing.getExternalId()).thenReturn(ExternalIdFactory.produce("externalId")); @@ -103,7 +123,8 @@ public class LoanRepaymentBusinessEventSerializerTest { when(loanCurrency.getCurrencyInMultiplesOf()).thenReturn(1); when(loanCurrency.getDigitsAfterDecimal()).thenReturn(1); when(mapper.mapLocalDate(any())).thenReturn(loanInstallmentRepaymentDueDate.format(DateTimeFormatter.ISO_DATE)); - moneyHelper.when(() -> MoneyHelper.getRoundingMode()).thenReturn(RoundingMode.UP); + when(pastDueDataMapper.map(any())).thenReturn(pastDueAmount); + when(pastDueService.retrieveLoanRepaymentPastDueAmountTillDate(loanForProcessing)).thenReturn(null); // when LoanRepaymentDueDataV1 data = (LoanRepaymentDueDataV1) serializer.toAvroDTO(event); @@ -119,13 +140,14 @@ public class LoanRepaymentBusinessEventSerializerTest { pastDue); assertEquals(data, expectedSerializedData); - moneyHelper.close(); + } @Test public void testLoanRepaymentEventLoanIdMandatoryFieldValidation() { // given - LoanRepaymentBusinessEventSerializer serializer = new LoanRepaymentBusinessEventSerializer(mapper); + LoanRepaymentBusinessEventSerializer serializer = new LoanRepaymentBusinessEventSerializer(mapper, pastDueDataMapper, + pastDueService); LocalDate loanInstallmentRepaymentDueDate = DateUtils.getBusinessLocalDate().plusDays(1); @@ -133,13 +155,15 @@ public class LoanRepaymentBusinessEventSerializerTest { LoanProduct loanProduct = Mockito.mock(LoanProduct.class); LoanSummary loanSummary = Mockito.mock(LoanSummary.class); MonetaryCurrency loanCurrency = Mockito.mock(MonetaryCurrency.class); - MockedStatic moneyHelper = Mockito.mockStatic(MoneyHelper.class); LoanRepaymentScheduleInstallment repaymentInstallment = new LoanRepaymentScheduleInstallment(loanForProcessing, 1, LocalDate.now(ZoneId.systemDefault()), loanInstallmentRepaymentDueDate, BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), false, new HashSet<>(), BigDecimal.valueOf(0.0)); LoanRepaymentDueBusinessEvent event = new LoanRepaymentDueBusinessEvent(repaymentInstallment); + RepaymentPastDueDataV1 pastDueAmount = new RepaymentPastDueDataV1(BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), + BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0)); + // set mandatory loanID field as null when(loanForProcessing.getId()).thenReturn(null); when(loanForProcessing.getAccountNumber()).thenReturn("0001"); @@ -151,11 +175,12 @@ public class LoanRepaymentBusinessEventSerializerTest { when(loanCurrency.getCurrencyInMultiplesOf()).thenReturn(1); when(loanCurrency.getDigitsAfterDecimal()).thenReturn(1); when(mapper.mapLocalDate(any())).thenReturn(loanInstallmentRepaymentDueDate.format(DateTimeFormatter.ISO_DATE)); - moneyHelper.when(() -> MoneyHelper.getRoundingMode()).thenReturn(RoundingMode.UP); + when(pastDueDataMapper.map(any())).thenReturn(pastDueAmount); + when(pastDueService.retrieveLoanRepaymentPastDueAmountTillDate(loanForProcessing)).thenReturn(null); // when AvroRuntimeException exceptionThrown = assertThrows(AvroRuntimeException.class, () -> serializer.toAvroDTO(event)); assertTrue(exceptionThrown.getMessage().contains("does not accept null values")); - moneyHelper.close(); } + } diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanCalculateRepaymentPastDueServiceTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanCalculateRepaymentPastDueServiceTest.java new file mode 100644 index 000000000..4270d19c4 --- /dev/null +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanCalculateRepaymentPastDueServiceTest.java @@ -0,0 +1,124 @@ +/** + * 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.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; +import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; +import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; +import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.organisation.monetary.domain.MoneyHelper; +import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentPastDueData; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +public class LoanCalculateRepaymentPastDueServiceTest { + + private LoanCalculateRepaymentPastDueService underTest; + private MockedStatic moneyHelper = Mockito.mockStatic(MoneyHelper.class); + + @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 LoanCalculateRepaymentPastDueService(); + moneyHelper.when(() -> MoneyHelper.getRoundingMode()).thenReturn(RoundingMode.UP); + } + + @AfterEach + public void reset() { + moneyHelper.close(); + } + + @Test + public void testLoanRepaymentPastDueAmountCalculations() { + // given + LocalDate businessDate = DateUtils.getBusinessLocalDate(); + Loan loanForProcessing = Mockito.mock(Loan.class); + MonetaryCurrency loanCurrency = Mockito.mock(MonetaryCurrency.class); + // repayments + + // closed repayment + LocalDate repayment1DueDate = businessDate.minusDays(5); + LoanRepaymentScheduleInstallment repaymentInstallment_closed = new LoanRepaymentScheduleInstallment(loanForProcessing, 1, + LocalDate.now(ZoneId.systemDefault()), repayment1DueDate, BigDecimal.valueOf(100.0), BigDecimal.valueOf(10.0), + BigDecimal.valueOf(10.0), BigDecimal.valueOf(10.0), false, new HashSet<>(), BigDecimal.valueOf(0.0)); + repaymentInstallment_closed.updateObligationMet(true); + + // past due repayments + LocalDate repayment2DueDate = businessDate.minusDays(4); + LoanRepaymentScheduleInstallment repaymentInstallment_1 = new LoanRepaymentScheduleInstallment(loanForProcessing, 2, + LocalDate.now(ZoneId.systemDefault()), repayment2DueDate, BigDecimal.valueOf(100.0), BigDecimal.valueOf(10.0), + BigDecimal.valueOf(10.0), BigDecimal.valueOf(10.0), false, new HashSet<>(), BigDecimal.valueOf(0.0)); + + LocalDate repayment3DueDate = DateUtils.getBusinessLocalDate().minusDays(3); + LoanRepaymentScheduleInstallment repaymentInstallment_2 = new LoanRepaymentScheduleInstallment(loanForProcessing, 3, + LocalDate.now(ZoneId.systemDefault()), repayment3DueDate, BigDecimal.valueOf(100.0), BigDecimal.valueOf(10.0), + BigDecimal.valueOf(10.0), BigDecimal.valueOf(10.0), false, new HashSet<>(), BigDecimal.valueOf(0.0)); + + // upcoming repayment + LocalDate repayment4DueDate = businessDate.plusDays(1); + LoanRepaymentScheduleInstallment repaymentInstallment_upcoming = new LoanRepaymentScheduleInstallment(loanForProcessing, 4, + LocalDate.now(ZoneId.systemDefault()), repayment4DueDate, BigDecimal.valueOf(100.0), BigDecimal.valueOf(10.0), + BigDecimal.valueOf(10.0), BigDecimal.valueOf(10.0), false, new HashSet<>(), BigDecimal.valueOf(0.0)); + + List loanRepayments = Arrays.asList(repaymentInstallment_closed, repaymentInstallment_1, + repaymentInstallment_2, repaymentInstallment_upcoming); + when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepayments); + when(loanForProcessing.getCurrency()).thenReturn(loanCurrency); + when(loanCurrency.getCode()).thenReturn("CODE"); + when(loanCurrency.getCurrencyInMultiplesOf()).thenReturn(1); + when(loanCurrency.getDigitsAfterDecimal()).thenReturn(1); + + // when + LoanRepaymentPastDueData pastDueAmount = underTest.retrieveLoanRepaymentPastDueAmountTillDate(loanForProcessing); + + // then + assertEquals(pastDueAmount.getTotalAmount(), BigDecimal.valueOf(260.00)); + assertEquals(pastDueAmount.getPrincipalAmount(), BigDecimal.valueOf(200.00)); + assertEquals(pastDueAmount.getInterestAmount(), BigDecimal.valueOf(20.00)); + assertEquals(pastDueAmount.getFeeAmount(), BigDecimal.valueOf(20.00)); + assertEquals(pastDueAmount.getPenaltyAmount(), BigDecimal.valueOf(20.00)); + + } +}