FINERACT-2107: Interest Refund - Allocation, Business Event & Full Repayment
This commit is contained in:
+12
-13
@@ -76,7 +76,6 @@ import org.apache.fineract.integrationtests.common.accounting.PeriodicAccrualAcc
|
||||
import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInMonthType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.EarlyPaymentLoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.FineractStyleLoanRepaymentScheduleTransactionProcessor;
|
||||
@@ -4426,7 +4425,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
||||
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation()
|
||||
.interestRatePerPeriod(5.0).interestCalculationPeriodType(DAYS).interestRateFrequencyType(YEARS)
|
||||
.daysInMonthType(DaysInMonthType.DAYS_30.getValue()).daysInYearType(DaysInYearType.DAYS_360).numberOfRepayments(5)//
|
||||
.daysInMonthType(DaysInMonthType.DAYS_30).daysInYearType(DaysInYearType.DAYS_360).numberOfRepayments(5)//
|
||||
.repaymentEvery(1)//
|
||||
.repaymentFrequencyType(2L)//
|
||||
.enableDownPayment(true)//
|
||||
@@ -4488,7 +4487,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
||||
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation()
|
||||
.interestRatePerPeriod(12.3).interestCalculationPeriodType(RepaymentFrequencyType.DAYS).interestRateFrequencyType(YEARS)
|
||||
.daysInMonthType(DaysInMonthType.DAYS_30.getValue()).daysInYearType(DaysInYearType.DAYS_360).numberOfRepayments(5)//
|
||||
.daysInMonthType(DaysInMonthType.DAYS_30).daysInYearType(DaysInYearType.DAYS_360).numberOfRepayments(5)//
|
||||
.repaymentEvery(1)//
|
||||
.repaymentFrequencyType(2L)//
|
||||
.enableDownPayment(true)//
|
||||
@@ -4551,7 +4550,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
||||
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation()
|
||||
.interestRatePerPeriod(12.3).interestCalculationPeriodType(RepaymentFrequencyType.DAYS).interestRateFrequencyType(YEARS)
|
||||
.daysInMonthType(DaysInMonthType.DAYS_30.getValue()).daysInYearType(DaysInYearType.DAYS_360).numberOfRepayments(5)//
|
||||
.daysInMonthType(DaysInMonthType.DAYS_30).daysInYearType(DaysInYearType.DAYS_360).numberOfRepayments(5)//
|
||||
.repaymentEvery(1)//
|
||||
.repaymentFrequencyType(2L)//
|
||||
.enableDownPayment(true)//
|
||||
@@ -4614,7 +4613,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
||||
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation()
|
||||
.interestRatePerPeriod(12.3).interestCalculationPeriodType(RepaymentFrequencyType.DAYS).interestRateFrequencyType(YEARS)
|
||||
.daysInMonthType(DaysInMonthType.ACTUAL.getValue()).daysInYearType(DaysInYearType.DAYS_365).numberOfRepayments(4)//
|
||||
.daysInMonthType(DaysInMonthType.ACTUAL).daysInYearType(DaysInYearType.DAYS_365).numberOfRepayments(4)//
|
||||
.repaymentEvery(1)//
|
||||
.repaymentFrequencyType(2L)//
|
||||
.allowPartialPeriodInterestCalcualtion(false)//
|
||||
@@ -4692,8 +4691,8 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
||||
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation()
|
||||
.interestRatePerPeriod(108.0).interestCalculationPeriodType(RepaymentFrequencyType.DAYS)
|
||||
.interestRateFrequencyType(YEARS).daysInMonthType(DaysInMonthType.ACTUAL.getValue())
|
||||
.daysInYearType(DaysInYearType.DAYS_360).numberOfRepayments(4)//
|
||||
.interestRateFrequencyType(YEARS).daysInMonthType(DaysInMonthType.ACTUAL).daysInYearType(DaysInYearType.DAYS_360)
|
||||
.numberOfRepayments(4)//
|
||||
.maxInterestRatePerPeriod((double) 110)//
|
||||
.repaymentEvery(1)//
|
||||
.repaymentFrequencyType(1L)//
|
||||
@@ -4873,7 +4872,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
||||
interestRefundTypes.add("MERCHANT_ISSUED_REFUND");
|
||||
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation()
|
||||
.interestRatePerPeriod(12.0).interestCalculationPeriodType(RepaymentFrequencyType.DAYS).interestRateFrequencyType(YEARS)
|
||||
.daysInMonthType(DaysInMonthType.ACTUAL.getValue()).daysInYearType(DaysInYearType.DAYS_365).numberOfRepayments(4)//
|
||||
.daysInMonthType(DaysInMonthType.ACTUAL).daysInYearType(DaysInYearType.DAYS_365).numberOfRepayments(4)//
|
||||
.repaymentEvery(5)//
|
||||
.repaymentFrequencyType(0L)//
|
||||
.allowPartialPeriodInterestCalcualtion(false)//
|
||||
@@ -4935,8 +4934,8 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
||||
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation()
|
||||
.interestRatePerPeriod(interestRatePerPeriod.doubleValue()).interestCalculationPeriodType(RepaymentFrequencyType.DAYS)
|
||||
.interestRateFrequencyType(YEARS).daysInMonthType(DaysInMonthType.DAYS_30.getValue())
|
||||
.daysInYearType(DaysInYearType.DAYS_360).numberOfRepayments(6)//
|
||||
.interestRateFrequencyType(YEARS).daysInMonthType(DaysInMonthType.DAYS_30).daysInYearType(DaysInYearType.DAYS_360)
|
||||
.numberOfRepayments(6)//
|
||||
.repaymentEvery(1)//
|
||||
.repaymentFrequencyType(2L)//
|
||||
.enableDownPayment(false)//
|
||||
@@ -5017,8 +5016,8 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
||||
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation()
|
||||
.interestRatePerPeriod(interestRatePerPeriod.doubleValue()).interestCalculationPeriodType(RepaymentFrequencyType.DAYS)
|
||||
.interestRateFrequencyType(YEARS).daysInMonthType(DaysInMonthType.DAYS_30.getValue())
|
||||
.daysInYearType(DaysInYearType.DAYS_360).numberOfRepayments(6)//
|
||||
.interestRateFrequencyType(YEARS).daysInMonthType(DaysInMonthType.DAYS_30).daysInYearType(DaysInYearType.DAYS_360)
|
||||
.numberOfRepayments(6)//
|
||||
.repaymentEvery(1)//
|
||||
.repaymentFrequencyType(2L)//
|
||||
.enableDownPayment(false)//
|
||||
@@ -5114,7 +5113,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
||||
.interestRatePerPeriod(interestRatePerPeriod.doubleValue())//
|
||||
.interestCalculationPeriodType(RepaymentFrequencyType.DAYS)//
|
||||
.interestRateFrequencyType(YEARS)//
|
||||
.daysInMonthType(DaysInMonthType.DAYS_30.getValue())//
|
||||
.daysInMonthType(DaysInMonthType.DAYS_30)//
|
||||
.daysInYearType(DaysInYearType.DAYS_360)//
|
||||
.numberOfRepayments(6)//
|
||||
.repaymentEvery(1)//
|
||||
|
||||
+7
@@ -1281,6 +1281,13 @@ public abstract class BaseLoanIntegrationTest {
|
||||
public static final Integer DAYS_365 = 365;
|
||||
}
|
||||
|
||||
public static class DaysInMonthType {
|
||||
|
||||
public static final Integer INVALID = 0;
|
||||
public static final Integer ACTUAL = 1;
|
||||
public static final Integer DAYS_30 = 30;
|
||||
}
|
||||
|
||||
public static class FuturePaymentAllocationRule {
|
||||
|
||||
public static final String LAST_INSTALLMENT = "LAST_INSTALLMENT";
|
||||
|
||||
+164
-50
@@ -25,9 +25,11 @@ import io.restassured.builder.ResponseSpecBuilder;
|
||||
import io.restassured.http.ContentType;
|
||||
import io.restassured.specification.RequestSpecification;
|
||||
import io.restassured.specification.ResponseSpecification;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@@ -35,6 +37,8 @@ import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -42,6 +46,8 @@ import org.apache.fineract.client.models.PostClientsResponse;
|
||||
import org.apache.fineract.client.models.PostCreateRescheduleLoansRequest;
|
||||
import org.apache.fineract.client.models.PostCreateRescheduleLoansResponse;
|
||||
import org.apache.fineract.client.models.PostLoanProductsRequest;
|
||||
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
|
||||
import org.apache.fineract.client.models.PostLoansRequest;
|
||||
import org.apache.fineract.client.models.PostUpdateRescheduleLoansRequest;
|
||||
import org.apache.fineract.infrastructure.event.external.service.validation.ExternalEventDTO;
|
||||
@@ -111,7 +117,7 @@ public class ExternalBusinessEventTest extends BaseLoanIntegrationTest {
|
||||
|
||||
loanTransactionHelper.makeLoanRepayment("15 March 2023", 125.0F, loanIdRef.get().intValue());
|
||||
|
||||
verifyBusinessEvents(new BusinessEvent("LoanBalanceChangedBusinessEvent", "15 March 2023", 300, 400.0, 291.04));
|
||||
verifyBusinessEvents(new LoanBusinessEvent("LoanBalanceChangedBusinessEvent", "15 March 2023", 300, 400.0, 291.04));
|
||||
});
|
||||
runAt("1 April 2023", () -> {
|
||||
|
||||
@@ -123,7 +129,7 @@ public class ExternalBusinessEventTest extends BaseLoanIntegrationTest {
|
||||
|
||||
loanTransactionHelper.makeLoanRepayment("15 April 2023", 125.0F, loanIdRef.get().intValue());
|
||||
|
||||
verifyBusinessEvents(new BusinessEvent("LoanBalanceChangedBusinessEvent", "15 April 2023", 300, 1000.0, 770.06));
|
||||
verifyBusinessEvents(new LoanBusinessEvent("LoanBalanceChangedBusinessEvent", "15 April 2023", 300, 1000.0, 770.06));
|
||||
|
||||
deleteAllExternalEvents();
|
||||
|
||||
@@ -131,24 +137,57 @@ public class ExternalBusinessEventTest extends BaseLoanIntegrationTest {
|
||||
.getResourceId();
|
||||
Assertions.assertNotNull(transactionId);
|
||||
|
||||
verifyBusinessEvents(new BusinessEvent("LoanBalanceChangedBusinessEvent", "15 April 2023", 700, 1000.0, 0.0));
|
||||
verifyBusinessEvents(new LoanBusinessEvent("LoanBalanceChangedBusinessEvent", "15 April 2023", 700, 1000.0, 0.0));
|
||||
|
||||
deleteAllExternalEvents();
|
||||
|
||||
loanTransactionHelper.reverseRepayment(loanIdRef.get().intValue(), transactionId.intValue(), "15 April 2023");
|
||||
|
||||
verifyBusinessEvents(new BusinessEvent("LoanBalanceChangedBusinessEvent", "15 April 2023", 300, 1000.0, 770.06));
|
||||
verifyBusinessEvents(new LoanBusinessEvent("LoanBalanceChangedBusinessEvent", "15 April 2023", 300, 1000.0, 770.06));
|
||||
|
||||
deleteAllExternalEvents();
|
||||
|
||||
loanTransactionHelper.makeLoanRepayment("15 April 2023", 830.22F, loanIdRef.get().intValue());
|
||||
|
||||
verifyBusinessEvents(new BusinessEvent("LoanBalanceChangedBusinessEvent", "15 April 2023", 600, 1000.0, 0.0));
|
||||
verifyBusinessEvents(new LoanBusinessEvent("LoanBalanceChangedBusinessEvent", "15 April 2023", 600, 1000.0, 0.0));
|
||||
|
||||
disableLoanBalanceChangedBusinessEvent();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyInterestRefundPostBusinessEventCreatedForMerchantIssuedRefundWithInterestRefund() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
enableLoanInterestRefundPstBusinessEvent(true);
|
||||
runAt("1 January 2021", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper
|
||||
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
|
||||
.daysInYearType(DaysInYearType.ACTUAL) //
|
||||
.supportedInterestRefundTypes(new ArrayList<>()).addSupportedInterestRefundTypesItem("MERCHANT_ISSUED_REFUND") //
|
||||
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
|
||||
);
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.99,
|
||||
12, null);
|
||||
Assertions.assertNotNull(loanId);
|
||||
loanIdRef.set(loanId);
|
||||
disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
|
||||
});
|
||||
runAt("22 January 2021", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
deleteAllExternalEvents();
|
||||
|
||||
PostLoansLoanIdTransactionsResponse postLoansLoanIdTransactionsResponse = loanTransactionHelper
|
||||
.makeLoanRepayment("MerchantIssuedRefund", "22 January 2021", 1000F, loanId.intValue());
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
|
||||
|
||||
verifyBusinessEvents(new LoanTransactionBusinessEvent("LoanTransactionInterestRefundPostBusinessEvent", "22 January 2021", 5.75,
|
||||
994.25, 5.75, 0.0, 0.0, 0.0));
|
||||
});
|
||||
enableLoanInterestRefundPstBusinessEvent(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExternalBusinessEventLoanRescheduledDueAdjustScheduleBusinessEventInterestChange() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
@@ -171,7 +210,7 @@ public class ExternalBusinessEventTest extends BaseLoanIntegrationTest {
|
||||
loanRescheduleRequestHelper.approveLoanRescheduleRequest(rescheduleLoansResponse.getResourceId(),
|
||||
new PostUpdateRescheduleLoansRequest().approvedOnDate("1 March 2024").locale("en").dateFormat(DATETIME_PATTERN));
|
||||
|
||||
verifyBusinessEvents(new BusinessEvent("LoanRescheduledDueAdjustScheduleBusinessEvent", "01 March 2024", 300, 400.0, 400.0,
|
||||
verifyBusinessEvents(new LoanBusinessEvent("LoanRescheduledDueAdjustScheduleBusinessEvent", "01 March 2024", 300, 400.0, 400.0,
|
||||
List.of("interestRateForInstallment")));
|
||||
});
|
||||
}
|
||||
@@ -260,11 +299,28 @@ public class ExternalBusinessEventTest extends BaseLoanIntegrationTest {
|
||||
|
||||
private void logBusinessEvents(List<ExternalEventDTO> allExternalEvents) {
|
||||
allExternalEvents.forEach(externalEventDTO -> {
|
||||
log.info("Event Received { type:'{}' businessDate:'{}' }", externalEventDTO.getType(), externalEventDTO.getBusinessDate());
|
||||
log.debug(externalEventDTO.toString());
|
||||
Object amount = externalEventDTO.getPayLoad().get("amount");
|
||||
Object outstandingLoanBalance = externalEventDTO.getPayLoad().get("outstandingLoanBalance");
|
||||
Object principalPortion = externalEventDTO.getPayLoad().get("principalPortion");
|
||||
Object interestPortion = externalEventDTO.getPayLoad().get("interestPortion");
|
||||
Object feePortion = externalEventDTO.getPayLoad().get("feeChargesPortion");
|
||||
Object penaltyPortion = externalEventDTO.getPayLoad().get("penaltyChargesPortion");
|
||||
log.info("Event Received\n type:'{}'\n businessDate:'{}'", externalEventDTO.getType(), externalEventDTO.getBusinessDate());
|
||||
log.info(
|
||||
"Values\n amount: {}\n outstandingLoanBalance: {}\n principalPortion: {}\n interestPortion: {}\n feePortion: {}\n penaltyPortion: {}",
|
||||
amount, outstandingLoanBalance, principalPortion, interestPortion, feePortion, penaltyPortion);
|
||||
});
|
||||
}
|
||||
|
||||
private static void enableLoanInterestRefundPstBusinessEvent(boolean enabled) {
|
||||
final Map<String, Boolean> updatedConfigurations = ExternalEventConfigurationHelper.updateExternalEventConfigurations(requestSpec,
|
||||
responseSpec, "{\"externalEventConfigurations\":{\"LoanTransactionInterestRefundPostBusinessEvent\":"
|
||||
+ (enabled ? "true" : "false") + "}}\n");
|
||||
Assertions.assertEquals(updatedConfigurations.size(), 1);
|
||||
Assertions.assertTrue(updatedConfigurations.containsKey("LoanTransactionInterestRefundPostBusinessEvent"));
|
||||
Assertions.assertEquals(enabled, updatedConfigurations.get("LoanTransactionInterestRefundPostBusinessEvent"));
|
||||
}
|
||||
|
||||
public void verifyBusinessEvents(BusinessEvent... businessEvents) {
|
||||
List<ExternalEventDTO> allExternalEvents = ExternalEventHelper.getAllExternalEvents(requestSpec, responseSpec);
|
||||
logBusinessEvents(allExternalEvents);
|
||||
@@ -273,61 +329,119 @@ public class ExternalBusinessEventTest extends BaseLoanIntegrationTest {
|
||||
Assertions.assertTrue(businessEvents.length <= allExternalEvents.size(),
|
||||
"Expected business event count is less than actual. Expected: " + businessEvents.length + " Actual: "
|
||||
+ allExternalEvents.size());
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATETIME_PATTERN, Locale.ENGLISH);
|
||||
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATETIME_PATTERN, Locale.ENGLISH);
|
||||
for (BusinessEvent businessEvent : businessEvents) {
|
||||
long count = allExternalEvents.stream().filter(externalEvent -> {
|
||||
Object summaryRes = externalEvent.getPayLoad().get("summary");
|
||||
Object statusRes = externalEvent.getPayLoad().get("status");
|
||||
Map<String, Object> summary = summaryRes instanceof Map ? (Map<String, Object>) summaryRes : Map.of();
|
||||
Map<String, Object> status = statusRes instanceof Map ? (Map<String, Object>) statusRes : Map.of();
|
||||
var principalDisbursed = summary.get("principalDisbursed");
|
||||
|
||||
var principalOutstanding = summary.get("principalOutstanding");
|
||||
var businessDate = LocalDate.parse(businessEvent.getBusinessDate(), formatter);
|
||||
Double statusId = (Double) status.get("id");
|
||||
return Objects.equals(externalEvent.getType(), businessEvent.getType())
|
||||
&& Objects.equals(externalEvent.getBusinessDate(), businessDate)
|
||||
&& Objects.equals(statusId, businessEvent.getStatusId().doubleValue())
|
||||
&& Objects.equals(principalDisbursed, businessEvent.getPrincipalDisbursed())
|
||||
&& Objects.equals(principalOutstanding, businessEvent.getPrincipalOutstanding())
|
||||
&& loanTermVariationsMatch((List<Map<String, Object>>) externalEvent.getPayLoad().get("loanTermVariations"),
|
||||
businessEvent.loanTermVariationType);
|
||||
}).count();
|
||||
long count = allExternalEvents.stream().filter(externalEvent -> businessEvent.verify(externalEvent, formatter)).count();
|
||||
Assertions.assertEquals(1, count, "Expected business event not found " + businessEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean loanTermVariationsMatch(final List<Map<String, Object>> loanTermVariations, final List<String> expectedTypes) {
|
||||
if (CollectionUtils.isEmpty(expectedTypes)) {
|
||||
return true;
|
||||
}
|
||||
final long numberOfMatches = expectedTypes
|
||||
.stream().filter(
|
||||
expectedType -> loanTermVariations.stream()
|
||||
.anyMatch(variation -> StringUtils
|
||||
.equals((String) ((Map<String, Object>) variation.get("termType")).get("value"), expectedType)))
|
||||
.count();
|
||||
|
||||
return numberOfMatches == expectedTypes.size();
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class BusinessEvent {
|
||||
|
||||
public BusinessEvent(String type, String businessDate, Integer statusId, Double principalDisbursed, Double principalOutstanding) {
|
||||
this.type = type;
|
||||
this.businessDate = businessDate;
|
||||
String type;
|
||||
String businessDate;
|
||||
|
||||
boolean verify(@NotNull ExternalEventDTO externalEvent, DateTimeFormatter formatter) {
|
||||
var businessDate = LocalDate.parse(getBusinessDate(), formatter);
|
||||
|
||||
return Objects.equals(externalEvent.getType(), getType()) && Objects.equals(externalEvent.getBusinessDate(), businessDate);
|
||||
}
|
||||
}
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public static class LoanTransactionBusinessEvent extends BusinessEvent {
|
||||
|
||||
private Double amount;
|
||||
private Double outstandingLoanBalance;
|
||||
private Double principalPortion;
|
||||
private Double interestPortion;
|
||||
private Double feeChargesPortion;
|
||||
private Double penaltyChargesPortion;
|
||||
|
||||
public LoanTransactionBusinessEvent(String type, String businessDate, Double amount, Double outstandingLoanBalance,
|
||||
Double principalPortion, Double interestPortion, Double feeChargesPortion, Double penaltyChargesPortion) {
|
||||
super(type, businessDate);
|
||||
this.amount = amount;
|
||||
this.outstandingLoanBalance = outstandingLoanBalance;
|
||||
this.principalPortion = principalPortion;
|
||||
this.interestPortion = interestPortion;
|
||||
this.feeChargesPortion = feeChargesPortion;
|
||||
this.penaltyChargesPortion = penaltyChargesPortion;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean verify(ExternalEventDTO externalEvent, DateTimeFormatter formatter) {
|
||||
Object amount = externalEvent.getPayLoad().get("amount");
|
||||
Object outstandingLoanBalance = externalEvent.getPayLoad().get("outstandingLoanBalance");
|
||||
Object principalPortion = externalEvent.getPayLoad().get("principalPortion");
|
||||
Object interestPortion = externalEvent.getPayLoad().get("interestPortion");
|
||||
Object feePortion = externalEvent.getPayLoad().get("feeChargesPortion");
|
||||
Object penaltyPortion = externalEvent.getPayLoad().get("penaltyChargesPortion");
|
||||
|
||||
return super.verify(externalEvent, formatter) && Objects.equals(amount, getAmount())
|
||||
&& Objects.equals(outstandingLoanBalance, getOutstandingLoanBalance())
|
||||
&& Objects.equals(principalPortion, getPrincipalPortion()) && Objects.equals(interestPortion, getInterestPortion())
|
||||
&& Objects.equals(feePortion, getFeeChargesPortion()) && Objects.equals(penaltyPortion, getPenaltyChargesPortion());
|
||||
}
|
||||
}
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public static class LoanBusinessEvent extends BusinessEvent {
|
||||
|
||||
private Integer statusId;
|
||||
private Double principalDisbursed;
|
||||
private Double principalOutstanding;
|
||||
private List<String> loanTermVariationType;
|
||||
|
||||
public LoanBusinessEvent(String type, String businessDate, Integer statusId, Double principalDisbursed,
|
||||
Double principalOutstanding) {
|
||||
super(type, businessDate);
|
||||
this.statusId = statusId;
|
||||
this.principalDisbursed = principalDisbursed;
|
||||
this.principalOutstanding = principalOutstanding;
|
||||
}
|
||||
|
||||
private String type;
|
||||
private String businessDate;
|
||||
private Integer statusId;
|
||||
private Double principalDisbursed;
|
||||
private Double principalOutstanding;
|
||||
private List<String> loanTermVariationType;
|
||||
public LoanBusinessEvent(String type, String businessDate, Integer statusId, Double principalDisbursed, Double principalOutstanding,
|
||||
List<String> loanTermVariationType) {
|
||||
super(type, businessDate);
|
||||
this.statusId = statusId;
|
||||
this.principalDisbursed = principalDisbursed;
|
||||
this.principalOutstanding = principalOutstanding;
|
||||
this.loanTermVariationType = loanTermVariationType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(ExternalEventDTO externalEvent, DateTimeFormatter formatter) {
|
||||
Object summaryRes = externalEvent.getPayLoad().get("summary");
|
||||
Object statusRes = externalEvent.getPayLoad().get("status");
|
||||
Map<String, Object> summary = summaryRes instanceof Map ? (Map<String, Object>) summaryRes : Map.of();
|
||||
Map<String, Object> status = statusRes instanceof Map ? (Map<String, Object>) statusRes : Map.of();
|
||||
var principalDisbursed = summary.get("principalDisbursed");
|
||||
|
||||
var principalOutstanding = summary.get("principalOutstanding");
|
||||
Double statusId = (Double) status.get("id");
|
||||
return super.verify(externalEvent, formatter) && Objects.equals(statusId, getStatusId().doubleValue())
|
||||
&& Objects.equals(principalDisbursed, getPrincipalDisbursed())
|
||||
&& Objects.equals(principalOutstanding, getPrincipalOutstanding()) && loanTermVariationsMatch(
|
||||
(List<Map<String, Object>>) externalEvent.getPayLoad().get("loanTermVariations"), loanTermVariationType);
|
||||
}
|
||||
|
||||
private boolean loanTermVariationsMatch(final List<Map<String, Object>> loanTermVariations, final List<String> expectedTypes) {
|
||||
if (CollectionUtils.isEmpty(expectedTypes)) {
|
||||
return true;
|
||||
}
|
||||
final long numberOfMatches = expectedTypes.stream().filter(expectedType -> loanTermVariations.stream().anyMatch(
|
||||
variation -> StringUtils.equals((String) ((Map<String, Object>) variation.get("termType")).get("value"), expectedType)))
|
||||
.count();
|
||||
|
||||
return numberOfMatches == expectedTypes.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+412
@@ -0,0 +1,412 @@
|
||||
/**
|
||||
* 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.integrationtests;
|
||||
|
||||
import io.restassured.builder.RequestSpecBuilder;
|
||||
import io.restassured.builder.ResponseSpecBuilder;
|
||||
import io.restassured.http.ContentType;
|
||||
import io.restassured.specification.RequestSpecification;
|
||||
import io.restassured.specification.ResponseSpecification;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
||||
import org.apache.fineract.client.models.PostClientsResponse;
|
||||
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
|
||||
import org.apache.fineract.integrationtests.common.BusinessStepHelper;
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@Slf4j
|
||||
public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
|
||||
|
||||
private static ResponseSpecification responseSpec;
|
||||
private static RequestSpecification requestSpec;
|
||||
private static LoanTransactionHelper loanTransactionHelper;
|
||||
private static PostClientsResponse client;
|
||||
private static BusinessStepHelper businessStepHelper;
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() {
|
||||
Utils.initializeRESTAssured();
|
||||
requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
|
||||
requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
|
||||
requestSpec.header("Fineract-Platform-TenantId", "default");
|
||||
responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
|
||||
loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec);
|
||||
ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
|
||||
client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyInterestRefundNotCreatedForPayoutRefundWhenTypesAreEmpty() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2021", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper
|
||||
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
|
||||
.daysInYearType(DaysInYearType.ACTUAL) //
|
||||
.supportedInterestRefundTypes(new ArrayList<>()) //
|
||||
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
|
||||
);
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.9,
|
||||
12, null);
|
||||
Assertions.assertNotNull(loanId);
|
||||
loanIdRef.set(loanId);
|
||||
disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
|
||||
});
|
||||
runAt("22 January 2021", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
PostLoansLoanIdTransactionsResponse postLoansLoanIdTransactionsResponse = loanTransactionHelper
|
||||
.makeLoanRepayment("PayoutRefund", "22 January 2021", 1000F, loanId.intValue());
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
|
||||
|
||||
logTransactions(loanId);
|
||||
verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"),
|
||||
transaction(1000.0, "Payout Refund", "22 January 2021"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyInterestRefundNotCreatedForMerchantIssuedRefundWhenTypesAreEmpty() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2021", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper
|
||||
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
|
||||
.daysInYearType(DaysInYearType.ACTUAL) //
|
||||
.supportedInterestRefundTypes(new ArrayList<>())//
|
||||
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
|
||||
);
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.9,
|
||||
12, null);
|
||||
Assertions.assertNotNull(loanId);
|
||||
loanIdRef.set(loanId);
|
||||
disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
|
||||
});
|
||||
runAt("22 January 2021", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
PostLoansLoanIdTransactionsResponse postLoansLoanIdTransactionsResponse = loanTransactionHelper
|
||||
.makeLoanRepayment("MerchantIssuedRefund", "22 January 2021", 1000F, loanId.intValue());
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
|
||||
|
||||
logTransactions(loanId);
|
||||
verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"),
|
||||
transaction(1000.0, "Merchant Issued Refund", "22 January 2021"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyInterestRefundCreatedForPayoutRefund() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2021", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper
|
||||
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
|
||||
.daysInYearType(DaysInYearType.ACTUAL) //
|
||||
.supportedInterestRefundTypes(new ArrayList<>()).addSupportedInterestRefundTypesItem("PAYOUT_REFUND") //
|
||||
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
|
||||
);
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.99,
|
||||
12, null);
|
||||
Assertions.assertNotNull(loanId);
|
||||
loanIdRef.set(loanId);
|
||||
disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
|
||||
});
|
||||
runAt("22 January 2021", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
PostLoansLoanIdTransactionsResponse postLoansLoanIdTransactionsResponse = loanTransactionHelper
|
||||
.makeLoanRepayment("PayoutRefund", "22 January 2021", 1000F, loanId.intValue());
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
|
||||
|
||||
logTransactions(loanId);
|
||||
verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"),
|
||||
transaction(1000.0, "Payout Refund", "22 January 2021"), transaction(5.75, "Accrual", "22 January 2021"),
|
||||
transaction(5.75, "Interest Refund", "22 January 2021"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyInterestRefundCreatedForMerchantIssuedRefund() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2021", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper
|
||||
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
|
||||
.daysInYearType(DaysInYearType.ACTUAL) //
|
||||
.supportedInterestRefundTypes(new ArrayList<>()).addSupportedInterestRefundTypesItem("MERCHANT_ISSUED_REFUND") //
|
||||
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
|
||||
);
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.99,
|
||||
12, null);
|
||||
Assertions.assertNotNull(loanId);
|
||||
loanIdRef.set(loanId);
|
||||
disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
|
||||
});
|
||||
runAt("22 January 2021", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
PostLoansLoanIdTransactionsResponse postLoansLoanIdTransactionsResponse = loanTransactionHelper
|
||||
.makeLoanRepayment("MerchantIssuedRefund", "22 January 2021", 1000F, loanId.intValue());
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
|
||||
|
||||
logTransactions(loanId);
|
||||
verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"),
|
||||
transaction(1000.0, "Merchant Issued Refund", "22 January 2021"), transaction(5.75, "Accrual", "22 January 2021"),
|
||||
transaction(5.75, "Interest Refund", "22 January 2021"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyUC01() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2021", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper
|
||||
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
|
||||
.daysInYearType(DaysInYearType.ACTUAL) //
|
||||
.supportedInterestRefundTypes(new ArrayList<>()).addSupportedInterestRefundTypesItem("PAYOUT_REFUND") //
|
||||
.addSupportedInterestRefundTypesItem("MERCHANT_ISSUED_REFUND") //
|
||||
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
|
||||
);
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.99,
|
||||
12, null);
|
||||
Assertions.assertNotNull(loanId);
|
||||
loanIdRef.set(loanId);
|
||||
disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
|
||||
});
|
||||
runAt("22 January 2021", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
PostLoansLoanIdTransactionsResponse postLoansLoanIdTransactionsResponse = loanTransactionHelper
|
||||
.makeLoanRepayment("PayoutRefund", "22 January 2021", 1000F, loanId.intValue());
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
|
||||
|
||||
logTransactions(loanId);
|
||||
verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"),
|
||||
transaction(1000.0, "Payout Refund", "22 January 2021"), transaction(5.75, "Accrual", "22 January 2021"),
|
||||
transaction(5.75, "Interest Refund", "22 January 2021"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyUC02a() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2021", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper
|
||||
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
|
||||
.daysInYearType(DaysInYearType.ACTUAL) //
|
||||
.supportedInterestRefundTypes(new ArrayList<>()).addSupportedInterestRefundTypesItem("PAYOUT_REFUND") //
|
||||
.addSupportedInterestRefundTypesItem("MERCHANT_ISSUED_REFUND") //
|
||||
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
|
||||
);
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.99,
|
||||
12, null);
|
||||
Assertions.assertNotNull(loanId);
|
||||
loanIdRef.set(loanId);
|
||||
disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
|
||||
});
|
||||
runAt("1 February 2021", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
PostLoansLoanIdTransactionsResponse postLoansLoanIdTransactionsResponse = loanTransactionHelper
|
||||
.makeLoanRepayment("PayoutRefund", "1 February 2021", 1000F, loanId.intValue());
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
|
||||
|
||||
logTransactions(loanId);
|
||||
verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"),
|
||||
transaction(1000.0, "Payout Refund", "01 February 2021"), transaction(8.48, "Accrual", "01 February 2021"),
|
||||
transaction(8.48, "Interest Refund", "01 February 2021"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyUC02b() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2021", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper
|
||||
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
|
||||
.daysInYearType(DaysInYearType.ACTUAL) //
|
||||
.supportedInterestRefundTypes(new ArrayList<>()).addSupportedInterestRefundTypesItem("PAYOUT_REFUND") //
|
||||
.addSupportedInterestRefundTypesItem("MERCHANT_ISSUED_REFUND") //
|
||||
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
|
||||
);
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.99,
|
||||
12, null);
|
||||
Assertions.assertNotNull(loanId);
|
||||
loanIdRef.set(loanId);
|
||||
disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
|
||||
});
|
||||
runAt("1 February 2021", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
PostLoansLoanIdTransactionsResponse postLoansLoanIdTransactionsResponse = loanTransactionHelper.makeLoanRepayment("Repayment",
|
||||
"1 February 2021", 87.89F, loanId.intValue());
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
|
||||
|
||||
logTransactions(loanId);
|
||||
verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"),
|
||||
transaction(87.89, "Repayment", "01 February 2021"));
|
||||
});
|
||||
|
||||
runAt("9 February 2021", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
PostLoansLoanIdTransactionsResponse postLoansLoanIdTransactionsResponse = loanTransactionHelper
|
||||
.makeLoanRepayment("PayoutRefund", "9 February 2021", 1000F, loanId.intValue());
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
|
||||
|
||||
logTransactions(loanId);
|
||||
verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"),
|
||||
transaction(1000.0, "Payout Refund", "09 February 2021"), transaction(87.89, "Repayment", "01 February 2021"),
|
||||
transaction(10.50, "Interest Refund", "09 February 2021"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyUC03() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2021", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper
|
||||
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
|
||||
.daysInYearType(DaysInYearType.ACTUAL) //
|
||||
.supportedInterestRefundTypes(new ArrayList<>())//
|
||||
.disallowExpectedDisbursements(true)//
|
||||
.multiDisburseLoan(true)//
|
||||
.maxTrancheCount(2).addSupportedInterestRefundTypesItem("PAYOUT_REFUND") //
|
||||
.addSupportedInterestRefundTypesItem("MERCHANT_ISSUED_REFUND") //
|
||||
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
|
||||
);
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.99,
|
||||
12, null);
|
||||
Assertions.assertNotNull(loanId);
|
||||
loanIdRef.set(loanId);
|
||||
disburseLoan(loanId, BigDecimal.valueOf(750), "1 January 2021");
|
||||
disburseLoan(loanId, BigDecimal.valueOf(250), "1 January 2021");
|
||||
});
|
||||
runAt("22 January 2021", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
PostLoansLoanIdTransactionsResponse postLoansLoanIdTransactionsResponse = loanTransactionHelper
|
||||
.makeLoanRepayment("PayoutRefund", "22 January 2021", 1000F, loanId.intValue());
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
|
||||
|
||||
logTransactions(loanId);
|
||||
verifyTransactions(loanId, transaction(750.0, "Disbursement", "01 January 2021"),
|
||||
transaction(250.0, "Disbursement", "01 January 2021"), transaction(1000.0, "Payout Refund", "22 January 2021"),
|
||||
transaction(5.75, "Accrual", "22 January 2021"), transaction(5.75, "Interest Refund", "22 January 2021"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyUC04() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2021", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper
|
||||
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
|
||||
.daysInYearType(DaysInYearType.ACTUAL) //
|
||||
.supportedInterestRefundTypes(new ArrayList<>()).disallowExpectedDisbursements(true).multiDisburseLoan(true)
|
||||
.maxTrancheCount(2).addSupportedInterestRefundTypesItem("PAYOUT_REFUND") //
|
||||
.addSupportedInterestRefundTypesItem("MERCHANT_ISSUED_REFUND") //
|
||||
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
|
||||
);
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.99,
|
||||
12, null);
|
||||
Assertions.assertNotNull(loanId);
|
||||
loanIdRef.set(loanId);
|
||||
disburseLoan(loanId, BigDecimal.valueOf(250), "1 January 2021");
|
||||
});
|
||||
runAt("4 January 2021", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
disburseLoan(loanId, BigDecimal.valueOf(750), "4 January 2021");
|
||||
});
|
||||
runAt("22 January 2021", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
PostLoansLoanIdTransactionsResponse postLoansLoanIdTransactionsResponse = loanTransactionHelper
|
||||
.makeLoanRepayment("PayoutRefund", "22 January 2021", 1000F, loanId.intValue());
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
|
||||
|
||||
logTransactions(loanId);
|
||||
verifyTransactions(loanId, transaction(750.0, "Disbursement", "04 January 2021"),
|
||||
transaction(250.0, "Disbursement", "01 January 2021"), transaction(1000.0, "Payout Refund", "22 January 2021"),
|
||||
transaction(5.14, "Accrual", "22 January 2021"), transaction(5.14, "Interest Refund", "22 January 2021"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyUC05() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
runAt("1 January 2021", () -> {
|
||||
PostLoanProductsResponse loanProduct = loanProductHelper
|
||||
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
|
||||
.daysInYearType(DaysInYearType.ACTUAL) //
|
||||
.supportedInterestRefundTypes(new ArrayList<>()).disallowExpectedDisbursements(true).multiDisburseLoan(true)
|
||||
.maxTrancheCount(2).addSupportedInterestRefundTypesItem("PAYOUT_REFUND") //
|
||||
.addSupportedInterestRefundTypesItem("MERCHANT_ISSUED_REFUND") //
|
||||
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
|
||||
);
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.99,
|
||||
12, null);
|
||||
Assertions.assertNotNull(loanId);
|
||||
loanIdRef.set(loanId);
|
||||
disburseLoan(loanId, BigDecimal.valueOf(500), "1 January 2021");
|
||||
});
|
||||
runAt("7 January 2021", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
disburseLoan(loanId, BigDecimal.valueOf(500), "7 January 2021");
|
||||
});
|
||||
runAt("1 February 2021", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
PostLoansLoanIdTransactionsResponse postLoansLoanIdTransactionsResponse = loanTransactionHelper.makeLoanRepayment("Repayment",
|
||||
"1 February 2021", 87.82F, loanId.intValue());
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
|
||||
|
||||
logTransactions(loanId);
|
||||
verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 January 2021"),
|
||||
transaction(500.0, "Disbursement", "07 January 2021"), transaction(87.82, "Repayment", "01 February 2021"));
|
||||
});
|
||||
|
||||
runAt("9 February 2021", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
PostLoansLoanIdTransactionsResponse postLoansLoanIdTransactionsResponse = loanTransactionHelper
|
||||
.makeLoanRepayment("PayoutRefund", "9 February 2021", 1000F, loanId.intValue());
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
|
||||
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
|
||||
|
||||
logTransactions(loanId);
|
||||
verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 January 2021"),
|
||||
transaction(500.0, "Disbursement", "07 January 2021"), transaction(1000.0, "Payout Refund", "09 February 2021"),
|
||||
transaction(87.82, "Repayment", "01 February 2021"), transaction(9.67, "Interest Refund", "09 February 2021"));
|
||||
});
|
||||
}
|
||||
|
||||
private void logTransactions(Long loanId) {
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
|
||||
assert loanDetails.getTransactions() != null;
|
||||
loanDetails.getTransactions()
|
||||
.forEach(tr -> log.info("Transaction {} {} {} ", tr.getType().getValue(), tr.getDate(), tr.getAmount()));
|
||||
}
|
||||
|
||||
}
|
||||
+4
-5
@@ -44,7 +44,6 @@ import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInMonthType;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
@@ -89,8 +88,8 @@ public class LoanRefundTransactionTest extends BaseLoanIntegrationTest {
|
||||
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation()
|
||||
.interestRatePerPeriod(108.0).interestCalculationPeriodType(RepaymentFrequencyType.DAYS)
|
||||
.interestRateFrequencyType(YEARS).daysInMonthType(DaysInMonthType.ACTUAL.getValue())
|
||||
.daysInYearType(DaysInYearType.DAYS_360).numberOfRepayments(4)//
|
||||
.interestRateFrequencyType(YEARS).daysInMonthType(DaysInMonthType.ACTUAL).daysInYearType(DaysInYearType.DAYS_360)
|
||||
.numberOfRepayments(4)//
|
||||
.maxInterestRatePerPeriod((double) 110)//
|
||||
.repaymentEvery(1)//
|
||||
.repaymentFrequencyType(1L)//
|
||||
@@ -161,8 +160,8 @@ public class LoanRefundTransactionTest extends BaseLoanIntegrationTest {
|
||||
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation()
|
||||
.interestRatePerPeriod(108.0).interestCalculationPeriodType(RepaymentFrequencyType.DAYS)
|
||||
.interestRateFrequencyType(YEARS).daysInMonthType(DaysInMonthType.ACTUAL.getValue())
|
||||
.daysInYearType(DaysInYearType.DAYS_360).numberOfRepayments(4)//
|
||||
.interestRateFrequencyType(YEARS).daysInMonthType(DaysInMonthType.ACTUAL).daysInYearType(DaysInYearType.DAYS_360)
|
||||
.numberOfRepayments(4)//
|
||||
.maxInterestRatePerPeriod((double) 110)//
|
||||
.repaymentEvery(1)//
|
||||
.repaymentFrequencyType(1L)//
|
||||
|
||||
+10
@@ -560,6 +560,16 @@ public class ExternalEventConfigurationHelper {
|
||||
loanTransactionAccrualActivityPreBusinessEvent.put("enabled", false);
|
||||
defaults.add(loanTransactionAccrualActivityPreBusinessEvent);
|
||||
|
||||
Map<String, Object> loanTransactionInterestRefundPostBusinessEvent = new HashMap<>();
|
||||
loanTransactionInterestRefundPostBusinessEvent.put("type", "LoanTransactionInterestRefundPostBusinessEvent");
|
||||
loanTransactionInterestRefundPostBusinessEvent.put("enabled", false);
|
||||
defaults.add(loanTransactionInterestRefundPostBusinessEvent);
|
||||
|
||||
Map<String, Object> loanTransactionInterestRefundPreBusinessEvent = new HashMap<>();
|
||||
loanTransactionInterestRefundPreBusinessEvent.put("type", "LoanTransactionInterestRefundPreBusinessEvent");
|
||||
loanTransactionInterestRefundPreBusinessEvent.put("enabled", false);
|
||||
defaults.add(loanTransactionInterestRefundPreBusinessEvent);
|
||||
|
||||
return defaults;
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user