From 23486d77427a5e1b907dbe6a018fb2aaffb95e37 Mon Sep 17 00:00:00 2001 From: Janos Haber Date: Wed, 5 Jul 2023 11:17:47 +0200 Subject: [PATCH] FINERACT-1926: COB Effective date fix - [x] Add effectiveTo filter to decide which transaction is not cancelled or already closed - [x] Integration test - [x] Database change log for UQ key status + externalTransferId --- .../LoanAccountOwnerTransferBusinessStep.java | 3 +- .../investor/module-changelog-master.xml | 1 + ...tatus_external_transfer_id_constraints.xml | 28 +++ ...nitiateExternalAssetOwnerTransferTest.java | 187 ++++++++++++++++++ 4 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 fineract-investor/src/main/resources/db/changelog/tenant/module/investor/parts/0010_external_transafer_status_external_transfer_id_constraints.xml diff --git a/fineract-investor/src/main/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStep.java b/fineract-investor/src/main/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStep.java index c629fd12f..c85f39342 100644 --- a/fineract-investor/src/main/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStep.java +++ b/fineract-investor/src/main/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStep.java @@ -65,7 +65,8 @@ public class LoanAccountOwnerTransferBusinessStep implements LoanCOBBusinessStep List transferDataList = externalAssetOwnerTransferRepository.findAll( (root, query, criteriaBuilder) -> criteriaBuilder.and(criteriaBuilder.equal(root.get("loanId"), loanId), criteriaBuilder.equal(root.get("settlementDate"), settlementDate), - root.get("status").in(List.of(ExternalTransferStatus.PENDING, ExternalTransferStatus.BUYBACK))), + root.get("status").in(List.of(ExternalTransferStatus.PENDING, ExternalTransferStatus.BUYBACK)), + criteriaBuilder.greaterThanOrEqualTo(root.get("effectiveDateTo"), FUTURE_DATE_9999_12_31)), Sort.by(Sort.Direction.ASC, "id")); int size = transferDataList.size(); diff --git a/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/module-changelog-master.xml b/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/module-changelog-master.xml index afdc51ec0..30a63050e 100644 --- a/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/module-changelog-master.xml +++ b/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/module-changelog-master.xml @@ -31,4 +31,5 @@ + diff --git a/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/parts/0010_external_transafer_status_external_transfer_id_constraints.xml b/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/parts/0010_external_transafer_status_external_transfer_id_constraints.xml new file mode 100644 index 000000000..0bd4fc839 --- /dev/null +++ b/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/parts/0010_external_transafer_status_external_transfer_id_constraints.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java index c662a73c0..cd5166e28 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java @@ -136,6 +136,193 @@ public class InitiateExternalAssetOwnerTransferTest { .glAccountId((long) transferAccount.getAccountID())); } + @Test + public void saleActiveLoanToExternalAssetOwnerWithCancelAndBuybackADayLater() { + try { + GlobalConfigurationHelper.manageConfigurations(REQUEST_SPEC, RESPONSE_SPEC, + GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true); + setInitialBusinessDate("2020-03-02"); + Integer clientID = createClient(); + Integer loanID = createLoanForClient(clientID); + addPenaltyForLoan(loanID, "10"); + + PostInitiateTransferResponse saleTransferResponse = createSaleTransfer(loanID, "2020-03-02"); + validateResponse(saleTransferResponse, loanID); + getAndValidateExternalAssetOwnerTransferByLoan(loanID, + ExpectedExternalTransferData.expected(PENDING, saleTransferResponse.getResourceExternalId(), "2020-03-02", "2020-03-02", + "9999-12-31", false, new BigDecimal("15767.420000"), new BigDecimal("15000.000000"), + new BigDecimal("757.420000"), new BigDecimal("10.000000"), new BigDecimal("0.000000"), + new BigDecimal("0.000000"))); + getAndValidateThereIsNoActiveMapping(saleTransferResponse.getResourceExternalId()); + PageExternalTransferData retrieveResponse = EXTERNAL_ASSET_OWNER_HELPER.retrieveTransfersByLoanId(loanID.longValue()); + retrieveResponse.getContent().forEach(transfer -> getAndValidateThereIsNoJournalEntriesForTransfer(transfer.getTransferId())); + + EXTERNAL_ASSET_OWNER_HELPER.cancelTransferByTransferExternalId(saleTransferResponse.getResourceExternalId()); + + getAndValidateExternalAssetOwnerTransferByLoan(loanID, + ExpectedExternalTransferData.expected(PENDING, saleTransferResponse.getResourceExternalId(), "2020-03-02", "2020-03-02", + "2020-03-02", false, new BigDecimal("15767.420000"), new BigDecimal("15000.000000"), + new BigDecimal("757.420000"), new BigDecimal("10.000000"), new BigDecimal("0.000000"), + new BigDecimal("0.000000")), + ExpectedExternalTransferData.expected(CANCELLED, saleTransferResponse.getResourceExternalId(), "2020-03-02", + "2020-03-02", "2020-03-02", false, new BigDecimal("15767.420000"), new BigDecimal("15000.000000"), + new BigDecimal("757.420000"), new BigDecimal("10.000000"), new BigDecimal("0.000000"), + new BigDecimal("0.000000"))); + PostInitiateTransferResponse oldSaleTransferResponse = saleTransferResponse; + saleTransferResponse = createSaleTransfer(loanID, "2020-03-02"); + validateResponse(saleTransferResponse, loanID); + + getAndValidateExternalAssetOwnerTransferByLoan(loanID, + ExpectedExternalTransferData.expected(PENDING, oldSaleTransferResponse.getResourceExternalId(), "2020-03-02", + "2020-03-02", "2020-03-02", false, new BigDecimal("15767.420000"), new BigDecimal("15000.000000"), + new BigDecimal("757.420000"), new BigDecimal("10.000000"), new BigDecimal("0.000000"), + new BigDecimal("0.000000")), + ExpectedExternalTransferData.expected(CANCELLED, oldSaleTransferResponse.getResourceExternalId(), "2020-03-02", + "2020-03-02", "2020-03-02", false, new BigDecimal("15767.420000"), new BigDecimal("15000.000000"), + new BigDecimal("757.420000"), new BigDecimal("10.000000"), new BigDecimal("0.000000"), + new BigDecimal("0.000000")), + ExpectedExternalTransferData.expected(PENDING, saleTransferResponse.getResourceExternalId(), "2020-03-02", "2020-03-02", + "9999-12-31", false, new BigDecimal("15767.420000"), new BigDecimal("15000.000000"), + new BigDecimal("757.420000"), new BigDecimal("10.000000"), new BigDecimal("0.000000"), + new BigDecimal("0.000000"))); + + updateBusinessDateAndExecuteCOBJob("2020-03-03"); + getAndValidateExternalAssetOwnerTransferByLoan(loanID, + ExpectedExternalTransferData.expected(PENDING, oldSaleTransferResponse.getResourceExternalId(), "2020-03-02", + "2020-03-02", "2020-03-02", false, new BigDecimal("15767.420000"), new BigDecimal("15000.000000"), + new BigDecimal("757.420000"), new BigDecimal("10.000000"), new BigDecimal("0.000000"), + new BigDecimal("0.000000")), + ExpectedExternalTransferData.expected(CANCELLED, oldSaleTransferResponse.getResourceExternalId(), "2020-03-02", + "2020-03-02", "2020-03-02", false, new BigDecimal("15767.420000"), new BigDecimal("15000.000000"), + new BigDecimal("757.420000"), new BigDecimal("10.000000"), new BigDecimal("0.000000"), + new BigDecimal("0.000000")), + ExpectedExternalTransferData.expected(PENDING, saleTransferResponse.getResourceExternalId(), "2020-03-02", "2020-03-02", + "2020-03-02", false, new BigDecimal("15767.420000"), new BigDecimal("15000.000000"), + new BigDecimal("757.420000"), new BigDecimal("10.000000"), new BigDecimal("0.000000"), + new BigDecimal("0.000000")), + ExpectedExternalTransferData.expected(ACTIVE, saleTransferResponse.getResourceExternalId(), "2020-03-02", "2020-03-03", + "9999-12-31", true, new BigDecimal("15767.420000"), new BigDecimal("15000.000000"), + new BigDecimal("757.420000"), new BigDecimal("10.000000"), new BigDecimal("0.000000"), + new BigDecimal("0.000000"))); + getAndValidateThereIsActiveMapping(loanID); + retrieveResponse = EXTERNAL_ASSET_OWNER_HELPER.retrieveTransfersByLoanId(loanID.longValue()); + LocalDate expectedDate = LocalDate.of(2020, 3, 2); + int initial = 2; + getAndValidateThereIsJournalEntriesForTransfer(retrieveResponse.getContent().get(initial + 1).getTransferId(), + ExpectedJournalEntryData.expected((long) ASSET_ACCOUNT.getAccountID(), (long) JournalEntryType.CREDIT.getValue(), + BigDecimal.valueOf(15757.420000), expectedDate, expectedDate), + ExpectedJournalEntryData.expected((long) FEE_PENALTY_ACCOUNT.getAccountID(), (long) JournalEntryType.CREDIT.getValue(), + BigDecimal.valueOf(10.000000), expectedDate, expectedDate), + ExpectedJournalEntryData.expected((long) TRANSFER_ACCOUNT.getAccountID(), (long) JournalEntryType.DEBIT.getValue(), + BigDecimal.valueOf(15767.420000), expectedDate, expectedDate), + ExpectedJournalEntryData.expected((long) ASSET_ACCOUNT.getAccountID(), (long) JournalEntryType.DEBIT.getValue(), + BigDecimal.valueOf(15757.420000), expectedDate, expectedDate), + ExpectedJournalEntryData.expected((long) FEE_PENALTY_ACCOUNT.getAccountID(), (long) JournalEntryType.DEBIT.getValue(), + BigDecimal.valueOf(10.000000), expectedDate, expectedDate), + ExpectedJournalEntryData.expected((long) TRANSFER_ACCOUNT.getAccountID(), (long) JournalEntryType.CREDIT.getValue(), + BigDecimal.valueOf(15767.420000), expectedDate, expectedDate)); + + PostInitiateTransferResponse buybackTransferResponse = createBuybackTransfer(loanID, "2020-03-03"); + validateResponse(buybackTransferResponse, loanID); + getAndValidateExternalAssetOwnerTransferByLoan(loanID, + ExpectedExternalTransferData.expected(PENDING, oldSaleTransferResponse.getResourceExternalId(), "2020-03-02", + "2020-03-02", "2020-03-02", false, new BigDecimal("15767.420000"), new BigDecimal("15000.000000"), + new BigDecimal("757.420000"), new BigDecimal("10.000000"), new BigDecimal("0.000000"), + new BigDecimal("0.000000")), + ExpectedExternalTransferData.expected(CANCELLED, oldSaleTransferResponse.getResourceExternalId(), "2020-03-02", + "2020-03-02", "2020-03-02", false, new BigDecimal("15767.420000"), new BigDecimal("15000.000000"), + new BigDecimal("757.420000"), new BigDecimal("10.000000"), new BigDecimal("0.000000"), + new BigDecimal("0.000000")), + ExpectedExternalTransferData.expected(PENDING, saleTransferResponse.getResourceExternalId(), "2020-03-02", "2020-03-02", + "2020-03-02", false, new BigDecimal("15767.420000"), new BigDecimal("15000.000000"), + new BigDecimal("757.420000"), new BigDecimal("10.000000"), new BigDecimal("0.000000"), + new BigDecimal("0.000000")), + ExpectedExternalTransferData.expected(ACTIVE, saleTransferResponse.getResourceExternalId(), "2020-03-02", "2020-03-03", + "9999-12-31", true, new BigDecimal("15767.420000"), new BigDecimal("15000.000000"), + new BigDecimal("757.420000"), new BigDecimal("10.000000"), new BigDecimal("0.000000"), + new BigDecimal("0.000000")), + ExpectedExternalTransferData.expected(BUYBACK, buybackTransferResponse.getResourceExternalId(), "2020-03-03", + "2020-03-03", "9999-12-31", false, new BigDecimal("15767.420000"), new BigDecimal("15000.000000"), + new BigDecimal("757.420000"), new BigDecimal("10.000000"), new BigDecimal("0.000000"), + new BigDecimal("0.000000"))); + getAndValidateThereIsActiveMapping(loanID); + retrieveResponse = EXTERNAL_ASSET_OWNER_HELPER.retrieveTransfersByLoanId(loanID.longValue()); + getAndValidateThereIsNoJournalEntriesForTransfer(retrieveResponse.getContent().get(initial + 2).getTransferId()); + + LOAN_TRANSACTION_HELPER.makeLoanRepayment((long) loanID, new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy") + .transactionDate(dateFormatter.format(expectedDate)).locale("en").transactionAmount(5.0)); + LocalDate repaymentSubmittedOnDate = expectedDate.plusDays(1); + getAndValidateOwnerJournalEntries(ownerExternalId, + ExpectedJournalEntryData.expected((long) ASSET_ACCOUNT.getAccountID(), (long) JournalEntryType.DEBIT.getValue(), + BigDecimal.valueOf(15757.420000), expectedDate, expectedDate), + ExpectedJournalEntryData.expected((long) FEE_PENALTY_ACCOUNT.getAccountID(), (long) JournalEntryType.DEBIT.getValue(), + BigDecimal.valueOf(10.000000), expectedDate, expectedDate), + ExpectedJournalEntryData.expected((long) FEE_PENALTY_ACCOUNT.getAccountID(), (long) JournalEntryType.CREDIT.getValue(), + BigDecimal.valueOf(5.000000), expectedDate, repaymentSubmittedOnDate), + ExpectedJournalEntryData.expected((long) ASSET_ACCOUNT.getAccountID(), (long) JournalEntryType.DEBIT.getValue(), + BigDecimal.valueOf(5.000000), expectedDate, repaymentSubmittedOnDate)); + + updateBusinessDateAndExecuteCOBJob("2020-03-04"); + getAndValidateExternalAssetOwnerTransferByLoan(loanID, + ExpectedExternalTransferData.expected(PENDING, oldSaleTransferResponse.getResourceExternalId(), "2020-03-02", + "2020-03-02", "2020-03-02", false, new BigDecimal("15767.420000"), new BigDecimal("15000.000000"), + new BigDecimal("757.420000"), new BigDecimal("10.000000"), new BigDecimal("0.000000"), + new BigDecimal("0.000000")), + ExpectedExternalTransferData.expected(CANCELLED, oldSaleTransferResponse.getResourceExternalId(), "2020-03-02", + "2020-03-02", "2020-03-02", false, new BigDecimal("15767.420000"), new BigDecimal("15000.000000"), + new BigDecimal("757.420000"), new BigDecimal("10.000000"), new BigDecimal("0.000000"), + new BigDecimal("0.000000")), + ExpectedExternalTransferData.expected(PENDING, saleTransferResponse.getResourceExternalId(), "2020-03-02", "2020-03-02", + "2020-03-02", false, new BigDecimal("15767.420000"), new BigDecimal("15000.000000"), + new BigDecimal("757.420000"), new BigDecimal("10.000000"), new BigDecimal("0.000000"), + new BigDecimal("0.000000")), + ExpectedExternalTransferData.expected(ACTIVE, saleTransferResponse.getResourceExternalId(), "2020-03-02", "2020-03-03", + "2020-03-03", true, new BigDecimal("15767.420000"), new BigDecimal("15000.000000"), + new BigDecimal("757.420000"), new BigDecimal("10.000000"), new BigDecimal("0.000000"), + new BigDecimal("0.000000")), + ExpectedExternalTransferData.expected(BUYBACK, buybackTransferResponse.getResourceExternalId(), "2020-03-03", + "2020-03-03", "2020-03-03", true, new BigDecimal("15762.420000"), new BigDecimal("15000.000000"), + new BigDecimal("757.420000"), new BigDecimal("5.000000"), new BigDecimal("0.000000"), + new BigDecimal("0.000000"))); + getAndValidateThereIsNoActiveMapping(saleTransferResponse.getResourceExternalId()); + retrieveResponse = EXTERNAL_ASSET_OWNER_HELPER.retrieveTransfersByLoanId(loanID.longValue()); + expectedDate = LocalDate.of(2020, 3, 3); + getAndValidateThereIsJournalEntriesForTransfer(retrieveResponse.getContent().get(initial + 2).getTransferId(), + ExpectedJournalEntryData.expected((long) ASSET_ACCOUNT.getAccountID(), (long) JournalEntryType.DEBIT.getValue(), + BigDecimal.valueOf(15757.420000), expectedDate, expectedDate), + ExpectedJournalEntryData.expected((long) FEE_PENALTY_ACCOUNT.getAccountID(), (long) JournalEntryType.DEBIT.getValue(), + BigDecimal.valueOf(5.000000), expectedDate, expectedDate), + ExpectedJournalEntryData.expected((long) TRANSFER_ACCOUNT.getAccountID(), (long) JournalEntryType.CREDIT.getValue(), + BigDecimal.valueOf(15762.420000), expectedDate, expectedDate), + ExpectedJournalEntryData.expected((long) ASSET_ACCOUNT.getAccountID(), (long) JournalEntryType.CREDIT.getValue(), + BigDecimal.valueOf(15757.420000), expectedDate, expectedDate), + ExpectedJournalEntryData.expected((long) FEE_PENALTY_ACCOUNT.getAccountID(), (long) JournalEntryType.CREDIT.getValue(), + BigDecimal.valueOf(5.000000), expectedDate, expectedDate), + ExpectedJournalEntryData.expected((long) TRANSFER_ACCOUNT.getAccountID(), (long) JournalEntryType.DEBIT.getValue(), + BigDecimal.valueOf(15762.420000), expectedDate, expectedDate)); + LocalDate previousDayDate = LocalDate.of(2020, 3, 2); + getAndValidateOwnerJournalEntries(ownerExternalId, + ExpectedJournalEntryData.expected((long) ASSET_ACCOUNT.getAccountID(), (long) JournalEntryType.DEBIT.getValue(), + BigDecimal.valueOf(15757.420000), previousDayDate, previousDayDate), + ExpectedJournalEntryData.expected((long) FEE_PENALTY_ACCOUNT.getAccountID(), (long) JournalEntryType.DEBIT.getValue(), + BigDecimal.valueOf(10.000000), previousDayDate, previousDayDate), + ExpectedJournalEntryData.expected((long) FEE_PENALTY_ACCOUNT.getAccountID(), (long) JournalEntryType.CREDIT.getValue(), + BigDecimal.valueOf(5.000000), previousDayDate, expectedDate), + ExpectedJournalEntryData.expected((long) ASSET_ACCOUNT.getAccountID(), (long) JournalEntryType.DEBIT.getValue(), + BigDecimal.valueOf(5.000000), previousDayDate, expectedDate), + ExpectedJournalEntryData.expected((long) ASSET_ACCOUNT.getAccountID(), (long) JournalEntryType.DEBIT.getValue(), + BigDecimal.valueOf(9.680000), expectedDate, expectedDate), + ExpectedJournalEntryData.expected((long) INCOME_ACCOUNT.getAccountID(), (long) JournalEntryType.CREDIT.getValue(), + BigDecimal.valueOf(9.680000), expectedDate, expectedDate), + ExpectedJournalEntryData.expected((long) ASSET_ACCOUNT.getAccountID(), (long) JournalEntryType.CREDIT.getValue(), + BigDecimal.valueOf(15757.420000), expectedDate, expectedDate), + ExpectedJournalEntryData.expected((long) FEE_PENALTY_ACCOUNT.getAccountID(), (long) JournalEntryType.CREDIT.getValue(), + BigDecimal.valueOf(5.000000), expectedDate, expectedDate)); + } finally { + cleanUpAndRestoreBusinessDate(); + } + } + @Test public void saleActiveLoanToExternalAssetOwnerAndBuybackADayLater() { try {