FINERACT-2232: Capitalized income - Business events
This commit is contained in:
+35
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.event.business.domain.loan.transaction;
|
||||
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
|
||||
public class LoanCapitalizedIncomeTransactionCreatedBusinessEvent extends LoanTransactionBusinessEvent {
|
||||
|
||||
private static final String TYPE = "LoanCapitalizedIncomeTransactionCreatedBusinessEvent";
|
||||
|
||||
public LoanCapitalizedIncomeTransactionCreatedBusinessEvent(LoanTransaction value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
+12
@@ -34,6 +34,10 @@ import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanBalanceChangedBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanCapitalizedIncomeTransactionCreatedBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeBalance;
|
||||
@@ -61,6 +65,7 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
|
||||
private final LoanCapitalizedIncomeBalanceRepository capitalizedIncomeBalanceRepository;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
private final LoanBalanceService loanBalanceService;
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
@@ -101,6 +106,9 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
|
||||
// Post journal entries
|
||||
journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
businessEventNotifierService
|
||||
.notifyPostBusinessEvent(new LoanCapitalizedIncomeTransactionCreatedBusinessEvent(capitalizedIncomeTransaction));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(capitalizedIncomeTransaction.getId()) //
|
||||
.withEntityExternalId(capitalizedIncomeTransaction.getExternalId()) //
|
||||
@@ -153,6 +161,10 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
|
||||
MathUtil.negativeToZero(capitalizedIncomeBalance.getUnrecognizedAmount().subtract(transactionAmount)));
|
||||
capitalizedIncomeBalanceRepository.save(capitalizedIncomeBalance);
|
||||
|
||||
businessEventNotifierService.notifyPostBusinessEvent(
|
||||
new LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent(savedCapitalizedIncomeAdjustment));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(savedCapitalizedIncomeAdjustment.getId()) //
|
||||
.withEntityExternalId(savedCapitalizedIncomeAdjustment.getExternalId()) //
|
||||
|
||||
+4
-2
@@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.loanaccount.starter;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository;
|
||||
@@ -50,10 +51,11 @@ public class ProgressiveLoanAccountConfiguration {
|
||||
PaymentDetailWritePlatformService paymentDetailWritePlatformService, LoanJournalEntryPoster journalEntryPoster,
|
||||
NoteWritePlatformService noteWritePlatformService, ExternalIdFactory externalIdFactory,
|
||||
LoanCapitalizedIncomeBalanceRepository capitalizedIncomeBalanceRepository,
|
||||
ReprocessLoanTransactionsService reprocessLoanTransactionsService, LoanBalanceService loanBalanceService) {
|
||||
ReprocessLoanTransactionsService reprocessLoanTransactionsService, LoanBalanceService loanBalanceService,
|
||||
BusinessEventNotifierService businessEventNotifierService) {
|
||||
return new CapitalizedIncomeWritePlatformServiceImpl(loanTransactionValidator, loanAssembler, loanTransactionRepository,
|
||||
paymentDetailWritePlatformService, journalEntryPoster, noteWritePlatformService, externalIdFactory,
|
||||
capitalizedIncomeBalanceRepository, reprocessLoanTransactionsService, loanBalanceService);
|
||||
capitalizedIncomeBalanceRepository, reprocessLoanTransactionsService, loanBalanceService, businessEventNotifierService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -201,4 +201,5 @@
|
||||
<include file="parts/0180_add_version_field_to_loan_transaction.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0181_add_capitalized_income_amortization_adjustment_transaction.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0182_transaction_summary_with_asset_owner_report_fix_charge_reason_and_add_buyback_intermediate.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0183_add_LoanCapitalizedIncomeTransactionCreatedBusinessEvent.xml" relativeToChangelogFile="true" />
|
||||
</databaseChangeLog>
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
<changeSet id="1" author="fineract">
|
||||
<insert tableName="m_external_event_configuration">
|
||||
<column name="type" value="LoanCapitalizedIncomeTransactionCreatedBusinessEvent"/>
|
||||
<column name="enabled" valueBoolean="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+4
-2
@@ -108,7 +108,8 @@ public class ExternalEventConfigurationValidationServiceTest {
|
||||
"LoanTransactionInterestRefundPreBusinessEvent", "LoanAccrualAdjustmentTransactionBusinessEvent",
|
||||
"LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent",
|
||||
"LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent", "LoanTransactionContractTerminationPostBusinessEvent",
|
||||
"LoanCapitalizedIncomeAmortizationAdjustmentTransactionCreatedBusinessEvent");
|
||||
"LoanCapitalizedIncomeAmortizationAdjustmentTransactionCreatedBusinessEvent",
|
||||
"LoanCapitalizedIncomeTransactionCreatedBusinessEvent");
|
||||
|
||||
List<FineractPlatformTenant> tenants = Arrays
|
||||
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
||||
@@ -197,7 +198,8 @@ public class ExternalEventConfigurationValidationServiceTest {
|
||||
"LoanTransactionInterestRefundPreBusinessEvent", "LoanAccrualAdjustmentTransactionBusinessEvent",
|
||||
"LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent",
|
||||
"LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent", "LoanTransactionContractTerminationPostBusinessEvent",
|
||||
"LoanCapitalizedIncomeAmortizationAdjustmentTransactionCreatedBusinessEvent");
|
||||
"LoanCapitalizedIncomeAmortizationAdjustmentTransactionCreatedBusinessEvent",
|
||||
"LoanCapitalizedIncomeTransactionCreatedBusinessEvent");
|
||||
|
||||
List<FineractPlatformTenant> tenants = Arrays
|
||||
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
||||
|
||||
+42
-1
@@ -87,6 +87,7 @@ import org.apache.fineract.client.models.RetrieveLoansPointInTimeRequest;
|
||||
import org.apache.fineract.client.util.CallFailedRuntimeException;
|
||||
import org.apache.fineract.client.util.Calls;
|
||||
import org.apache.fineract.infrastructure.configuration.api.GlobalConfigurationConstants;
|
||||
import org.apache.fineract.infrastructure.event.external.service.validation.ExternalEventDTO;
|
||||
import org.apache.fineract.integrationtests.client.IntegrationTest;
|
||||
import org.apache.fineract.integrationtests.common.BatchHelper;
|
||||
import org.apache.fineract.integrationtests.common.BusinessDateHelper;
|
||||
@@ -99,6 +100,9 @@ import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
|
||||
import org.apache.fineract.integrationtests.common.accounting.JournalEntryHelper;
|
||||
import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
|
||||
import org.apache.fineract.integrationtests.common.error.ErrorResponse;
|
||||
import org.apache.fineract.integrationtests.common.externalevents.BusinessEvent;
|
||||
import org.apache.fineract.integrationtests.common.externalevents.ExternalEventHelper;
|
||||
import org.apache.fineract.integrationtests.common.externalevents.ExternalEventsExtension;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanAccountLockHelper;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanProductHelper;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
|
||||
@@ -119,7 +123,7 @@ import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
@Slf4j
|
||||
@ExtendWith(LoanTestLifecycleExtension.class)
|
||||
@ExtendWith({ LoanTestLifecycleExtension.class, ExternalEventsExtension.class })
|
||||
public abstract class BaseLoanIntegrationTest extends IntegrationTest {
|
||||
|
||||
protected static final String DATETIME_PATTERN = "dd MMMM yyyy";
|
||||
@@ -171,6 +175,7 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
|
||||
protected GlobalConfigurationHelper globalConfigurationHelper = new GlobalConfigurationHelper();
|
||||
protected final CodeHelper codeHelper = new CodeHelper();
|
||||
protected final ChargesHelper chargesHelper = new ChargesHelper();
|
||||
protected final ExternalEventHelper externalEventHelper = new ExternalEventHelper();
|
||||
|
||||
protected static void validateRepaymentPeriod(GetLoansLoanIdResponse loanDetails, Integer index, LocalDate dueDate, double principalDue,
|
||||
double principalPaid, double principalOutstanding, double paidInAdvance, double paidLate) {
|
||||
@@ -1269,6 +1274,42 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest {
|
||||
new PostLoansLoanIdRequest().rejectedOnDate(rejectedOnDate).locale("en").dateFormat(DATETIME_PATTERN));
|
||||
}
|
||||
|
||||
protected void verifyBusinessEvents(BusinessEvent... businessEvents) {
|
||||
List<ExternalEventDTO> allExternalEvents = ExternalEventHelper.getAllExternalEvents(requestSpec, responseSpec);
|
||||
logBusinessEvents(allExternalEvents);
|
||||
Assertions.assertNotNull(businessEvents);
|
||||
Assertions.assertNotNull(allExternalEvents);
|
||||
Assertions.assertTrue(businessEvents.length <= allExternalEvents.size(),
|
||||
"Expected business event count is less than actual. Expected: " + businessEvents.length + " Actual: "
|
||||
+ allExternalEvents.size());
|
||||
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATETIME_PATTERN, Locale.ENGLISH);
|
||||
for (BusinessEvent businessEvent : businessEvents) {
|
||||
long count = allExternalEvents.stream().filter(externalEvent -> businessEvent.verify(externalEvent, formatter)).count();
|
||||
Assertions.assertEquals(1, count, "Expected business event not found " + businessEvent);
|
||||
}
|
||||
}
|
||||
|
||||
protected void logBusinessEvents(List<ExternalEventDTO> allExternalEvents) {
|
||||
allExternalEvents.forEach(externalEventDTO -> {
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
protected void deleteAllExternalEvents() {
|
||||
ExternalEventHelper.deleteAllExternalEvents(requestSpec, createResponseSpecification(Matchers.is(204)));
|
||||
List<ExternalEventDTO> allExternalEvents = ExternalEventHelper.getAllExternalEvents(requestSpec, responseSpec);
|
||||
Assertions.assertEquals(0, allExternalEvents.size());
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class BatchRequestBuilder {
|
||||
|
||||
|
||||
-7
@@ -37,7 +37,6 @@ import org.apache.fineract.integrationtests.common.SchedulerJobHelper;
|
||||
import org.apache.fineract.integrationtests.common.externalevents.ExternalEventHelper;
|
||||
import org.apache.fineract.integrationtests.common.externalevents.ExternalEventsExtension;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -305,12 +304,6 @@ public class CustomSnapshotEventIntegrationTest extends BaseLoanIntegrationTest
|
||||
});
|
||||
}
|
||||
|
||||
private void deleteAllExternalEvents() {
|
||||
ExternalEventHelper.deleteAllExternalEvents(requestSpec, createResponseSpecification(Matchers.is(204)));
|
||||
List<ExternalEventDTO> allExternalEvents = ExternalEventHelper.getAllExternalEvents(requestSpec, responseSpec);
|
||||
Assertions.assertEquals(0, allExternalEvents.size());
|
||||
}
|
||||
|
||||
private void enableCOBBusinessStep(String... steps) {
|
||||
new BusinessStepHelper().updateSteps("LOAN_CLOSE_OF_BUSINESS", steps);
|
||||
|
||||
|
||||
+3
-250
@@ -25,24 +25,12 @@ 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.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
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;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdStatus;
|
||||
import org.apache.fineract.client.models.GlobalConfigurationPropertyData;
|
||||
@@ -66,8 +54,10 @@ import org.apache.fineract.integrationtests.common.LoanRescheduleRequestHelper;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.apache.fineract.integrationtests.common.externalevents.ExternalEventHelper;
|
||||
import org.apache.fineract.integrationtests.common.externalevents.ExternalEventsExtension;
|
||||
import org.apache.fineract.integrationtests.common.externalevents.LoanAdjustTransactionBusinessEvent;
|
||||
import org.apache.fineract.integrationtests.common.externalevents.LoanBusinessEvent;
|
||||
import org.apache.fineract.integrationtests.common.externalevents.LoanTransactionBusinessEvent;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
@@ -1012,12 +1002,6 @@ public class ExternalBusinessEventTest extends BaseLoanIntegrationTest {
|
||||
externalEventHelper.disableBusinessEvent("LoanBalanceChangedBusinessEvent");
|
||||
}
|
||||
|
||||
private void deleteAllExternalEvents() {
|
||||
ExternalEventHelper.deleteAllExternalEvents(requestSpec, createResponseSpecification(Matchers.is(204)));
|
||||
List<ExternalEventDTO> allExternalEvents = ExternalEventHelper.getAllExternalEvents(requestSpec, responseSpec);
|
||||
Assertions.assertEquals(0, allExternalEvents.size());
|
||||
}
|
||||
|
||||
private static Long createLoanProductPeriodicWithInterest() {
|
||||
String name = Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_", 6);
|
||||
String shortName = Utils.uniqueRandomStringGenerator("", 4);
|
||||
@@ -1070,21 +1054,6 @@ public class ExternalBusinessEventTest extends BaseLoanIntegrationTest {
|
||||
return loanId;
|
||||
}
|
||||
|
||||
private void logBusinessEvents(List<ExternalEventDTO> allExternalEvents) {
|
||||
allExternalEvents.forEach(externalEventDTO -> {
|
||||
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 void enableLoanInterestRefundPstBusinessEvent(boolean enabled) {
|
||||
externalEventHelper.configureBusinessEvent("LoanTransactionInterestRefundPostBusinessEvent", enabled);
|
||||
}
|
||||
@@ -1096,220 +1065,4 @@ public class ExternalBusinessEventTest extends BaseLoanIntegrationTest {
|
||||
private void configureLoanAccrualTransactionCreatedBusinessEvent(boolean enabled) {
|
||||
externalEventHelper.configureBusinessEvent("LoanAccrualTransactionCreatedBusinessEvent", enabled);
|
||||
}
|
||||
|
||||
public void verifyBusinessEvents(BusinessEvent... businessEvents) {
|
||||
List<ExternalEventDTO> allExternalEvents = ExternalEventHelper.getAllExternalEvents(requestSpec, responseSpec);
|
||||
logBusinessEvents(allExternalEvents);
|
||||
Assertions.assertNotNull(businessEvents);
|
||||
Assertions.assertNotNull(allExternalEvents);
|
||||
Assertions.assertTrue(businessEvents.length <= allExternalEvents.size(),
|
||||
"Expected business event count is less than actual. Expected: " + businessEvents.length + " Actual: "
|
||||
+ allExternalEvents.size());
|
||||
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATETIME_PATTERN, Locale.ENGLISH);
|
||||
for (BusinessEvent businessEvent : businessEvents) {
|
||||
long count = allExternalEvents.stream().filter(externalEvent -> businessEvent.verify(externalEvent, formatter)).count();
|
||||
Assertions.assertEquals(1, count, "Expected business event not found " + businessEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class BusinessEvent {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
public static class LoanAdjustTransactionBusinessEvent extends BusinessEvent {
|
||||
|
||||
private String transactionTypeCode;
|
||||
private String transactionDate;
|
||||
private Double oldAmount;
|
||||
private Double newAmount;
|
||||
private Double oldPrincipalPortion;
|
||||
private Double newPrincipalPortion;
|
||||
private Double oldInterestPortion;
|
||||
private Double newInterestPortion;
|
||||
private Double oldFeePortion;
|
||||
private Double newFeePortion;
|
||||
private Double oldPenaltyPortion;
|
||||
private Double newPenaltyPortion;
|
||||
|
||||
public LoanAdjustTransactionBusinessEvent(String type, String businessDate, String transactionTypeCode, String transactionDate) {
|
||||
super(type, businessDate);
|
||||
this.transactionTypeCode = transactionTypeCode;
|
||||
this.transactionDate = transactionDate;
|
||||
}
|
||||
|
||||
public LoanAdjustTransactionBusinessEvent(String type, String businessDate, String transactionTypeCode, String transactionDate,
|
||||
Double oldAmount, Double newAmount) {
|
||||
super(type, businessDate);
|
||||
this.transactionTypeCode = transactionTypeCode;
|
||||
this.transactionDate = transactionDate;
|
||||
this.oldAmount = oldAmount;
|
||||
this.newAmount = newAmount;
|
||||
}
|
||||
|
||||
public LoanAdjustTransactionBusinessEvent(String type, String businessDate, String transactionTypeCode, String transactionDate,
|
||||
Double oldAmount, Double newAmount, Double oldPrincipalPortion, Double newPrincipalPortion, Double oldInterestPortion,
|
||||
Double newInterestPortion, Double oldFeePortion, Double newFeePortion, Double oldPenaltyPortion, Double newPenaltyPortion) {
|
||||
super(type, businessDate);
|
||||
this.transactionTypeCode = transactionTypeCode;
|
||||
this.transactionDate = transactionDate;
|
||||
this.oldAmount = oldAmount;
|
||||
this.newAmount = newAmount;
|
||||
this.oldPrincipalPortion = oldPrincipalPortion;
|
||||
this.newPrincipalPortion = newPrincipalPortion;
|
||||
this.oldInterestPortion = oldInterestPortion;
|
||||
this.newInterestPortion = newInterestPortion;
|
||||
this.oldFeePortion = oldFeePortion;
|
||||
this.newFeePortion = newFeePortion;
|
||||
this.oldPenaltyPortion = oldPenaltyPortion;
|
||||
this.newPenaltyPortion = newPenaltyPortion;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean verify(ExternalEventDTO externalEvent, DateTimeFormatter formatter) {
|
||||
final Object transactionToAdjust = externalEvent.getPayLoad().get("transactionToAdjust");
|
||||
final Map<?, Object> transActionToAdjustMap = transactionToAdjust instanceof Map ? (Map<String, Object>) transactionToAdjust
|
||||
: Collections.emptyMap();
|
||||
|
||||
Object actualOldAmount = transActionToAdjustMap.get("amount");
|
||||
Object actualOldPrincipalPortion = transActionToAdjustMap.get("principalPortion");
|
||||
Object actualOldInterestPortion = transActionToAdjustMap.get("interestPortion");
|
||||
Object actualOldFeePortion = transActionToAdjustMap.get("feeChargesPortion");
|
||||
Object actualOldPenaltyPortion = transActionToAdjustMap.get("penaltyChargesPortion");
|
||||
|
||||
final Object newTransactionDetail = externalEvent.getPayLoad().get("newTransactionDetail");
|
||||
final Map<?, Object> newTransactionDetailMap = newTransactionDetail instanceof Map ? (Map<String, Object>) newTransactionDetail
|
||||
: Collections.emptyMap();
|
||||
|
||||
Object actualNewAmount = newTransactionDetailMap.get("amount");
|
||||
Object actualNewPrincipalPortion = newTransactionDetailMap.get("principalPortion");
|
||||
Object actualNewInterestPortion = newTransactionDetailMap.get("interestPortion");
|
||||
Object actualNewFeePortion = newTransactionDetailMap.get("feeChargesPortion");
|
||||
Object actualNewPenaltyPortion = newTransactionDetailMap.get("penaltyChargesPortion");
|
||||
|
||||
final Object actualTransactionDate = transActionToAdjustMap.get("date");
|
||||
final Object transactionType = transActionToAdjustMap.get("type");
|
||||
final Map<?, Object> transactionTypeMap = transactionType instanceof Map ? (Map<String, Object>) transactionType
|
||||
: Collections.emptyMap();
|
||||
final Object actualTransactionTypeCode = transactionTypeMap.get("code");
|
||||
|
||||
return super.verify(externalEvent, formatter)//
|
||||
&& Objects.equals(actualTransactionTypeCode, transactionTypeCode)
|
||||
&& Objects.equals(actualTransactionDate, transactionDate)//
|
||||
&& (oldAmount == null || Objects.equals(actualOldAmount, oldAmount))//
|
||||
&& (newAmount == null || Objects.equals(actualNewAmount, newAmount))//
|
||||
&& (oldPrincipalPortion == null || Objects.equals(actualOldPrincipalPortion, oldPrincipalPortion))//
|
||||
&& (newPrincipalPortion == null || Objects.equals(actualNewPrincipalPortion, newPrincipalPortion))//
|
||||
&& (oldInterestPortion == null || Objects.equals(actualOldInterestPortion, oldInterestPortion))//
|
||||
&& (newInterestPortion == null || Objects.equals(actualNewInterestPortion, newInterestPortion))//
|
||||
&& (oldFeePortion == null || Objects.equals(actualOldFeePortion, oldFeePortion))//
|
||||
&& (newFeePortion == null || Objects.equals(actualNewFeePortion, newFeePortion))//
|
||||
&& (oldPenaltyPortion == null || Objects.equals(actualOldPenaltyPortion, oldPenaltyPortion))//
|
||||
&& (newPenaltyPortion == null || Objects.equals(actualNewPenaltyPortion, newPenaltyPortion));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+113
@@ -36,6 +36,9 @@ import org.apache.fineract.client.models.PostLoansResponse;
|
||||
import org.apache.fineract.client.util.CallFailedRuntimeException;
|
||||
import org.apache.fineract.integrationtests.common.BusinessStepHelper;
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
import org.apache.fineract.integrationtests.common.externalevents.LoanAdjustTransactionBusinessEvent;
|
||||
import org.apache.fineract.integrationtests.common.externalevents.LoanBusinessEvent;
|
||||
import org.apache.fineract.integrationtests.common.externalevents.LoanTransactionBusinessEvent;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -780,4 +783,114 @@ public class LoanCapitalizedIncomeTest extends BaseLoanIntegrationTest {
|
||||
Assertions.assertEquals(thousandFiveHundred, loanDetails.getSummary().getTotalPrincipal().setScale(1, RoundingMode.HALF_UP));
|
||||
Assertions.assertEquals(zero, loanDetails.getSummary().getPrincipalOutstanding().setScale(0, RoundingMode.HALF_UP));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCapitalizedIncomeEvents() {
|
||||
externalEventHelper.enableBusinessEvent("LoanCapitalizedIncomeTransactionCreatedBusinessEvent");
|
||||
externalEventHelper.enableBusinessEvent("LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent");
|
||||
externalEventHelper.enableBusinessEvent("LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent");
|
||||
externalEventHelper.enableBusinessEvent("LoanCapitalizedIncomeAmortizationAdjustmentTransactionCreatedBusinessEvent");
|
||||
externalEventHelper.enableBusinessEvent("LoanAdjustTransactionBusinessEvent");
|
||||
externalEventHelper.enableBusinessEvent("LoanBalanceChangedBusinessEvent");
|
||||
|
||||
final AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
final AtomicReference<Long> capitalizedIncomeTransactionIdRef = new AtomicReference<>();
|
||||
|
||||
final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
|
||||
|
||||
final PostLoanProductsResponse loanProductsResponse = loanProductHelper
|
||||
.createLoanProduct(create4IProgressive().enableIncomeCapitalization(true)
|
||||
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
|
||||
.capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)
|
||||
.deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())
|
||||
.incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())
|
||||
.capitalizedIncomeType(PostLoanProductsRequest.CapitalizedIncomeTypeEnum.FEE));
|
||||
|
||||
runAt("1 January 2024", () -> {
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "1 January 2024",
|
||||
500.0, 7.0, 3, null);
|
||||
loanIdRef.set(loanId);
|
||||
|
||||
disburseLoan(loanId, BigDecimal.valueOf(100), "1 January 2024");
|
||||
|
||||
deleteAllExternalEvents();
|
||||
|
||||
Long capitalizedIncomeTransactionId = loanTransactionHelper.addCapitalizedIncome(loanId, "1 January 2024", 100.0)
|
||||
.getResourceId();
|
||||
capitalizedIncomeTransactionIdRef.set(capitalizedIncomeTransactionId);
|
||||
|
||||
verifyBusinessEvents(
|
||||
new LoanTransactionBusinessEvent("LoanCapitalizedIncomeTransactionCreatedBusinessEvent", "01 January 2024", 100.0,
|
||||
200.0, 100.0, 0.0, 0.0, 0.0),
|
||||
new LoanBusinessEvent("LoanBalanceChangedBusinessEvent", "01 January 2024", 300, 100.0, 200.0));
|
||||
});
|
||||
runAt("2 January 2024", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
|
||||
deleteAllExternalEvents();
|
||||
|
||||
executeInlineCOB(loanId);
|
||||
|
||||
verifyTransactions(loanId, //
|
||||
transaction(100.0, "Disbursement", "01 January 2024"), //
|
||||
transaction(100.0, "Capitalized Income", "01 January 2024"), //
|
||||
transaction(1.10, "Capitalized Income Amortization", "01 January 2024") //
|
||||
);
|
||||
verifyBusinessEvents(new LoanTransactionBusinessEvent("LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent",
|
||||
"01 January 2024", 1.10, 0.0, 0.0, 0.0, 1.10, 0.0));
|
||||
});
|
||||
runAt("3 January 2024", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
executeInlineCOB(loanId);
|
||||
|
||||
deleteAllExternalEvents();
|
||||
|
||||
Long capitalizedIncomeAdjustmentTransactionId = loanTransactionHelper
|
||||
.capitalizedIncomeAdjustment(loanId, capitalizedIncomeTransactionIdRef.get(), "3 January 2024", 50.0).getResourceId();
|
||||
|
||||
verifyTransactions(loanId, //
|
||||
transaction(100.0, "Disbursement", "01 January 2024"), //
|
||||
transaction(100.0, "Capitalized Income", "01 January 2024"), //
|
||||
transaction(1.10, "Capitalized Income Amortization", "01 January 2024"), //
|
||||
transaction(0.04, "Accrual", "02 January 2024"), //
|
||||
transaction(1.10, "Capitalized Income Amortization", "02 January 2024"), //
|
||||
transaction(50.0, "Capitalized Income Adjustment", "03 January 2024") //
|
||||
);
|
||||
|
||||
verifyBusinessEvents(
|
||||
new LoanTransactionBusinessEvent("LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent", "03 January 2024",
|
||||
50.0, 150.0, 50.0, 0.0, 0.0, 0.0),
|
||||
new LoanBusinessEvent("LoanBalanceChangedBusinessEvent", "03 January 2024", 300, 100.0, 150.0));
|
||||
|
||||
deleteAllExternalEvents();
|
||||
|
||||
loanTransactionHelper.reverseLoanTransaction(loanId, capitalizedIncomeAdjustmentTransactionId, "3 January 2024");
|
||||
|
||||
verifyBusinessEvents(new LoanAdjustTransactionBusinessEvent("LoanAdjustTransactionBusinessEvent", "03 January 2024",
|
||||
"loanTransactionType.capitalizedIncomeAdjustment", "2024-01-03"));
|
||||
});
|
||||
runAt("4 January 2024", () -> {
|
||||
Long loanId = loanIdRef.get();
|
||||
executeInlineCOB(loanId);
|
||||
|
||||
deleteAllExternalEvents();
|
||||
|
||||
loanTransactionHelper.reverseLoanTransaction(loanId, capitalizedIncomeTransactionIdRef.get(), "3 January 2024");
|
||||
|
||||
verifyTransactions(loanId, //
|
||||
transaction(100.0, "Disbursement", "01 January 2024"), //
|
||||
transaction(100.0, "Capitalized Income", "01 January 2024"), //
|
||||
transaction(1.10, "Capitalized Income Amortization", "01 January 2024"), //
|
||||
transaction(0.04, "Accrual", "02 January 2024"), //
|
||||
transaction(1.10, "Capitalized Income Amortization", "02 January 2024"), //
|
||||
transaction(0.04, "Accrual", "03 January 2024"), //
|
||||
transaction(1.10, "Capitalized Income Amortization", "02 January 2024"), //
|
||||
transaction(50.0, "Capitalized Income Adjustment", "03 January 2024") //
|
||||
);
|
||||
|
||||
verifyBusinessEvents(new LoanAdjustTransactionBusinessEvent("LoanAdjustTransactionBusinessEvent", "04 January 2024",
|
||||
"loanTransactionType.capitalizedIncome", "2024-01-01") //
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -606,6 +606,11 @@ public class ExternalEventConfigurationHelper {
|
||||
loanCapitalizedIncomeAmortizationAdjustmentTransactionCreatedBusinessEvent.put("enabled", false);
|
||||
defaults.add(loanCapitalizedIncomeAmortizationAdjustmentTransactionCreatedBusinessEvent);
|
||||
|
||||
Map<String, Object> loanCapitalizedIncomeTransactionCreatedBusinessEvent = new HashMap<>();
|
||||
loanCapitalizedIncomeTransactionCreatedBusinessEvent.put("type", "LoanCapitalizedIncomeTransactionCreatedBusinessEvent");
|
||||
loanCapitalizedIncomeTransactionCreatedBusinessEvent.put("enabled", false);
|
||||
defaults.add(loanCapitalizedIncomeTransactionCreatedBusinessEvent);
|
||||
|
||||
return defaults;
|
||||
}
|
||||
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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.common.externalevents;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Objects;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.event.external.service.validation.ExternalEventDTO;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class BusinessEvent {
|
||||
|
||||
protected String type;
|
||||
protected String businessDate;
|
||||
|
||||
public boolean verify(@NotNull ExternalEventDTO externalEvent, DateTimeFormatter formatter) {
|
||||
var businessDate = LocalDate.parse(getBusinessDate(), formatter);
|
||||
|
||||
return Objects.equals(externalEvent.getType(), getType()) && Objects.equals(externalEvent.getBusinessDate(), businessDate);
|
||||
}
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* 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.common.externalevents;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.apache.fineract.infrastructure.event.external.service.validation.ExternalEventDTO;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class LoanAdjustTransactionBusinessEvent extends BusinessEvent {
|
||||
|
||||
private String transactionTypeCode;
|
||||
private String transactionDate;
|
||||
private Double oldAmount;
|
||||
private Double newAmount;
|
||||
private Double oldPrincipalPortion;
|
||||
private Double newPrincipalPortion;
|
||||
private Double oldInterestPortion;
|
||||
private Double newInterestPortion;
|
||||
private Double oldFeePortion;
|
||||
private Double newFeePortion;
|
||||
private Double oldPenaltyPortion;
|
||||
private Double newPenaltyPortion;
|
||||
|
||||
// minimum data for checking if transaction was reversed
|
||||
public LoanAdjustTransactionBusinessEvent(String type, String businessDate, String transactionTypeCode, String transactionDate) {
|
||||
super(type, businessDate);
|
||||
this.transactionTypeCode = transactionTypeCode;
|
||||
this.transactionDate = transactionDate;
|
||||
}
|
||||
|
||||
// minimum data for checking if transaction was adjusted
|
||||
public LoanAdjustTransactionBusinessEvent(String type, String businessDate, String transactionTypeCode, String transactionDate,
|
||||
Double oldAmount, Double newAmount) {
|
||||
super(type, businessDate);
|
||||
this.transactionTypeCode = transactionTypeCode;
|
||||
this.transactionDate = transactionDate;
|
||||
this.oldAmount = oldAmount;
|
||||
this.newAmount = newAmount;
|
||||
}
|
||||
|
||||
public LoanAdjustTransactionBusinessEvent(String type, String businessDate, String transactionTypeCode, String transactionDate,
|
||||
Double oldAmount, Double newAmount, Double oldPrincipalPortion, Double newPrincipalPortion, Double oldInterestPortion,
|
||||
Double newInterestPortion, Double oldFeePortion, Double newFeePortion, Double oldPenaltyPortion, Double newPenaltyPortion) {
|
||||
super(type, businessDate);
|
||||
this.transactionTypeCode = transactionTypeCode;
|
||||
this.transactionDate = transactionDate;
|
||||
this.oldAmount = oldAmount;
|
||||
this.newAmount = newAmount;
|
||||
this.oldPrincipalPortion = oldPrincipalPortion;
|
||||
this.newPrincipalPortion = newPrincipalPortion;
|
||||
this.oldInterestPortion = oldInterestPortion;
|
||||
this.newInterestPortion = newInterestPortion;
|
||||
this.oldFeePortion = oldFeePortion;
|
||||
this.newFeePortion = newFeePortion;
|
||||
this.oldPenaltyPortion = oldPenaltyPortion;
|
||||
this.newPenaltyPortion = newPenaltyPortion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(ExternalEventDTO externalEvent, DateTimeFormatter formatter) {
|
||||
final Object transactionToAdjust = externalEvent.getPayLoad().get("transactionToAdjust");
|
||||
final Map<?, Object> transActionToAdjustMap = transactionToAdjust instanceof Map ? (Map<String, Object>) transactionToAdjust
|
||||
: Collections.emptyMap();
|
||||
|
||||
Object actualOldAmount = transActionToAdjustMap.get("amount");
|
||||
Object actualOldPrincipalPortion = transActionToAdjustMap.get("principalPortion");
|
||||
Object actualOldInterestPortion = transActionToAdjustMap.get("interestPortion");
|
||||
Object actualOldFeePortion = transActionToAdjustMap.get("feeChargesPortion");
|
||||
Object actualOldPenaltyPortion = transActionToAdjustMap.get("penaltyChargesPortion");
|
||||
|
||||
final Object newTransactionDetail = externalEvent.getPayLoad().get("newTransactionDetail");
|
||||
final Map<?, Object> newTransactionDetailMap = newTransactionDetail instanceof Map ? (Map<String, Object>) newTransactionDetail
|
||||
: Collections.emptyMap();
|
||||
|
||||
Object actualNewAmount = newTransactionDetailMap.get("amount");
|
||||
Object actualNewPrincipalPortion = newTransactionDetailMap.get("principalPortion");
|
||||
Object actualNewInterestPortion = newTransactionDetailMap.get("interestPortion");
|
||||
Object actualNewFeePortion = newTransactionDetailMap.get("feeChargesPortion");
|
||||
Object actualNewPenaltyPortion = newTransactionDetailMap.get("penaltyChargesPortion");
|
||||
|
||||
final Object actualTransactionDate = transActionToAdjustMap.get("date");
|
||||
final Object transactionType = transActionToAdjustMap.get("type");
|
||||
final Map<?, Object> transactionTypeMap = transactionType instanceof Map ? (Map<String, Object>) transactionType
|
||||
: Collections.emptyMap();
|
||||
final Object actualTransactionTypeCode = transactionTypeMap.get("code");
|
||||
|
||||
return super.verify(externalEvent, formatter)//
|
||||
&& Objects.equals(actualTransactionTypeCode, transactionTypeCode) && Objects.equals(actualTransactionDate, transactionDate)//
|
||||
&& (oldAmount == null || Objects.equals(actualOldAmount, oldAmount))//
|
||||
&& (newAmount == null || Objects.equals(actualNewAmount, newAmount))//
|
||||
&& (oldPrincipalPortion == null || Objects.equals(actualOldPrincipalPortion, oldPrincipalPortion))//
|
||||
&& (newPrincipalPortion == null || Objects.equals(actualNewPrincipalPortion, newPrincipalPortion))//
|
||||
&& (oldInterestPortion == null || Objects.equals(actualOldInterestPortion, oldInterestPortion))//
|
||||
&& (newInterestPortion == null || Objects.equals(actualNewInterestPortion, newInterestPortion))//
|
||||
&& (oldFeePortion == null || Objects.equals(actualOldFeePortion, oldFeePortion))//
|
||||
&& (newFeePortion == null || Objects.equals(actualNewFeePortion, newFeePortion))//
|
||||
&& (oldPenaltyPortion == null || Objects.equals(actualOldPenaltyPortion, oldPenaltyPortion))//
|
||||
&& (newPenaltyPortion == null || Objects.equals(actualNewPenaltyPortion, newPenaltyPortion));
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* 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.common.externalevents;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.event.external.service.validation.ExternalEventDTO;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public 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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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.common.externalevents;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Objects;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.apache.fineract.infrastructure.event.external.service.validation.ExternalEventDTO;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public 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
|
||||
public 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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user