From adcd76154eb957bc0cb6f0d740481803ec26940c Mon Sep 17 00:00:00 2001 From: Janos Haber Date: Fri, 16 Dec 2022 12:27:54 +0100 Subject: [PATCH] FINERACT-1734 - Loan Account Delinquency Changed event definition Extend current Loan Account Delinquency range change event. --- .../v1/LoanAccountDelinquencyRangeDataV1.avsc | 38 ++-- .../main/avro/loan/v1/LoanAmountDataV1.avsc | 47 +++++ .../loan/v1/LoanChargeDataRangeViewV1.avsc | 23 +++ .../mapper/generic/CurrencyDataMapper.java | 31 +++ .../mapper/loan/LoanChargeDataMapper.java | 3 + ...ncyRangeChangeBusinessEventSerializer.java | 55 +++++- ...ntDelinquencyRangeEventSerializerTest.java | 178 ++++++++++++++++++ 7 files changed, 359 insertions(+), 16 deletions(-) create mode 100644 fineract-avro-schemas/src/main/avro/loan/v1/LoanAmountDataV1.avsc create mode 100644 fineract-avro-schemas/src/main/avro/loan/v1/LoanChargeDataRangeViewV1.avsc create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/generic/CurrencyDataMapper.java create mode 100644 fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAccountDelinquencyRangeEventSerializerTest.java diff --git a/fineract-avro-schemas/src/main/avro/loan/v1/LoanAccountDelinquencyRangeDataV1.avsc b/fineract-avro-schemas/src/main/avro/loan/v1/LoanAccountDelinquencyRangeDataV1.avsc index 048526c34..b9bb2dbf1 100644 --- a/fineract-avro-schemas/src/main/avro/loan/v1/LoanAccountDelinquencyRangeDataV1.avsc +++ b/fineract-avro-schemas/src/main/avro/loan/v1/LoanAccountDelinquencyRangeDataV1.avsc @@ -4,24 +4,16 @@ "type": "record", "fields": [ { - "default": null, - "name": "id", - "type": [ - "null", - "long" - ] + "name": "loanId", + "type": "long" + }, + { + "name": "loanAccountNo", + "type": "string" }, { "default": null, - "name": "accountNo", - "type": [ - "null", - "string" - ] - }, - { - "default": null, - "name": "externalId", + "name": "loanExternalId", "type": [ "null", "string" @@ -34,6 +26,22 @@ "null", "org.apache.fineract.avro.loan.v1.DelinquencyRangeDataV1" ] + }, + { + "name": "charges", + "type": { + "type": "array", + "items": "org.apache.fineract.avro.loan.v1.LoanChargeDataRangeViewV1" + } + }, + { + "name": "currency", + "type": "org.apache.fineract.avro.generic.v1.CurrencyDataV1" + }, + { + "name": "amount", + "doc": "Contains installments total, fee, interest, principal and penalty amount summaries", + "type": "org.apache.fineract.avro.loan.v1.LoanAmountDataV1" } ] } diff --git a/fineract-avro-schemas/src/main/avro/loan/v1/LoanAmountDataV1.avsc b/fineract-avro-schemas/src/main/avro/loan/v1/LoanAmountDataV1.avsc new file mode 100644 index 000000000..584f24dd0 --- /dev/null +++ b/fineract-avro-schemas/src/main/avro/loan/v1/LoanAmountDataV1.avsc @@ -0,0 +1,47 @@ +{ + "name": "LoanAmountDataV1", + "namespace": "org.apache.fineract.avro.loan.v1", + "type": "record", + "fields": [ + { + "default": null, + "name": "totalAmount", + "type": [ + "null", + "bigdecimal" + ] + }, + { + "default": null, + "name": "principalAmount", + "type": [ + "null", + "bigdecimal" + ] + }, + { + "default": null, + "name": "interestAmount", + "type": [ + "null", + "bigdecimal" + ] + }, + { + "default": null, + "name": "feeAmount", + "type": [ + "null", + "bigdecimal" + ] + }, + { + "default": null, + "name": "penaltyAmount", + "type": [ + "null", + "bigdecimal" + ] + } + ] +} diff --git a/fineract-avro-schemas/src/main/avro/loan/v1/LoanChargeDataRangeViewV1.avsc b/fineract-avro-schemas/src/main/avro/loan/v1/LoanChargeDataRangeViewV1.avsc new file mode 100644 index 000000000..ebb747c76 --- /dev/null +++ b/fineract-avro-schemas/src/main/avro/loan/v1/LoanChargeDataRangeViewV1.avsc @@ -0,0 +1,23 @@ +{ + "name": "LoanChargeDataRangeViewV1", + "namespace": "org.apache.fineract.avro.loan.v1", + "type": "record", + "fields": [ + { + "name": "id", + "type": "long" + }, + { + "name": "name", + "type": "string" + }, + { + "name": "currency", + "type": ["org.apache.fineract.avro.generic.v1.CurrencyDataV1"] + }, + { + "name": "amount", + "type": "bigdecimal" + } + ] +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/generic/CurrencyDataMapper.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/generic/CurrencyDataMapper.java new file mode 100644 index 000000000..4195640d7 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/generic/CurrencyDataMapper.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.generic; + +import org.apache.fineract.avro.generic.v1.CurrencyDataV1; +import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.support.AvroMapperConfig; +import org.apache.fineract.organisation.monetary.data.CurrencyData; +import org.mapstruct.Mapper; + +@Mapper(config = AvroMapperConfig.class) +public interface CurrencyDataMapper { + + CurrencyDataV1 map(CurrencyData source); + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/loan/LoanChargeDataMapper.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/loan/LoanChargeDataMapper.java index b000dde1a..0a7ae2710 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/loan/LoanChargeDataMapper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/loan/LoanChargeDataMapper.java @@ -18,6 +18,7 @@ */ package org.apache.fineract.infrastructure.event.external.service.serialization.mapper.loan; +import org.apache.fineract.avro.loan.v1.LoanChargeDataRangeViewV1; import org.apache.fineract.avro.loan.v1.LoanChargeDataV1; import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.support.AvroMapperConfig; import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData; @@ -27,4 +28,6 @@ import org.mapstruct.Mapper; public interface LoanChargeDataMapper { LoanChargeDataV1 map(LoanChargeData source); + + LoanChargeDataRangeViewV1 mapRangeView(LoanChargeData source); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanDelinquencyRangeChangeBusinessEventSerializer.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanDelinquencyRangeChangeBusinessEventSerializer.java index a7e4879fc..f73b16b6c 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanDelinquencyRangeChangeBusinessEventSerializer.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanDelinquencyRangeChangeBusinessEventSerializer.java @@ -18,16 +18,27 @@ */ package org.apache.fineract.infrastructure.event.external.service.serialization.serializer.loan; +import java.math.BigDecimal; +import java.util.List; +import java.util.function.BiFunction; import lombok.RequiredArgsConstructor; import org.apache.avro.generic.GenericContainer; import org.apache.fineract.avro.generator.ByteBufferSerializable; import org.apache.fineract.avro.loan.v1.DelinquencyRangeDataV1; import org.apache.fineract.avro.loan.v1.LoanAccountDelinquencyRangeDataV1; +import org.apache.fineract.avro.loan.v1.LoanAmountDataV1; +import org.apache.fineract.avro.loan.v1.LoanChargeDataRangeViewV1; import org.apache.fineract.infrastructure.event.business.domain.BusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.loan.LoanDelinquencyRangeChangeBusinessEvent; +import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.generic.CurrencyDataMapper; +import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.loan.LoanChargeDataMapper; import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.loan.LoanDelinquencyRangeDataMapper; 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.LoanAccountData; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanaccount.domain.LoanInstallmentCharge; +import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; @@ -39,8 +50,13 @@ import org.springframework.stereotype.Component; public class LoanDelinquencyRangeChangeBusinessEventSerializer extends AbstractBusinessEventSerializer { private final LoanReadPlatformService service; + private final LoanDelinquencyRangeDataMapper mapper; + private final LoanChargeDataMapper chargeMapper; + + private final CurrencyDataMapper currencyMapper; + @Override protected ByteBufferSerializable toAvroDTO(BusinessEvent rawEvent) { LoanDelinquencyRangeChangeBusinessEvent event = (LoanDelinquencyRangeChangeBusinessEvent) rawEvent; @@ -48,8 +64,45 @@ public class LoanDelinquencyRangeChangeBusinessEventSerializer extends AbstractB Long id = data.getId(); String accountNumber = data.getAccountNo(); String externalId = data.getExternalId().getValue(); + MonetaryCurrency loanCurrency = event.get().getCurrency(); + List charges = event// + .get()// + .getRepaymentScheduleInstallments()// + .stream()// + .flatMap(installment -> installment.getInstallmentCharges().stream())// + .map(LoanInstallmentCharge::getLoancharge)// + .map(charge -> chargeMapper.mapRangeView(charge.toData()))// + .toList(); + LoanAmountDataV1 amount = LoanAmountDataV1.newBuilder()// + .setPrincipalAmount(calculateDataSummary(event.get(), + (loan, installment) -> installment.getPrincipalOutstanding(loanCurrency).getAmount()))// + .setFeeAmount(calculateDataSummary(event.get(), + (loan, installment) -> installment.getFeeChargesOutstanding(loanCurrency).getAmount()))// + .setInterestAmount(calculateDataSummary(event.get(), + (loan, installment) -> installment.getInterestOutstanding(loanCurrency).getAmount()))// + .setPenaltyAmount(calculateDataSummary(event.get(), + (loan, installment) -> installment.getPenaltyChargesOutstanding(loanCurrency).getAmount()))// + .setTotalAmount( + calculateDataSummary(event.get(), (loan, installment) -> installment.getTotalOutstanding(loanCurrency).getAmount()))// + .build(); + DelinquencyRangeDataV1 delinquencyRange = mapper.map(data.getDelinquencyRange()); - return new LoanAccountDelinquencyRangeDataV1(id, accountNumber, externalId, delinquencyRange); + LoanAccountDelinquencyRangeDataV1.Builder builder = LoanAccountDelinquencyRangeDataV1.newBuilder(); + return builder// + .setLoanId(id)// + .setLoanAccountNo(accountNumber)// + .setLoanExternalId(externalId)// + .setDelinquencyRange(delinquencyRange)// + .setCharges(charges)// + .setAmount(amount)// + .setCurrency(currencyMapper.map(data.getCurrency()))// + .build(); + } + + private BigDecimal calculateDataSummary(Loan loan, BiFunction mapper) { + return loan.getRepaymentScheduleInstallments().stream().map(installment -> mapper.apply(loan, installment)).reduce(BigDecimal.ZERO, + BigDecimal::add); + } @Override diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAccountDelinquencyRangeEventSerializerTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAccountDelinquencyRangeEventSerializerTest.java new file mode 100644 index 000000000..e9e8fcbe9 --- /dev/null +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAccountDelinquencyRangeEventSerializerTest.java @@ -0,0 +1,178 @@ +/** + * 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.serializer.loan; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import org.apache.fineract.avro.loan.v1.LoanAccountDelinquencyRangeDataV1; +import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; +import org.apache.fineract.infrastructure.core.domain.ExternalId; +import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; +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.LoanDelinquencyRangeChangeBusinessEvent; +import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.generic.CurrencyDataMapperImpl; +import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.loan.LoanChargeDataMapperImpl; +import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.loan.LoanDelinquencyRangeDataMapperImpl; +import org.apache.fineract.organisation.monetary.data.CurrencyData; +import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.organisation.monetary.domain.Money; +import org.apache.fineract.organisation.monetary.domain.MoneyHelper; +import org.apache.fineract.portfolio.charge.data.ChargeData; +import org.apache.fineract.portfolio.charge.domain.Charge; +import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType; +import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode; +import org.apache.fineract.portfolio.charge.domain.ChargeTimeType; +import org.apache.fineract.portfolio.delinquency.data.DelinquencyRangeData; +import org.apache.fineract.portfolio.loanaccount.data.LoanAccountData; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; +import org.apache.fineract.portfolio.loanaccount.domain.LoanInstallmentCharge; +import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; +import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService; +import org.junit.jupiter.api.Assertions; +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.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; + +@ExtendWith(MockitoExtension.class) +public class LoanAccountDelinquencyRangeEventSerializerTest { + + @Mock + private LoanReadPlatformService loanReadPlatformService; + + @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())))); + } + + @Test + public void testLoanRepaymentEventPayloadSerialization() throws IOException { + // given + LoanDelinquencyRangeChangeBusinessEventSerializer serializer = new LoanDelinquencyRangeChangeBusinessEventSerializer( + loanReadPlatformService, new LoanDelinquencyRangeDataMapperImpl(), new LoanChargeDataMapperImpl(), + new CurrencyDataMapperImpl()); + + Loan loanForProcessing = Mockito.mock(Loan.class); + LoanAccountData loanAccountData = mock(LoanAccountData.class); + MonetaryCurrency loanCurrency = new MonetaryCurrency("CODE", 1, 1); + MockedStatic moneyHelper = Mockito.mockStatic(MoneyHelper.class); + + when(loanForProcessing.getId()).thenReturn(1L); + when(loanAccountData.getId()).thenReturn(1L); + when(loanAccountData.getAccountNo()).thenReturn("0001"); + when(loanAccountData.getExternalId()).thenReturn(ExternalIdFactory.produce("externalId")); + when(loanAccountData.getDelinquencyRange()).thenReturn(new DelinquencyRangeData(1L, "classification", 1, 10)); + when(loanAccountData.getCurrency()).thenAnswer(a -> new CurrencyData(loanCurrency.getCode(), loanCurrency.getDigitsAfterDecimal(), + loanCurrency.getCurrencyInMultiplesOf())); + when(loanForProcessing.getCurrency()).thenReturn(loanCurrency); + + when(loanReadPlatformService.retrieveOne(any(Long.class))).thenReturn(loanAccountData); + + LoanDelinquencyRangeChangeBusinessEvent event = new LoanDelinquencyRangeChangeBusinessEvent(loanForProcessing); + List repaymentScheduleInstallments = new ArrayList<>(); + + repaymentScheduleInstallments.add(buildInstallment(loanForProcessing, loanCurrency, BigDecimal.valueOf(100), BigDecimal.valueOf(5), + BigDecimal.valueOf(30), BigDecimal.valueOf(50), BigDecimal.valueOf(185), new BigDecimal("100.5"), new BigDecimal("200.3"))); + when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments); + + moneyHelper.when(() -> MoneyHelper.getRoundingMode()).thenReturn(RoundingMode.UP); + + // when + LoanAccountDelinquencyRangeDataV1 data = (LoanAccountDelinquencyRangeDataV1) serializer.toAvroDTO(event); + + // then + Assertions.assertEquals(1L, data.getLoanId()); + Assertions.assertEquals("0001", data.getLoanAccountNo()); + Assertions.assertEquals("externalId", data.getLoanExternalId()); + Assertions.assertEquals(1L, data.getDelinquencyRange().getId()); + Assertions.assertEquals("classification", data.getDelinquencyRange().getClassification()); + Assertions.assertEquals(1, data.getDelinquencyRange().getMinimumAgeDays()); + Assertions.assertEquals(10, data.getDelinquencyRange().getMaximumAgeDays()); + Assertions.assertEquals(2, data.getCharges().size()); + Assertions.assertTrue(io.vavr.collection.List.ofAll(data.getCharges()) + .find(a -> a.getAmount().compareTo(new BigDecimal("100.5")) == 0).isDefined()); + Assertions.assertTrue(io.vavr.collection.List.ofAll(data.getCharges()) + .find(a -> a.getAmount().compareTo(new BigDecimal("200.3")) == 0).isDefined()); + + Assertions.assertEquals(0, data.getAmount().getTotalAmount().compareTo(new BigDecimal("185.0"))); + Assertions.assertEquals(0, data.getAmount().getPrincipalAmount().compareTo(new BigDecimal("100.0"))); + Assertions.assertEquals(0, data.getAmount().getInterestAmount().compareTo(new BigDecimal("30.0"))); + Assertions.assertEquals(0, data.getAmount().getFeeAmount().compareTo(new BigDecimal("5.0"))); + Assertions.assertEquals(0, data.getAmount().getPenaltyAmount().compareTo(new BigDecimal("50.0"))); + + // assertEquals(data, expectedSerializedData); + moneyHelper.close(); + } + + private LoanRepaymentScheduleInstallment buildInstallment(Loan loan, MonetaryCurrency currency, BigDecimal principalAmount, + BigDecimal freeAmount, BigDecimal interestAmount, BigDecimal penaltyAmount, BigDecimal totalAmount, BigDecimal... charges) { + + LoanRepaymentScheduleInstallment installment = mock(LoanRepaymentScheduleInstallment.class); + when(installment.getPrincipalOutstanding(any())).thenAnswer(a -> Money.of(currency, principalAmount)); + when(installment.getInterestOutstanding(any())).thenAnswer(a -> Money.of(currency, interestAmount)); + when(installment.getPenaltyChargesOutstanding(any())).thenAnswer(a -> Money.of(currency, penaltyAmount)); + when(installment.getFeeChargesOutstanding(any())).thenAnswer(a -> Money.of(currency, freeAmount)); + when(installment.getTotalOutstanding(any())).thenAnswer(a -> Money.of(currency, totalAmount)); + Charge charge = mock(Charge.class); + when(charge.toData()).thenAnswer(a -> { + ChargeData chargeData = mock(ChargeData.class); + when(chargeData.getCurrency()).thenAnswer(b -> new CurrencyData(currency.getCode())); + return chargeData; + }); + + Set installmentCharges = Arrays.stream(charges) + .map(amount -> buildLoanInstallmentCharge(amount, charge, loan)).collect(Collectors.toSet()); + when(installment.getInstallmentCharges()).thenReturn(installmentCharges); + return installment; + } + + private LoanInstallmentCharge buildLoanInstallmentCharge(BigDecimal amount, Charge charge, Loan loan) { + LoanInstallmentCharge installmentCharge = new LoanInstallmentCharge(); + ReflectionTestUtils.setField(installmentCharge, "amount", amount); + ReflectionTestUtils.setField(installmentCharge, "loancharge", buildLoanCharge(loan, amount, charge)); + return installmentCharge; + } + + private LoanCharge buildLoanCharge(Loan loan, BigDecimal amount, Charge charge) { + return new LoanCharge(loan, charge, amount, amount, ChargeTimeType.TRANCHE_DISBURSEMENT, ChargeCalculationType.FLAT, + LocalDate.of(2022, 6, 27), ChargePaymentMode.REGULAR, 1, new BigDecimal(100), ExternalId.generate()); + } +}