You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ad...@apache.org on 2025/07/07 11:59:42 UTC
(fineract) 02/02: FINERACT-2311: added e2e auto test scenarios for manual journal entries with asset externalization owner
This is an automated email from the ASF dual-hosted git repository.
adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
commit c8b88c3664c4299555f6cca1f1902754908e8176
Author: MarianaDmytrivBinariks <md...@binariks.com>
AuthorDate: Mon Jul 7 12:26:48 2025 +0300
FINERACT-2311: added e2e auto test scenarios for manual journal entries with asset externalization owner
---
.../fineract/test/factory/LoanRequestFactory.java | 21 ++++
.../test/stepdef/common/JournalEntriesStepDef.java | 136 +++++++++++++++++++++
.../fineract/test/support/TestContextKey.java | 2 +
.../features/AssetExternalization.feature | 80 ++++++++++++
4 files changed, 239 insertions(+)
diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/LoanRequestFactory.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/LoanRequestFactory.java
index 4c7b0fc286..bd538658e5 100644
--- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/LoanRequestFactory.java
+++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/LoanRequestFactory.java
@@ -19,12 +19,14 @@
package org.apache.fineract.test.factory;
import java.math.BigDecimal;
+import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.client.models.DisbursementDetail;
import org.apache.fineract.client.models.InterestPauseRequestDto;
+import org.apache.fineract.client.models.JournalEntryCommand;
import org.apache.fineract.client.models.PostAddAndDeleteDisbursementDetailRequest;
import org.apache.fineract.client.models.PostCreateRescheduleLoansRequest;
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdRequest;
@@ -34,11 +36,14 @@ import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionI
import org.apache.fineract.client.models.PostLoansRequest;
import org.apache.fineract.client.models.PostUpdateRescheduleLoansRequest;
import org.apache.fineract.client.models.PutLoansLoanIdRequest;
+import org.apache.fineract.client.models.SingleDebitOrCreditEntryCommand;
import org.apache.fineract.test.data.InterestCalculationPeriodTime;
import org.apache.fineract.test.data.InterestType;
import org.apache.fineract.test.data.LoanTermFrequencyType;
import org.apache.fineract.test.data.RepaymentFrequencyType;
import org.apache.fineract.test.data.TransactionProcessingStrategyCode;
+import org.apache.fineract.test.data.accounttype.AccountTypeResolver;
+import org.apache.fineract.test.data.accounttype.DefaultAccountType;
import org.apache.fineract.test.data.loanproduct.DefaultLoanProduct;
import org.apache.fineract.test.data.loanproduct.LoanProductResolver;
import org.apache.fineract.test.helper.Utils;
@@ -87,6 +92,8 @@ public class LoanRequestFactory {
public static final String DATE_WITHDRAWN_STRING = FORMATTER.format(Utils.now().minusMonths(1L));
public static final String DEFAULT_TRANSACTION_DATE = FORMATTER.format(Utils.now().minusMonths(1L));
+ private final AccountTypeResolver accountTypeResolver;
+
public PostLoansRequest defaultLoansRequest(Long clientId) {
return new PostLoansRequest()//
.clientId(clientId)//
@@ -312,4 +319,18 @@ public class LoanRequestFactory {
public static PostLoansLoanIdRequest defaultLoanContractTerminationRequest() {
return new PostLoansLoanIdRequest().dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE).note("Contract Termination");
}
+
+ public JournalEntryCommand defaultManualJournalEntryRequest(BigDecimal amount) {
+ final Long glAccountDebit = accountTypeResolver.resolve(DefaultAccountType.LOANS_RECEIVABLE);
+ final Long glAccountCredit = accountTypeResolver.resolve(DefaultAccountType.SUSPENSE_CLEARING_ACCOUNT);
+
+ return new JournalEntryCommand().amount(BigDecimal.TEN).officeId(1L).currencyCode("USD").locale(DEFAULT_LOCALE)
+ .dateFormat("uuuu-MM-dd").transactionDate(LocalDate.of(2024, 1, 1))
+ .addCreditsItem(new SingleDebitOrCreditEntryCommand().glAccountId(glAccountCredit).amount(amount))
+ .addDebitsItem(new SingleDebitOrCreditEntryCommand().glAccountId(glAccountDebit).amount(amount));
+ }
+
+ public JournalEntryCommand defaultManualJournalEntryRequest(BigDecimal amount, String externalAssetOwner) {
+ return defaultManualJournalEntryRequest(amount).externalAssetOwner(externalAssetOwner);
+ }
}
diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/common/JournalEntriesStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/common/JournalEntriesStepDef.java
index b1b2e09419..3aeca48bb8 100644
--- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/common/JournalEntriesStepDef.java
+++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/common/JournalEntriesStepDef.java
@@ -18,11 +18,14 @@
*/
package org.apache.fineract.test.stepdef.common;
+import static org.apache.fineract.test.stepdef.loan.LoanRescheduleStepDef.FORMATTER_EN;
import static org.assertj.core.api.Assertions.assertThat;
import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Then;
import java.io.IOException;
+import java.math.BigDecimal;
+import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
@@ -32,11 +35,14 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.GetJournalEntriesTransactionIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
+import org.apache.fineract.client.models.JournalEntryCommand;
import org.apache.fineract.client.models.JournalEntryTransactionItem;
+import org.apache.fineract.client.models.PostJournalEntriesResponse;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.JournalEntriesApi;
import org.apache.fineract.client.services.LoansApi;
import org.apache.fineract.test.data.TransactionType;
+import org.apache.fineract.test.factory.LoanRequestFactory;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.stepdef.AbstractStepDef;
@@ -55,6 +61,9 @@ public class JournalEntriesStepDef extends AbstractStepDef {
@Autowired
private JournalEntriesApi journalEntriesApi;
+ @Autowired
+ private LoanRequestFactory loanRequestFactory;
+
@Then("Loan Transactions tab has a {string} transaction with date {string} which has the following Journal entries:")
public void journalEntryDataCheck(String transactionType, String transactionDate, DataTable table) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
@@ -360,4 +369,131 @@ public class JournalEntriesStepDef extends AbstractStepDef {
assertThat(journalLinesActualList.stream().findFirst().get().size()).isZero();
}
+
+ public Response<PostJournalEntriesResponse> addManualJournalEntryWithoutExternalAssetOwner(String amount, String date)
+ throws IOException {
+ LocalDate transactionDate = LocalDate.parse(date, FORMATTER_EN);
+ JournalEntryCommand journalEntriesRequest = loanRequestFactory.defaultManualJournalEntryRequest(new BigDecimal(amount))
+ .transactionDate(transactionDate);
+ Response<PostJournalEntriesResponse> journalEntriesResponse = journalEntriesApi.createGLJournalEntry("", journalEntriesRequest)
+ .execute();
+ testContext().set(TestContextKey.MANUAL_JOURNAL_ENTRIES_REQUEST, journalEntriesRequest);
+ return journalEntriesResponse;
+ }
+
+ public Response<PostJournalEntriesResponse> addManualJournalEntryWithExternalAssetOwner(String amount, String date,
+ String externalAssetOwner) throws IOException {
+ LocalDate transactionDate = LocalDate.parse(date, FORMATTER_EN);
+ JournalEntryCommand journalEntriesRequest = loanRequestFactory
+ .defaultManualJournalEntryRequest(new BigDecimal(amount), externalAssetOwner).transactionDate(transactionDate);
+ Response<PostJournalEntriesResponse> journalEntriesResponse = journalEntriesApi.createGLJournalEntry("", journalEntriesRequest)
+ .execute();
+ testContext().set(TestContextKey.MANUAL_JOURNAL_ENTRIES_REQUEST, journalEntriesRequest);
+ return journalEntriesResponse;
+ }
+
+ @Then("Admin creates manual Journal entry with {string} amount and {string} date and unique External Asset Owner")
+ public void createManualJournalEntryWithExternalAssetOwner(String amount, String date) throws IOException {
+ String ownerExternalIdStored = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID);
+ Response<PostJournalEntriesResponse> journalEntriesResponse = addManualJournalEntryWithExternalAssetOwner(amount, date,
+ ownerExternalIdStored);
+
+ testContext().set(TestContextKey.MANUAL_JOURNAL_ENTRIES_RESPONSE, journalEntriesResponse);
+ ErrorHelper.checkSuccessfulApiCall(journalEntriesResponse);
+ }
+
+ @Then("Admin creates manual Journal entry with {string} amount and {string} date and empty External Asset Owner")
+ public void createManualJournalEntryWithEmptyExternalAssetOwner(String amount, String date) throws IOException {
+ Response<PostJournalEntriesResponse> journalEntriesResponse = addManualJournalEntryWithExternalAssetOwner(amount, date, "");
+
+ testContext().set(TestContextKey.MANUAL_JOURNAL_ENTRIES_RESPONSE, journalEntriesResponse);
+ ErrorHelper.checkSuccessfulApiCall(journalEntriesResponse);
+ }
+
+ @Then("Admin creates manual Journal entry with {string} amount and {string} date and without External Asset Owner")
+ public void createManualJournalEntryWithoutExternalAssetOwner(String amount, String date) throws IOException {
+ Response<PostJournalEntriesResponse> journalEntriesResponse = addManualJournalEntryWithoutExternalAssetOwner(amount, date);
+
+ testContext().set(TestContextKey.MANUAL_JOURNAL_ENTRIES_RESPONSE, journalEntriesResponse);
+ ErrorHelper.checkSuccessfulApiCall(journalEntriesResponse);
+ }
+
+ @Then("Verify manual Journal entry with External Asset Owner {string} and with the following Journal entries:")
+ public void checkManualJournalEntry(String externalAssetOwnerEnabled, DataTable table) {
+ Response<PostJournalEntriesResponse> journalEnriesResponse = testContext().get(TestContextKey.MANUAL_JOURNAL_ENTRIES_RESPONSE);
+ PostJournalEntriesResponse journalEntriesResponseBody = journalEnriesResponse.body();
+ String transactionId = journalEntriesResponseBody.getTransactionId();
+
+ JournalEntryCommand journalEntriesRequest = testContext().get(TestContextKey.MANUAL_JOURNAL_ENTRIES_REQUEST);
+
+ Response<GetJournalEntriesTransactionIdResponse> journalEntryDataResponse = null;
+ try {
+ journalEntryDataResponse = journalEntriesApi.retrieveAll1(//
+ null, //
+ null, //
+ null, //
+ null, //
+ null, //
+ null, //
+ null, //
+ transactionId, //
+ null, //
+ null, //
+ null, //
+ null, //
+ null, //
+ null, //
+ null, //
+ null, //
+ null, //
+ null, //
+ true//
+ ).execute();
+ ErrorHelper.checkSuccessfulApiCall(journalEntryDataResponse);
+ } catch (IOException e) {
+ log.error("Exception", e);
+ }
+
+ List<List<String>> data = table.asLists();
+ for (int i = 1; i < data.size(); i++) {
+ List<List<List<String>>> possibleActualValuesList = new ArrayList<>();
+ List<String> expectedValues = data.get(i);
+ if (Boolean.parseBoolean(externalAssetOwnerEnabled)) {
+ expectedValues
+ .add(journalEntriesRequest.getExternalAssetOwner() == null ? null : journalEntriesRequest.getExternalAssetOwner());
+ }
+ boolean containsAnyExpected = false;
+
+ GetJournalEntriesTransactionIdResponse journalEntryData = journalEntryDataResponse.body();
+
+ List<JournalEntryTransactionItem> journalLinesActual = journalEntryData.getPageItems();
+
+ List<List<String>> actualValuesList = journalLinesActual.stream().map(t -> {
+ List<String> actualValues = new ArrayList<>();
+ actualValues.add(t.getGlAccountType().getValue() == null ? null : t.getGlAccountType().getValue());
+ actualValues.add(t.getGlAccountCode() == null ? null : t.getGlAccountCode());
+ actualValues.add(t.getGlAccountName() == null ? null : t.getGlAccountName());
+ actualValues.add("DEBIT".equals(t.getEntryType().getValue()) ? String.valueOf(t.getAmount()) : null);
+ actualValues.add("CREDIT".equals(t.getEntryType().getValue()) ? String.valueOf(t.getAmount()) : null);
+ actualValues.add(String.valueOf(t.getManualEntry()).toLowerCase());
+ if (Boolean.parseBoolean(externalAssetOwnerEnabled)) {
+ actualValues.add(t.getExternalAssetOwner() == null ? null : t.getExternalAssetOwner());
+ }
+
+ return actualValues;
+ }).collect(Collectors.toList());
+
+ possibleActualValuesList.add(actualValuesList);
+
+ boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValues));
+ if (containsExpectedValues) {
+ containsAnyExpected = true;
+ }
+
+ assertThat(containsAnyExpected)
+ .as(ErrorMessageHelper.wrongValueInLineInJournalEntries(transactionId, i, possibleActualValuesList, expectedValues))
+ .isTrue();
+ }
+ }
+
}
diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java
index 3ccde040b9..b61a28e5c3 100644
--- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java
+++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java
@@ -232,4 +232,6 @@ public abstract class TestContextKey {
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALCULATION_CONTRACT_TERMINATION = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyInterestRecalculationContractTermination";
public static final String LOAN_CONTRACT_TERMINATION_RESPONSE = "loanContractTerminationResponse";
public static final String LOAN_UNDO_CONTRACT_TERMINATION_RESPONSE = "loanUndoContractTerminationResponse";
+ public static final String MANUAL_JOURNAL_ENTRIES_REQUEST = "manualJournalEntriesRequest";
+ public static final String MANUAL_JOURNAL_ENTRIES_RESPONSE = "manualJournalEntriesResponse";
}
diff --git a/fineract-e2e-tests-runner/src/test/resources/features/AssetExternalization.feature b/fineract-e2e-tests-runner/src/test/resources/features/AssetExternalization.feature
index c205844b61..468b4fd59c 100644
--- a/fineract-e2e-tests-runner/src/test/resources/features/AssetExternalization.feature
+++ b/fineract-e2e-tests-runner/src/test/resources/features/AssetExternalization.feature
@@ -1776,3 +1776,83 @@ Feature: Asset Externalization
| ASSET | 112603 | Interest/Fee Receivable | DEBIT | 0.33 |
| INCOME | 404000 | Interest Income | CREDIT | 0.33 |
When Global config "outstanding-interest-calculation-strategy-for-external-asset-transfer" value set to "TOTAL_OUTSTANDING_INTEREST"
+
+ @TestRailId:C3799 @AssetExternalizationJournalEntry
+ Scenario: Verify manual journal entry with External Asset Owner value if asset-externalization is enabled for existing loan - UC1
+ Given Global configuration "asset-externalization-of-non-active-loans" is enabled
+
+ When Admin sets the business date to "1 June 2025"
+ When Admin creates a client with random data
+ When Admin creates a new default Loan with date: "1 June 2025"
+ And Admin successfully approves the loan on "1 June 2025" with "1000" amount and expected disbursement date on "1 June 2025"
+ When Admin successfully disburse the loan on "1 June 2025" with "1000" EUR transaction amount
+ Then Loan status will be "ACTIVE"
+ When Admin makes asset externalization request by Loan ID with unique ownerExternalId, system-generated transferExternalId and the following data:
+ | Transaction type | settlementDate | purchasePriceRatio |
+ | sale | 2025-06-01 | 1 |
+ Then Asset externalization response has the correct Loan ID, transferExternalId
+ Then Fetching Asset externalization details by loan id gives numberOfElements: 1 with correct ownerExternalId and the following data:
+ | settlementDate | purchasePriceRatio | status | effectiveFrom | effectiveTo | Transaction type |
+ | 2025-06-01 | 1 | PENDING | 2025-06-01 | 9999-12-31 | SALE |
+
+ When Admin sets the business date to "26 June 2025"
+ Then Admin creates manual Journal entry with "131" amount and "26 June 2025" date and unique External Asset Owner
+ Then Verify manual Journal entry with External Asset Owner "true" and with the following Journal entries:
+ | Type | Account code | Account name | Debit | Credit | Manual Entry |
+ | ASSET | 112601 | Loans Receivable | 131.0 | | true |
+ | LIABILITY | 145023 | Suspense/Clearing account | | 131.0 | true |
+ Given Global configuration "asset-externalization-of-non-active-loans" is enabled
+
+ When Loan Pay-off is made on "26 June 2025"
+ Then Loan's all installments have obligations met
+
+ @TestRailId:C3800 @AssetExternalizationJournalEntry
+ Scenario: Verify manual journal entry with External Asset Owner empty value if asset-externalization is enabled - UC2
+ Given Global configuration "asset-externalization-of-non-active-loans" is enabled
+ When Admin sets the business date to "25 June 2025"
+ Then Admin creates manual Journal entry with "88" amount and "20 June 2025" date and without External Asset Owner
+ Then Verify manual Journal entry with External Asset Owner "true" and with the following Journal entries:
+ | Type | Account code | Account name | Debit | Credit | Manual Entry |
+ | ASSET | 112601 | Loans Receivable | 88.0 | | true |
+ | LIABILITY | 145023 | Suspense/Clearing account | | 88.0 | true |
+ Given Global configuration "asset-externalization-of-non-active-loans" is enabled
+
+ @TestRailId:C38001 @AssetExternalizationJournalEntry
+ Scenario: Verify manual journal entry with External Asset Owner empty value if asset-externalization is enabled for existing loan - UC3
+ Given Global configuration "asset-externalization-of-non-active-loans" is enabled
+
+ When Admin sets the business date to "1 June 2025"
+ When Admin creates a client with random data
+ When Admin creates a new default Loan with date: "1 June 2025"
+ And Admin successfully approves the loan on "1 June 2025" with "1000" amount and expected disbursement date on "1 June 2025"
+ When Admin successfully disburse the loan on "1 June 2025" with "1000" EUR transaction amount
+ Then Loan status will be "ACTIVE"
+ When Admin makes asset externalization request by Loan ID with unique ownerExternalId, system-generated transferExternalId and the following data:
+ | Transaction type | settlementDate | purchasePriceRatio |
+ | sale | 2025-06-01 | 1 |
+ Then Asset externalization response has the correct Loan ID, transferExternalId
+ Then Fetching Asset externalization details by loan id gives numberOfElements: 1 with correct ownerExternalId and the following data:
+ | settlementDate | purchasePriceRatio | status | effectiveFrom | effectiveTo | Transaction type |
+ | 2025-06-01 | 1 | PENDING | 2025-06-01 | 9999-12-31 | SALE |
+
+ When Admin sets the business date to "25 June 2025"
+ Then Admin creates manual Journal entry with "99" amount and "20 June 2025" date and without External Asset Owner
+ Then Verify manual Journal entry with External Asset Owner "true" and with the following Journal entries:
+ | Type | Account code | Account name | Debit | Credit | Manual Entry |
+ | ASSET | 112601 | Loans Receivable | 99.0 | | true |
+ | LIABILITY | 145023 | Suspense/Clearing account | | 99.0 | true |
+ Given Global configuration "asset-externalization-of-non-active-loans" is enabled
+
+ When Loan Pay-off is made on "25 June 2025"
+ Then Loan's all installments have obligations met
+
+ @TestRailId:C3802 @AssetExternalizationJournalEntry
+ Scenario: Verify manual journal entry with no External Asset Owner value if asset-externalization is disabled - UC4
+ Given Global configuration "asset-externalization-of-non-active-loans" is disabled
+ When Admin sets the business date to "25 June 2025"
+ Then Admin creates manual Journal entry with "250.05" amount and "15 June 2025" date and without External Asset Owner
+ Then Verify manual Journal entry with External Asset Owner "false" and with the following Journal entries:
+ | Type | Account code | Account name | Debit | Credit | Manual Entry |
+ | ASSET | 112601 | Loans Receivable | 250.05 | | true |
+ | LIABILITY | 145023 | Suspense/Clearing account | | 250.05 | true |
+ Given Global configuration "asset-externalization-of-non-active-loans" is enabled