You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ar...@apache.org on 2024/12/13 15:48:12 UTC
(fineract) branch develop updated: FINERACT-2060: Progressive loan - final accrual calculation on closed loan
This is an automated email from the ASF dual-hosted git repository.
arnold pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push:
new 136bb0009 FINERACT-2060: Progressive loan - final accrual calculation on closed loan
136bb0009 is described below
commit 136bb0009ab3830045dbf0dfb09c7cde3a0162ba
Author: Marta Jankovics <ma...@gmail.com>
AuthorDate: Wed Dec 11 12:42:42 2024 +0100
FINERACT-2060: Progressive loan - final accrual calculation on closed loan
---
.../fineract/test/helper/ErrorMessageHelper.java | 124 ++++-------
.../test/stepdef/common/BatchApiStepDef.java | 4 +-
.../test/stepdef/common/JournalEntriesStepDef.java | 8 +-
.../fineract/test/stepdef/loan/LoanStepDef.java | 25 ++-
.../test/resources/features/EMICalculation.feature | 2 +
.../resources/features/LoanAccrualActivity.feature | 15 +-
.../test/resources/features/LoanChargeback.feature | 12 +-
.../test/resources/features/LoanRepayment.feature | 10 +-
.../loanaccount/data/AccrualChargeData.java | 1 +
.../loanaccount/data/AccrualPeriodData.java | 10 +
.../loanaccount/data/LoanScheduleAccrualData.java | 201 -----------------
.../portfolio/loanaccount/domain/Loan.java | 11 +-
.../LoanAccrualActivityProcessingService.java | 9 +-
...AccrualTransactionBusinessEventServiceImpl.java | 10 +-
.../service/LoanAccrualsProcessingService.java | 8 +-
.../service/LoanWritePlatformService.java | 11 -
.../domain/LoanAccountDomainServiceJpa.java | 37 ++--
.../AddPeriodicAccrualEntriesForLoansTasklet.java | 2 +-
...nRescheduleRequestWritePlatformServiceImpl.java | 8 +-
.../LoanAccrualActivityProcessingServiceImpl.java | 221 +++++++++++--------
...rviceImpl.java => LoanAccrualEventService.java} | 45 ++--
.../service/LoanAccrualsProcessingServiceImpl.java | 211 ++++++++----------
.../LoanChargeWritePlatformServiceImpl.java | 25 ++-
.../LoanStatusChangePlatformServiceImpl.java | 16 +-
.../service/LoanTransactionAssembler.java | 22 +-
.../LoanWritePlatformServiceJpaRepositoryImpl.java | 238 ++++++++-------------
.../starter/LoanAccountConfiguration.java | 37 ++--
.../LoanChargeWritePlatformServiceImplTest.java | 3 +
...nWritePlatformServiceJpaRepositoryImplTest.java | 4 -
.../integrationtests/BaseLoanIntegrationTest.java | 13 +-
.../LoanChargeProgressiveTest.java | 12 +-
...ebackWithCreditAllocationsIntegrationTests.java | 4 +
.../integrationtests/LoanInterestRefundTest.java | 69 +++---
.../LoanTransactionAccrualActivityPostingTest.java | 64 +++---
34 files changed, 628 insertions(+), 864 deletions(-)
diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java
index 08bd21ff5..4bc6dbdfe 100644
--- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java
+++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java
@@ -348,9 +348,12 @@ public final class ErrorMessageHelper {
}
public static String wrongLoanStatus(Integer actual, Integer expected) {
- String actualToStr = actual.toString();
- String expectedToStr = expected.toString();
- return String.format("Wrong Loan status ID. Actual ID is: %s - But expected ID is: %s", actualToStr, expectedToStr);
+ return wrongLoanStatus(null, actual, expected);
+ }
+
+ public static String wrongLoanStatus(String resourceId, Integer actual, Integer expected) {
+ return String.format("Wrong Loan status ID of resource %s. Actual ID is: %s - But expected ID is: %s", resourceId, actual,
+ expected);
}
public static String wrongFraudFlag(Boolean actualFraudStatus, Boolean expectedFraudStatus) {
@@ -446,109 +449,54 @@ public final class ErrorMessageHelper {
return String.format("Idempotency key is not matching: Actual value is: %s - But expected value is: %s", actual, expected);
}
- public static String wrongNumberOfLinesInRepaymentSchedule(int actual, int expected) {
- return wrongNumberOfLinesInRepaymentSchedule(null, actual, expected);
- }
-
public static String wrongNumberOfLinesInRepaymentSchedule(String resourceId, int actual, int expected) {
- String actualStr = String.valueOf(actual);
- String expectedStr = String.valueOf(expected);
- String prefx = "Number of lines in Repayment schedule";
- String postfx = " is not correct. Actual value is: %s - Expected value is: %s";
- if (resourceId != null) {
- return String.format(prefx + " of resource %s" + postfx, resourceId, actualStr, expectedStr);
- }
- return String.format(prefx + postfx, actualStr, expectedStr);
- }
-
- public static String wrongValueInLineInRepaymentSchedule(int line, List<List<String>> actual, List<String> expected) {
- return wrongValueInLineInRepaymentSchedule(null, line, actual, expected);
+ return String.format("Number of lines in Repayment schedule of resource %s is not correct. " //
+ + "Actual value is: %s - But expected value is: %s", resourceId, actual, expected);
}
- public static String wrongValueInLineInRepaymentSchedule(String resourceId, int line, List<List<String>> actual,
+ public static String wrongValueInLineInRepaymentSchedule(String resourceId, int line, List<List<String>> actualList,
List<String> expected) {
- String lineStr = String.valueOf(line);
- String expectedStr = expected.toString();
- StringBuilder sb = new StringBuilder();
- for (List<String> innerList : actual) {
- sb.append(innerList.toString());
- sb.append(System.lineSeparator());
- }
- String prefx = "%nWrong value in Repayment schedule";
- String postfx = " tab line %s. %nActual values in line (with the same due date) are: %n%s %nExpected values in line: %n%s";
- if (resourceId != null) {
- return String.format(prefx + " of resource %s" + postfx, resourceId, lineStr, sb.toString(), expectedStr);
- }
- return String.format(prefx + postfx, lineStr, sb.toString(), expectedStr);
- }
-
- public static String wrongValueInLineInTransactionsTab(int line, List<List<String>> actual, List<String> expected) {
- return wrongValueInLineInTransactionsTab(null, line, actual, expected);
- }
-
- public static String wrongValueInLineInTransactionsTab(String resourceId, int line, List<List<String>> actual, List<String> expected) {
- String lineStr = String.valueOf(line);
- String expectedStr = expected.toString();
- StringBuilder sb = new StringBuilder();
- for (List<String> innerList : actual) {
- sb.append(innerList.toString());
- sb.append(System.lineSeparator());
- }
- String prefx = "%nWrong value in Transactions tab";
- String postfx = " line %s. %nActual values in line (with the same date) are: %n%s %nExpected values in line: %n%s";
- if (resourceId != null) {
- return String.format(prefx + " of resource %s" + postfx, resourceId, lineStr, sb.toString(), expectedStr);
- }
- return String.format(prefx + postfx, lineStr, sb.toString(), expectedStr);
+ String actual = actualList.stream().map(Object::toString).collect(Collectors.joining(System.lineSeparator()));
+ return String.format("%nWrong value in Repayment schedule of resource %s tab line %s." //
+ + "%nActual values in line (with the same due date) are: %n%s - But expected values in line: %n%s", resourceId, line,
+ actual, expected);
}
- public static String nrOfLinesWrongInTransactionsTab(int actual, int expected) {
- return nrOfLinesWrongInTransactionsTab(null, actual, expected);
+ public static String wrongValueInLineInTransactionsTab(String resourceId, int line, List<List<String>> actualList,
+ List<String> expected) {
+ String actual = actualList.stream().map(Object::toString).collect(Collectors.joining(System.lineSeparator()));
+ return String.format("%nWrong value in Transactions tab of resource %s line %s." //
+ + "%nActual values in line (with the same date) are: %n%s %nExpected values in line: %n%s", resourceId, line, actual,
+ expected);
}
public static String nrOfLinesWrongInTransactionsTab(String resourceId, int actual, int expected) {
- String actualStr = String.valueOf(actual);
- String expectedStr = String.valueOf(expected);
-
- String prefx = "%nNumber of lines does not match in Transactions tab and expected datatable";
- String postfx = ". %nNumber of transaction tab lines: %s %nNumber of expected datatable lines: %s%n";
- if (resourceId != null) {
- return String.format(prefx + " of resource %s" + postfx, resourceId, actualStr, expectedStr);
- }
- return String.format(prefx + postfx, actualStr, expectedStr);
+ return String.format("%nNumber of lines does not match in Transactions tab and expected datatable of resource %s." //
+ + "%nNumber of transaction tab lines: %s %nNumber of expected datatable lines: %s%n", resourceId, actual, expected);
}
- public static String wrongValueInLineInChargesTab(int line, List<List<String>> actual, List<String> expected) {
- String lineStr = String.valueOf(line);
- String expectedStr = expected.toString();
- StringBuilder sb = new StringBuilder();
- for (List<String> innerList : actual) {
- sb.append(innerList.toString());
- sb.append(System.lineSeparator());
- }
-
- return String.format(
- "%nWrong value in Charges tab line %s. %nActual values in line (with the same date) are: %n%s %nExpected values in line: %n%s",
- lineStr, sb.toString(), expectedStr);
+ public static String wrongValueInLineInChargesTab(String resourceId, int line, List<List<String>> actualList, List<String> expected) {
+ String actual = actualList.stream().map(Object::toString).collect(Collectors.joining(System.lineSeparator()));
+ return String.format("%nWrong value in Charges tab of resource %s line %s." //
+ + "%nActual values in line (with the same date) are: %n%s %nExpected values in line: %n%s", resourceId, line, actual,
+ expected);
}
public static String wrongValueInLineInJournalEntries(int line, List<List<List<String>>> actual, List<String> expected) {
- String lineStr = String.valueOf(line);
- String expectedStr = expected.toString();
- StringBuilder sb = new StringBuilder();
- for (List<List<String>> innerList : actual) {
- sb.append(innerList.toString());
- sb.append(System.lineSeparator());
- }
+ return wrongValueInLineInJournalEntries(null, line, actual, expected);
+ }
- return String.format(
- "%nWrong value in Journal entries line %s. %nActual values for the possible transactions in line (with the same date) are: %n%s %nExpected values in line: %n%s",
- lineStr, sb.toString(), expectedStr);
+ public static String wrongValueInLineInJournalEntries(String resourceId, int line, List<List<List<String>>> actualList,
+ List<String> expected) {
+ String actual = actualList.stream().map(Object::toString).collect(Collectors.joining(System.lineSeparator()));
+ return String.format("%nWrong value in Journal entries of resource %s line %s." //
+ + "%nActual values for the possible transactions in line (with the same date) are: %n%s %nExpected values in line: %n%s",
+ resourceId, line, actual, expected);
}
public static String wrongDataInJournalEntriesGlAccountType(int line, String actual, String expected) {
- return String.format("Wrong data in Journal entries, line %s / GL account type. Actual value is: %s - But expected value is: %s",
- line, actual, expected);
+ return String.format("Wrong data in Journal entries, line %s / GL account type. " //
+ + "Actual value is: %s - But expected value is: %s", line, actual, expected);
}
public static String wrongDataInJournalEntriesGlAccountCode(int line, String actual, String expected) {
diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/common/BatchApiStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/common/BatchApiStepDef.java
index e5d611bea..6f80c2b68 100644
--- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/common/BatchApiStepDef.java
+++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/common/BatchApiStepDef.java
@@ -865,7 +865,9 @@ public class BatchApiStepDef extends AbstractStepDef {
Integer statusIdActual = status.getId();
Integer statusIdExpected = LoanStatus.APPROVED.value;
- assertThat(statusIdActual).as(ErrorMessageHelper.wrongLoanStatus(statusIdActual, statusIdExpected)).isEqualTo(statusIdExpected);
+ String resourceId = String.valueOf(response.body().getId());
+ assertThat(statusIdActual).as(ErrorMessageHelper.wrongLoanStatus(resourceId, statusIdActual, statusIdExpected))
+ .isEqualTo(statusIdExpected);
}
@Then("Nr. {int} Client creation was rolled back")
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 97191a40a..f2f74e20d 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
@@ -59,6 +59,7 @@ public class JournalEntriesStepDef extends AbstractStepDef {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
+ String resourceId = String.valueOf(loanId);
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
@@ -132,7 +133,8 @@ public class JournalEntriesStepDef extends AbstractStepDef {
}
}
assertThat(containsAnyExpected)
- .as(ErrorMessageHelper.wrongValueInLineInJournalEntries(i, possibleActualValuesList, expectedValues)).isTrue();
+ .as(ErrorMessageHelper.wrongValueInLineInJournalEntries(resourceId, i, possibleActualValuesList, expectedValues))
+ .isTrue();
}
}
@@ -141,6 +143,7 @@ public class JournalEntriesStepDef extends AbstractStepDef {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
+ String resourceId = String.valueOf(loanId);
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
@@ -218,7 +221,8 @@ public class JournalEntriesStepDef extends AbstractStepDef {
}
}
assertThat(containsAnyExpected)
- .as(ErrorMessageHelper.wrongValueInLineInJournalEntries(i, possibleActualValuesList, expectedValues)).isTrue();
+ .as(ErrorMessageHelper.wrongValueInLineInJournalEntries(resourceId, i, possibleActualValuesList, expectedValues))
+ .isTrue();
}
}
}
diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java
index 20d50d2af..e1b4cdcb9 100644
--- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java
+++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java
@@ -1298,6 +1298,8 @@ public class LoanStepDef extends AbstractStepDef {
public void disburseLoan(String actualDisbursementDate, String transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
+ String resourceId = String.valueOf(loanId);
+
PostLoansLoanIdRequest disburseRequest = LoanRequestFactory.defaultLoanDisburseRequest()
.actualDisbursementDate(actualDisbursementDate).transactionAmount(new BigDecimal(transactionAmount));
@@ -1310,7 +1312,7 @@ public class LoanStepDef extends AbstractStepDef {
Long statusExpected = Long.valueOf(loanDetails.body().getStatus().getId());
assertThat(statusActual)//
- .as(ErrorMessageHelper.wrongLoanStatus(Math.toIntExact(statusActual), Math.toIntExact(statusExpected)))//
+ .as(ErrorMessageHelper.wrongLoanStatus(resourceId, Math.toIntExact(statusActual), Math.toIntExact(statusExpected)))//
.isEqualTo(statusExpected);//
eventCheckHelper.disburseLoanEventCheck(loanId);
eventCheckHelper.loanDisbursalTransactionEventCheck(loanDisburseResponse);
@@ -1320,6 +1322,8 @@ public class LoanStepDef extends AbstractStepDef {
public void disburseLoanWithoutAutoDownpayment(String actualDisbursementDate, String transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
+ String resourceId = String.valueOf(loanId);
+
PostLoansLoanIdRequest disburseRequest = LoanRequestFactory.defaultLoanDisburseRequest()
.actualDisbursementDate(actualDisbursementDate).transactionAmount(new BigDecimal(transactionAmount));
@@ -1333,7 +1337,7 @@ public class LoanStepDef extends AbstractStepDef {
Long statusExpected = Long.valueOf(loanDetails.body().getStatus().getId());
assertThat(statusActual)//
- .as(ErrorMessageHelper.wrongLoanStatus(Math.toIntExact(statusActual), Math.toIntExact(statusExpected)))//
+ .as(ErrorMessageHelper.wrongLoanStatus(resourceId, Math.toIntExact(statusActual), Math.toIntExact(statusExpected)))//
.isEqualTo(statusExpected);//
eventCheckHelper.disburseLoanEventCheck(loanId);
eventCheckHelper.loanDisbursalTransactionEventCheck(loanDisburseResponse);
@@ -1773,6 +1777,7 @@ public class LoanStepDef extends AbstractStepDef {
public void loanChargesGivenChargeDataCheck(DataTable table) throws IOException {
Response<PostLoansResponse> loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanCreateResponse.body().getLoanId();
+ String resourceId = String.valueOf(loanId);
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "charges", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
@@ -1786,14 +1791,15 @@ public class LoanStepDef extends AbstractStepDef {
boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValues));
- assertThat(containsExpectedValues).as(ErrorMessageHelper.wrongValueInLineInChargesTab(1, actualValuesList, expectedValues))
- .isTrue();
+ assertThat(containsExpectedValues)
+ .as(ErrorMessageHelper.wrongValueInLineInChargesTab(resourceId, 1, actualValuesList, expectedValues)).isTrue();
}
@Then("Loan Charges tab has the following data:")
public void loanChargesTabCheck(DataTable table) throws IOException {
Response<PostLoansResponse> loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanCreateResponse.body().getLoanId();
+ String resourceId = String.valueOf(loanId);
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "charges", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
@@ -1808,8 +1814,8 @@ public class LoanStepDef extends AbstractStepDef {
boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValues));
- assertThat(containsExpectedValues).as(ErrorMessageHelper.wrongValueInLineInChargesTab(i, actualValuesList, expectedValues))
- .isTrue();
+ assertThat(containsExpectedValues)
+ .as(ErrorMessageHelper.wrongValueInLineInChargesTab(resourceId, i, actualValuesList, expectedValues)).isTrue();
}
}
@@ -1846,6 +1852,7 @@ public class LoanStepDef extends AbstractStepDef {
public void loanStatus(String statusExpected) throws IOException {
Response<PostLoansResponse> loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanCreateResponse.body().getLoanId();
+ String resourceId = String.valueOf(loanId);
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
@@ -1855,7 +1862,7 @@ public class LoanStepDef extends AbstractStepDef {
LoanStatus loanStatusExpected = LoanStatus.valueOf(statusExpected);
Integer loanStatusExpectedValue = loanStatusExpected.getValue();
- assertThat(loanStatusActualValue).as(ErrorMessageHelper.wrongLoanStatus(loanStatusActualValue, loanStatusExpectedValue))
+ assertThat(loanStatusActualValue).as(ErrorMessageHelper.wrongLoanStatus(resourceId, loanStatusActualValue, loanStatusExpectedValue))
.isEqualTo(loanStatusExpectedValue);
}
@@ -2776,7 +2783,9 @@ public class LoanStepDef extends AbstractStepDef {
assertNotNull(loanDetails.body().getStatus());
final Long statusExpected = Long.valueOf(loanDetails.body().getStatus().getId());
- assertThat(statusActual).as(ErrorMessageHelper.wrongLoanStatus(Math.toIntExact(statusActual), Math.toIntExact(statusExpected)))
+ String resourceId = String.valueOf(loanId);
+ assertThat(statusActual)
+ .as(ErrorMessageHelper.wrongLoanStatus(resourceId, Math.toIntExact(statusActual), Math.toIntExact(statusExpected)))
.isEqualTo(statusExpected);
eventCheckHelper.disburseLoanEventCheck(loanId);
eventCheckHelper.loanDisbursalTransactionEventCheck(loanDisburseResponse);
diff --git a/fineract-e2e-tests-runner/src/test/resources/features/EMICalculation.feature b/fineract-e2e-tests-runner/src/test/resources/features/EMICalculation.feature
index ece05f178..3966682d8 100644
--- a/fineract-e2e-tests-runner/src/test/resources/features/EMICalculation.feature
+++ b/fineract-e2e-tests-runner/src/test/resources/features/EMICalculation.feature
@@ -5231,6 +5231,7 @@ Feature: EMI calculation and repayment schedule checks for interest bearing loan
| 22 January 2021 | Accrual | 5.7 | 0.0 | 5.7 | 0.0 | 0.0 | 0.0 | false | false |
| 22 January 2021 | Merchant Issued Refund | 1000.0 | 914.37 | 5.42 | 0.0 | 0.0 | 0.0 | false | true |
| 22 January 2021 | Interest Refund | 5.42 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | true |
+ | 22 January 2021 | Accrual Adjustment | 0.28 | 0.0 | 0.28 | 0.0 | 0.0 | 0.0 | false | false |
@TestRailId:C3302
Scenario: UC18-2 - In case of repayment reversal the Interest Refund transaction needs to be recalculated
@@ -5288,6 +5289,7 @@ Feature: EMI calculation and repayment schedule checks for interest bearing loan
| 22 January 2021 | Merchant Issued Refund | 1000.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | true |
| 22 January 2021 | Interest Refund | 5.7 | 0.0 | 5.7 | 0.0 | 0.0 | 0.0 | false | true |
| 23 January 2021 | Credit Balance Refund | 85.63 | 85.63 | 0.0 | 0.0 | 0.0 | 85.63 | false | true |
+ | 22 January 2021 | Accrual | 5.42 | 0.0 | 5.42 | 0.0 | 0.0 | 0.0 | false | false |
And In Loan Transactions the "2"th Transaction has Transaction type="Repayment" and is reverted
@TestRailId:C3303
diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanAccrualActivity.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanAccrualActivity.feature
index 5bf616d7d..c16a4f24c 100644
--- a/fineract-e2e-tests-runner/src/test/resources/features/LoanAccrualActivity.feature
+++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanAccrualActivity.feature
@@ -1331,10 +1331,8 @@ Feature: LoanAccrualActivity
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false | false |
- | 02 January 2024 | Accrual | 0.33 | 0.0 | 0.33 | 0.0 | 0.0 | 0.0 | false | false |
- | 03 January 2024 | Accrual | 0.33 | 0.0 | 0.33 | 0.0 | 0.0 | 0.0 | false | false |
| 04 January 2024 | Repayment | 1004.1 | 1000.0 | 4.1 | 0.0 | 0.0 | 0.0 | true | false |
- | 04 January 2024 | Accrual | 0.32 | 0.0 | 0.32 | 0.0 | 0.0 | 0.0 | false | false |
+ | 04 January 2024 | Accrual | 4.1 | 0.0 | 4.1 | 0.0 | 0.0 | 0.0 | false | false |
# --- Accrual activity ---
When Admin sets the business date to "07 January 2024"
When Admin runs inline COB job for Loan
@@ -1351,11 +1349,9 @@ Feature: LoanAccrualActivity
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false | false |
- | 02 January 2024 | Accrual | 0.33 | 0.0 | 0.33 | 0.0 | 0.0 | 0.0 | false | false |
- | 03 January 2024 | Accrual | 0.33 | 0.0 | 0.33 | 0.0 | 0.0 | 0.0 | false | false |
+ | 04 January 2024 | Accrual | 4.1 | 0.0 | 4.1 | 0.0 | 0.0 | 0.0 | false | false |
| 04 January 2024 | Repayment | 1004.1 | 1000.0 | 4.1 | 0.0 | 0.0 | 0.0 | true | false |
- | 04 January 2024 | Accrual | 0.32 | 0.0 | 0.32 | 0.0 | 0.0 | 0.0 | false | false |
- | 05 January 2024 | Accrual | 0.33 | 0.0 | 0.33 | 0.0 | 0.0 | 0.0 | false | false |
+ | 05 January 2024 | Accrual Adjustment | 2.79 | 0.0 | 2.79 | 0.0 | 0.0 | 0.0 | false | false |
| 06 January 2024 | Accrual | 0.33 | 0.0 | 0.33 | 0.0 | 0.0 | 0.0 | false | false |
| 06 January 2024 | Accrual Activity | 1.64 | 0.0 | 1.64 | 0.0 | 0.0 | 0.0 | false | false |
@@ -1481,9 +1477,8 @@ Feature: LoanAccrualActivity
| 05 January 2024 | Accrual | 0.33 | 0.0 | 0.33 | 0.0 | 0.0 | 0.0 | false | false |
| 06 January 2024 | Accrual | 0.33 | 0.0 | 0.33 | 0.0 | 0.0 | 0.0 | false | false |
| 06 January 2024 | Accrual Activity | 1.64 | 0.0 | 1.64 | 0.0 | 0.0 | 0.0 | false | false |
- | 07 January 2024 | Accrual | 0.25 | 0.0 | 0.25 | 0.0 | 0.0 | 0.0 | false | false |
| 08 January 2024 | Repayment | 753.07 | 749.4 | 3.67 | 0.0 | 0.0 | 250.6 | false | true |
- | 08 January 2024 | Accrual | 0.24 | 0.0 | 0.24 | 0.0 | 0.0 | 0.0 | false | false |
+ | 08 January 2024 | Accrual | 2.46 | 0.0 | 2.46 | 0.0 | 0.0 | 0.0 | false | false |
@TestRailId:C3182
Scenario: Verify accrual activity posting job
@@ -2045,9 +2040,9 @@ Feature: LoanAccrualActivity
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 22 April 2024 | Disbursement | 400.0 | 0.0 | 0.0 | 0.0 | 0.0 | 400.0 | false | false |
+ | 22 April 2024 | Accrual | 30.0 | 0.0 | 0.0 | 0.0 | 30.0 | 0.0 | false | false |
| 22 April 2024 | Repayment | 600.0 | 400.0 | 0.0 | 0.0 | 30.0 | 0.0 | true | true |
| 22 April 2024 | Goodwill Credit | 15.0 | 0.0 | 0.0 | 0.0 | 15.0 | 400.0 | false | true |
- | 09 October 2024 | Accrual | 30.0 | 0.0 | 0.0 | 0.0 | 30.0 | 0.0 | false | false |
| 10 October 2024 | Charge Adjustment | 15.0 | 0.0 | 0.0 | 0.0 | 15.0 | 400.0 | false | true |
@TestRailId:C3274
diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeback.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeback.feature
index ee224ea57..48827bb9b 100644
--- a/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeback.feature
+++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeback.feature
@@ -2959,6 +2959,7 @@ Feature: LoanChargeback
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | 0.0 |
| 19 January 2024 | Merchant Issued Refund | 10.0 | 10.0 | 0.0 | 0.0 | 0.0 | 90.0 | 0.0 |
| 01 February 2024 | Repayment | 105.0 | 90.0 | 0.0 | 0.0 | 5.0 | 0.0 | 10.0 |
+ | 01 February 2024 | Accrual | 5.0 | 0.0 | 0.0 | 0.0 | 5.0 | 0.0 | 0.0 |
| 15 April 2024 | Chargeback | 7.0 | 2.0 | 0.0 | 0.0 | 5.0 | 0.0 | 7.0 |
| 16 April 2024 | Chargeback | 7.0 | 7.0 | 0.0 | 0.0 | 0.0 | 4.0 | 3.0 |
Then Loan Transactions tab has a "CHARGEBACK" transaction with date "16 April 2024" which has the following Journal entries:
@@ -3032,6 +3033,7 @@ Feature: LoanChargeback
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Overpayment |
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | 0.0 |
| 01 February 2024 | Repayment | 105.0 | 100.0 | 0.0 | 0.0 | 5.0 | 0.0 | 0.0 |
+ | 01 February 2024 | Accrual | 5.0 | 0.0 | 0.0 | 0.0 | 5.0 | 0.0 | 0.0 |
| 01 March 2024 | Chargeback | 3.0 | 0.0 | 0.0 | 0.0 | 3.0 | 0.0 | 0.0 |
Then Loan Transactions tab has a "DISBURSEMENT" transaction with date "01 January 2024" which has the following Journal entries:
| Type | Account code | Account name | Debit | Credit |
@@ -3065,6 +3067,7 @@ Feature: LoanChargeback
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Overpayment |
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | 0.0 |
| 01 February 2024 | Repayment | 105.0 | 100.0 | 0.0 | 0.0 | 5.0 | 0.0 | 0.0 |
+ | 01 February 2024 | Accrual | 5.0 | 0.0 | 0.0 | 0.0 | 5.0 | 0.0 | 0.0 |
| 01 March 2024 | Chargeback | 3.0 | 0.0 | 0.0 | 0.0 | 3.0 | 0.0 | 0.0 |
| 05 March 2024 | Chargeback | 10.0 | 8.0 | 0.0 | 0.0 | 2.0 | 8.0 | 0.0 |
Then Loan Transactions tab has a "DISBURSEMENT" transaction with date "01 January 2024" which has the following Journal entries:
@@ -3154,8 +3157,8 @@ Feature: LoanChargeback
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Overpayment |
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | 0.0 |
| 01 February 2024 | Repayment | 105.0 | 100.0 | 0.0 | 0.0 | 5.0 | 0.0 | 0.0 |
+ | 01 February 2024 | Accrual | 5.0 | 0.0 | 0.0 | 0.0 | 5.0 | 0.0 | 0.0 |
| 15 April 2024 | Chargeback | 3.0 | 0.0 | 0.0 | 0.0 | 3.0 | 0.0 | 0.0 |
- | 15 April 2024 | Accrual | 5.0 | 0.0 | 0.0 | 0.0 | 5.0 | 0.0 | 0.0 |
Then Loan Transactions tab has a "DISBURSEMENT" transaction with date "01 January 2024" which has the following Journal entries:
| Type | Account code | Account name | Debit | Credit |
| ASSET | 112601 | Loans Receivable | 100.0 | |
@@ -3165,7 +3168,7 @@ Feature: LoanChargeback
| ASSET | 112601 | Loans Receivable | | 100.0 |
| ASSET | 112603 | Interest/Fee Receivable | | 5.0 |
| LIABILITY | 145023 | Suspense/Clearing account | 105.0 | |
- Then Loan Transactions tab has a "ACCRUAL" transaction with date "15 April 2024" which has the following Journal entries:
+ Then Loan Transactions tab has a "ACCRUAL" transaction with date "01 February 2024" which has the following Journal entries:
| Type | Account code | Account name | Debit | Credit |
| ASSET | 112603 | Interest/Fee Receivable | 5.0 | |
| INCOME | 404007 | Fee Income | | 5.0 |
@@ -3193,8 +3196,8 @@ Feature: LoanChargeback
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Overpayment |
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | 0.0 |
| 01 February 2024 | Repayment | 105.0 | 100.0 | 0.0 | 0.0 | 5.0 | 0.0 | 0.0 |
+ | 01 February 2024 | Accrual | 5.0 | 0.0 | 0.0 | 0.0 | 5.0 | 0.0 | 0.0 |
| 15 April 2024 | Chargeback | 3.0 | 0.0 | 0.0 | 0.0 | 3.0 | 0.0 | 0.0 |
- | 15 April 2024 | Accrual | 5.0 | 0.0 | 0.0 | 0.0 | 5.0 | 0.0 | 0.0 |
| 20 April 2024 | Chargeback | 10.0 | 8.0 | 0.0 | 0.0 | 2.0 | 8.0 | 0.0 |
Then Loan Transactions tab has a "DISBURSEMENT" transaction with date "01 January 2024" which has the following Journal entries:
| Type | Account code | Account name | Debit | Credit |
@@ -3205,7 +3208,7 @@ Feature: LoanChargeback
| ASSET | 112601 | Loans Receivable | | 100.0 |
| ASSET | 112603 | Interest/Fee Receivable | | 5.0 |
| LIABILITY | 145023 | Suspense/Clearing account | 105.0 | |
- Then Loan Transactions tab has a "ACCRUAL" transaction with date "15 April 2024" which has the following Journal entries:
+ Then Loan Transactions tab has a "ACCRUAL" transaction with date "01 February 2024" which has the following Journal entries:
| Type | Account code | Account name | Debit | Credit |
| ASSET | 112603 | Interest/Fee Receivable | 5.0 | |
| INCOME | 404007 | Fee Income | | 5.0 |
@@ -3354,6 +3357,7 @@ Feature: LoanChargeback
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | 0.0 |
| 19 January 2024 | Merchant Issued Refund | 10.0 | 10.0 | 0.0 | 0.0 | 0.0 | 90.0 | 0.0 |
| 01 February 2024 | Repayment | 105.0 | 90.0 | 0.0 | 0.0 | 5.0 | 0.0 | 10.0 |
+ | 01 February 2024 | Accrual | 5.0 | 0.0 | 0.0 | 0.0 | 5.0 | 0.0 | 0.0 |
| 15 April 2024 | Chargeback | 3.0 | 0.0 | 0.0 | 0.0 | 3.0 | 0.0 | 3.0 |
| 20 April 2024 | Chargeback | 10.0 | 8.0 | 0.0 | 0.0 | 2.0 | 3.0 | 7.0 |
Then Loan Transactions tab has a "DISBURSEMENT" transaction with date "01 January 2024" which has the following Journal entries:
diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanRepayment.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanRepayment.feature
index 4e206c9a8..aebca854c 100644
--- a/fineract-e2e-tests-runner/src/test/resources/features/LoanRepayment.feature
+++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanRepayment.feature
@@ -2473,8 +2473,8 @@ Feature: LoanRepayment
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 January 2023 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
- | 10 January 2023 | Repayment | 1020.0 | 1000.0 | 0.0 | 20.0 | 0.0 | 0.0 |
| 10 January 2023 | Accrual | 20.0 | 0.0 | 0.0 | 20.0 | 0.0 | 0.0 |
+ | 10 January 2023 | Repayment | 1020.0 | 1000.0 | 0.0 | 20.0 | 0.0 | 0.0 |
Then Loan Charges tab has the following data:
| Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding |
| NSF fee | true | Specified due date | 28 January 2023 | Flat | 20.0 | 0.0 | 0.0 | 20.0 |
@@ -2486,8 +2486,8 @@ Feature: LoanRepayment
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 January 2023 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
- | 10 January 2023 | Repayment | 1020.0 | 1000.0 | 0.0 | 20.0 | 0.0 | 0.0 |
| 10 January 2023 | Accrual | 20.0 | 0.0 | 0.0 | 20.0 | 0.0 | 0.0 |
+ | 10 January 2023 | Repayment | 1020.0 | 1000.0 | 0.0 | 20.0 | 0.0 | 0.0 |
| 28 January 2023 | Repayment | 520.0 | 500.0 | 0.0 | 0.0 | 20.0 | 500.0 |
Then Loan Charges tab has the following data:
| Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding |
@@ -2507,8 +2507,8 @@ Feature: LoanRepayment
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 January 2023 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
- | 10 January 2023 | Repayment | 1020.0 | 1000.0 | 0.0 | 20.0 | 0.0 | 0.0 |
| 10 January 2023 | Accrual | 20.0 | 0.0 | 0.0 | 20.0 | 0.0 | 0.0 |
+ | 10 January 2023 | Repayment | 1020.0 | 1000.0 | 0.0 | 20.0 | 0.0 | 0.0 |
| 28 January 2023 | Repayment | 520.0 | 500.0 | 0.0 | 0.0 | 20.0 | 500.0 |
| 30 January 2023 | Repayment | 520.0 | 500.0 | 0.0 | 20.0 | 0.0 | 0.0 |
| 30 January 2023 | Accrual | 20.0 | 0.0 | 0.0 | 0.0 | 20.0 | 0.0 |
@@ -3656,9 +3656,9 @@ Feature: LoanRepayment
| 13 August 2024 | Repayment | 35.44 | 35.44 | 0.0 | 0.0 | 0.0 | 0.0 | true |
| 22 August 2024 | Repayment | 35.44 | 35.44 | 0.0 | 0.0 | 0.0 | 0.0 | true |
| 22 August 2024 | Repayment | 38.24 | 35.44 | 0.0 | 0.0 | 2.8 | 0.0 | false |
- | 22 August 2024 | Accrual | 5.0 | 0.0 | 0.0 | 0.0 | 5.0 | 0.0 | false |
| 23 August 2024 | Repayment | 10.0 | 0.0 | 0.0 | 0.0 | 5.0 | 0.0 | false |
| 24 August 2024 | Accrual | 2.8 | 0.0 | 0.0 | 0.0 | 2.8 | 0.0 | false |
+ | 24 August 2024 | Accrual | 5.0 | 0.0 | 0.0 | 0.0 | 5.0 | 0.0 | false |
Then Loan Repayment schedule has 2 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 23 July 2024 | | 111.92 | | | 0.0 | | 0.0 | 0.0 | | | |
@@ -3676,9 +3676,9 @@ Feature: LoanRepayment
| 13 August 2024 | Repayment | 35.44 | 35.44 | 0.0 | 0.0 | 0.0 | 0.0 | true |
| 22 August 2024 | Repayment | 35.44 | 35.44 | 0.0 | 0.0 | 0.0 | 0.0 | true |
| 22 August 2024 | Repayment | 38.24 | 35.44 | 0.0 | 0.0 | 2.8 | 0.0 | false |
- | 22 August 2024 | Accrual | 5.0 | 0.0 | 0.0 | 0.0 | 5.0 | 0.0 | false |
| 23 August 2024 | Repayment | 10.0 | 0.0 | 0.0 | 0.0 | 10.0 | 0.0 | false |
| 24 August 2024 | Accrual | 2.8 | 0.0 | 0.0 | 0.0 | 2.8 | 0.0 | false |
+ | 24 August 2024 | Accrual | 5.0 | 0.0 | 0.0 | 0.0 | 5.0 | 0.0 | false |
| 25 August 2024 | Accrual | 5.0 | 0.0 | 0.0 | 0.0 | 5.0 | 0.0 | false |
Then Loan Repayment schedule has 2 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccrualChargeData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccrualChargeData.java
index d3109bd7e..447131c05 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccrualChargeData.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccrualChargeData.java
@@ -34,4 +34,5 @@ public class AccrualChargeData {
private Money chargeAmount;
private Money chargeAccruable;
private Money chargeAccrued;
+ private Money transactionAccrued;
}
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccrualPeriodData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccrualPeriodData.java
index 7c39384eb..30252632a 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccrualPeriodData.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccrualPeriodData.java
@@ -68,10 +68,20 @@ public class AccrualPeriodData {
return charges.stream().filter(charge -> !charge.isPenalty()).map(AccrualChargeData::getChargeAccrued).reduce(null, MathUtil::plus);
}
+ public Money getFeeTransactionAccrued() {
+ return charges.stream().filter(charge -> !charge.isPenalty()).map(AccrualChargeData::getTransactionAccrued).reduce(null,
+ MathUtil::plus);
+ }
+
public Money getPenaltyAccrued() {
return charges.stream().filter(AccrualChargeData::isPenalty).map(AccrualChargeData::getChargeAccrued).reduce(null, MathUtil::plus);
}
+ public Money getPenaltyTransactionAccrued() {
+ return charges.stream().filter(AccrualChargeData::isPenalty).map(AccrualChargeData::getTransactionAccrued).reduce(null,
+ MathUtil::plus);
+ }
+
public Money getChargeAccruable() {
return charges.stream().map(AccrualChargeData::getChargeAccruable).reduce(null, MathUtil::plus);
}
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanScheduleAccrualData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanScheduleAccrualData.java
deleted file mode 100644
index 21abaa153..000000000
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanScheduleAccrualData.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/**
- * 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.portfolio.loanaccount.data;
-
-import java.math.BigDecimal;
-import java.time.LocalDate;
-import java.util.Map;
-import org.apache.fineract.organisation.monetary.data.CurrencyData;
-import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
-
-public class LoanScheduleAccrualData {
-
- private final Long loanId;
- private final Long officeId;
- private final LocalDate accruedTill;
- private final PeriodFrequencyType repaymentFrequency;
- private final Integer repayEvery;
- private final Integer installmentNumber;
- private final LocalDate dueDate;
- private final LocalDate fromDate;
- private final Long repaymentScheduleId;
- private final Long loanProductId;
- private final BigDecimal interestIncome;
- private final BigDecimal feeIncome;
- private final BigDecimal penaltyIncome;
- private final BigDecimal waivedInterestIncome;
- private final BigDecimal accruedInterestIncome;
- private final BigDecimal accruedFeeIncome;
- private final BigDecimal accruedPenaltyIncome;
- private final CurrencyData currencyData;
- private final LocalDate interestCalculatedFrom;
-
- private Map<LoanChargeData, BigDecimal> applicableCharges;
- private BigDecimal dueDateFeeIncome;
- private BigDecimal dueDatePenaltyIncome;
- private BigDecimal accruableIncome;
-
- private BigDecimal creditedFee;
- private BigDecimal creditedPenalty;
-
- public LoanScheduleAccrualData(final Long loanId, final Long officeId, final Integer installmentNumber, final LocalDate accruedTill,
- final PeriodFrequencyType repaymentFrequency, final Integer repayEvery, final LocalDate dueDate, final LocalDate fromDate,
- final Long repaymentScheduleId, final Long loanProductId, final BigDecimal interestIncome, final BigDecimal feeIncome,
- final BigDecimal penaltyIncome, final BigDecimal accruedInterestIncome, final BigDecimal accruedFeeIncome,
- final BigDecimal accruedPenaltyIncome, final CurrencyData currencyData, final LocalDate interestCalculatedFrom,
- final BigDecimal waivedInterestIncome, BigDecimal creditedFee, BigDecimal creditedPenalty) {
- this.loanId = loanId;
- this.installmentNumber = installmentNumber;
- this.officeId = officeId;
- this.accruedTill = accruedTill;
- this.dueDate = dueDate;
- this.fromDate = fromDate;
- this.repaymentScheduleId = repaymentScheduleId;
- this.loanProductId = loanProductId;
- this.interestIncome = interestIncome;
- this.feeIncome = feeIncome;
- this.penaltyIncome = penaltyIncome;
- this.accruedFeeIncome = accruedFeeIncome;
- this.accruedInterestIncome = accruedInterestIncome;
- this.accruedPenaltyIncome = accruedPenaltyIncome;
- this.currencyData = currencyData;
- this.repaymentFrequency = repaymentFrequency;
- this.repayEvery = repayEvery;
- this.interestCalculatedFrom = interestCalculatedFrom;
- this.waivedInterestIncome = waivedInterestIncome;
- this.creditedFee = creditedFee;
- this.creditedPenalty = creditedPenalty;
- }
-
- public Long getLoanId() {
- return this.loanId;
- }
-
- public Long getOfficeId() {
- return this.officeId;
- }
-
- public LocalDate getDueDate() {
- return this.dueDate;
- }
-
- public LocalDate getDueDateAsLocaldate() {
- return this.dueDate;
- }
-
- public Long getRepaymentScheduleId() {
- return this.repaymentScheduleId;
- }
-
- public Long getLoanProductId() {
- return this.loanProductId;
- }
-
- public BigDecimal getInterestIncome() {
- return this.interestIncome;
- }
-
- public BigDecimal getFeeIncome() {
- return this.feeIncome;
- }
-
- public BigDecimal getPenaltyIncome() {
- return this.penaltyIncome;
- }
-
- public BigDecimal getAccruedInterestIncome() {
- return this.accruedInterestIncome;
- }
-
- public BigDecimal getAccruedFeeIncome() {
- return this.accruedFeeIncome;
- }
-
- public BigDecimal getAccruedPenaltyIncome() {
- return this.accruedPenaltyIncome;
- }
-
- public CurrencyData getCurrencyData() {
- return this.currencyData;
- }
-
- public LocalDate getAccruedTill() {
- return this.accruedTill;
- }
-
- public LocalDate getFromDateAsLocaldate() {
- return this.fromDate;
- }
-
- public PeriodFrequencyType getRepaymentFrequency() {
- return this.repaymentFrequency;
- }
-
- public Integer getRepayEvery() {
- return this.repayEvery;
- }
-
- public LocalDate getInterestCalculatedFrom() {
- return this.interestCalculatedFrom;
- }
-
- public Integer getInstallmentNumber() {
- return this.installmentNumber;
- }
-
- public Map<LoanChargeData, BigDecimal> getApplicableCharges() {
- return this.applicableCharges;
- }
-
- public BigDecimal getDueDateFeeIncome() {
- return this.dueDateFeeIncome;
- }
-
- public BigDecimal getDueDatePenaltyIncome() {
- return this.dueDatePenaltyIncome;
- }
-
- public void updateChargeDetails(final Map<LoanChargeData, BigDecimal> applicableCharges, final BigDecimal dueDateFeeIncome,
- final BigDecimal dueDatePenaltyIncome) {
- this.applicableCharges = applicableCharges;
- this.dueDateFeeIncome = dueDateFeeIncome;
- this.dueDatePenaltyIncome = dueDatePenaltyIncome;
- }
-
- public BigDecimal getWaivedInterestIncome() {
- return this.waivedInterestIncome;
- }
-
- public BigDecimal getAccruableIncome() {
- return this.accruableIncome;
- }
-
- public void updateAccruableIncome(BigDecimal accruableIncome) {
- this.accruableIncome = accruableIncome;
- }
-
- public BigDecimal getCreditedFee() {
- return this.creditedFee;
- }
-
- public BigDecimal getCreditedPenalty() {
- return this.creditedPenalty;
- }
-
-}
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
index 280693da5..27bb50d75 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
@@ -18,6 +18,8 @@
*/
package org.apache.fineract.portfolio.loanaccount.domain;
+import static org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType.PROGRESSIVE;
+
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
@@ -740,6 +742,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
* is lesser than todays date. If not, the transaction date is set to todays date
*/
public LoanTransaction handleChargeAppliedTransaction(final LoanCharge loanCharge, final LocalDate suppliedTransactionDate) {
+ if (isProgressiveSchedule()) {
+ return null;
+ }
final Money chargeAmount = loanCharge.getAmount(getCurrency());
Money feeCharges = chargeAmount;
Money penaltyCharges = Money.zero(getCurrency());
@@ -1053,7 +1058,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
LoanCharge charge = loanCharge;
// add new charges
if (loanCharge.getId() == null) {
- LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = null;
+ LoanTrancheDisbursementCharge loanTrancheDisbursementCharge;
loanCharge.update(this);
if (this.loanProduct.isMultiDisburseLoan() && loanCharge.isTrancheDisbursementCharge()) {
loanCharge.getTrancheDisbursementCharge().getloanDisbursementDetails().updateLoan(this);
@@ -3563,4 +3568,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
public LoanRepaymentScheduleTransactionProcessor getTransactionProcessor() {
return transactionProcessorFactory.determineProcessor(transactionProcessingStrategyCode);
}
+
+ public boolean isProgressiveSchedule() {
+ return getLoanProductRelatedDetail().getLoanScheduleType() == PROGRESSIVE;
+ }
}
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualActivityProcessingService.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualActivityProcessingService.java
index 31520704b..6f7f67750 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualActivityProcessingService.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualActivityProcessingService.java
@@ -18,6 +18,7 @@
*/
package org.apache.fineract.portfolio.loanaccount.service;
+import jakarta.validation.constraints.NotNull;
import java.time.LocalDate;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.springframework.transaction.annotation.Transactional;
@@ -25,13 +26,13 @@ import org.springframework.transaction.annotation.Transactional;
public interface LoanAccrualActivityProcessingService {
@Transactional
- void makeAccrualActivityTransaction(Long loanId, LocalDate currentDate);
+ void makeAccrualActivityTransaction(@NotNull Long loanId, @NotNull LocalDate currentDate);
- Loan makeAccrualActivityTransaction(Loan loan, LocalDate currentDate);
+ void makeAccrualActivityTransaction(@NotNull Loan loan, @NotNull LocalDate currentDate);
@Transactional
- void processAccrualActivityForLoanClosure(Loan loan);
+ void processAccrualActivityForLoanClosure(@NotNull Loan loan);
@Transactional
- void processAccrualActivityForLoanReopen(Loan loan);
+ void processAccrualActivityForLoanReopen(@NotNull Loan loan);
}
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualTransactionBusinessEventServiceImpl.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualTransactionBusinessEventServiceImpl.java
index 0a0c84997..bf96b21a6 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualTransactionBusinessEventServiceImpl.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualTransactionBusinessEventServiceImpl.java
@@ -20,7 +20,9 @@ package org.apache.fineract.portfolio.loanaccount.service;
import java.util.List;
import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanAccrualAdjustmentTransactionBusinessEvent;
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanAccrualTransactionCreatedBusinessEvent;
+import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionBusinessEvent;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
@@ -33,8 +35,12 @@ public class LoanAccrualTransactionBusinessEventServiceImpl implements LoanAccru
@Override
public void raiseBusinessEventForAccrualTransactions(Loan loan, List<Long> existingTransactionIds) {
for (final LoanTransaction transaction : loan.getLoanTransactions()) {
- if (transaction.isNotReversed() && transaction.isAccrual() && !existingTransactionIds.contains(transaction.getId())) {
- businessEventNotifierService.notifyPostBusinessEvent(new LoanAccrualTransactionCreatedBusinessEvent(transaction));
+ if (transaction.isNotReversed() && (transaction.isAccrual() || transaction.isAccrualAdjustment())
+ && !existingTransactionIds.contains(transaction.getId())) {
+ LoanTransactionBusinessEvent businessEvent = transaction.isAccrual()
+ ? new LoanAccrualTransactionCreatedBusinessEvent(transaction)
+ : new LoanAccrualAdjustmentTransactionBusinessEvent(transaction);
+ businessEventNotifierService.notifyPostBusinessEvent(businessEvent);
}
}
}
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingService.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingService.java
index 91addc4f6..297774a04 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingService.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingService.java
@@ -35,16 +35,14 @@ public interface LoanAccrualsProcessingService {
void reprocessExistingAccruals(@NotNull Loan loan);
- void processAccrualsForInterestRecalculation(@NotNull Loan loan, boolean isInterestRecalculationEnabled);
+ void processAccrualsOnInterestRecalculation(@NotNull Loan loan, boolean isInterestRecalculationEnabled, boolean addJournal);
- void processIncomePostingAndAccruals(@NotNull Loan loan);
+ void addIncomePostingAndAccruals(Long loanId) throws Exception;
- void addIncomeAndAccrualTransactions(Long loanId) throws Exception;
+ void processIncomePostingAndAccruals(@NotNull Loan loan);
void processAccrualsOnLoanClosure(@NotNull Loan loan);
- void processAccrualsOnLoanReopen(@NotNull Loan loan);
-
void processAccrualsOnLoanForeClosure(@NotNull Loan loan, @NotNull LocalDate foreClosureDate,
@NotNull List<LoanTransaction> newAccrualTransactions);
}
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformService.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformService.java
index 97bd23535..f64f2d71a 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformService.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformService.java
@@ -30,7 +30,6 @@ import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand;
import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkRepaymentCommand;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
-import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
import org.springframework.transaction.annotation.Transactional;
@@ -53,16 +52,6 @@ public interface LoanWritePlatformService {
CommandProcessingResult makeLoanRepaymentWithChargeRefundChargeType(LoanTransactionType repaymentTransactionType, Long loanId,
JsonCommand command, boolean isRecoveryRepayment, String chargeRefundChargeType);
- @Transactional
- Loan reverseReplayAccrualActivityTransaction(Loan loan, LoanTransaction loanTransaction, LoanRepaymentScheduleInstallment installment,
- LocalDate transactionDate);
-
- @Transactional
- Loan makeAccrualActivityTransaction(Loan loan, LoanRepaymentScheduleInstallment installment, LocalDate transactionDate);
-
- @Transactional
- Loan makeAccrualActivityTransaction(Loan loan, LoanTransaction accrualActivityTransaction);
-
@Transactional
CommandProcessingResult makeInterestPaymentWaiver(JsonCommand command);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
index 6452ced33..a1a25ee0b 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
@@ -293,10 +293,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
this.noteRepository.save(note);
}
- postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, isLoanToLoanTransfer);
- loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
- loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
- loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+ loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+ loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
setLoanDelinquencyTag(loan, transactionDate);
@@ -306,6 +304,10 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
businessEventNotifierService.notifyPostBusinessEvent(transactionRepaymentEvent);
}
+
+ postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, isLoanToLoanTransfer);
+ loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
// disable all active standing orders linked to this loan if status
// changes to closed
disableStandingInstructionsLinkedToClosedLoan(loan);
@@ -492,13 +494,13 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
this.noteRepository.save(note);
}
- postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
- loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
-
- loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
- loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+ loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+ loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
businessEventNotifierService.notifyPostBusinessEvent(new LoanChargePaymentPostBusinessEvent(newPaymentTransaction));
+
+ postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
+ loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
return newPaymentTransaction;
}
@@ -726,13 +728,15 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
this.noteRepository.save(note);
}
- postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, false);
- loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
- loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+ loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+ loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
businessEventNotifierService
.notifyPostBusinessEvent(new LoanCreditBalanceRefundPostBusinessEvent(newCreditBalanceRefundTransaction));
+ postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, false);
+ loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
return newCreditBalanceRefundTransaction;
}
@@ -778,13 +782,14 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
this.noteRepository.save(note);
}
- postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, false);
- loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
- loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
- loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+ loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+ loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
businessEventNotifierService.notifyPostBusinessEvent(new LoanRefundPostBusinessEvent(newRefundTransaction));
+ postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, false);
+ loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
builderResult.withEntityId(newRefundTransaction.getId()).withOfficeId(loan.getOfficeId()).withClientId(loan.getClientId())
.withGroupId(loan.getGroupId());
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/addperiodicaccrualentriesforloanswithincomepostedastransactions/AddPeriodicAccrualEntriesForLoansTasklet.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/addperiodicaccrualentriesforloanswithincomepostedastransactions/AddPeriodicAccrualEntriesForLoansTasklet.java
index d7f18f226..68d3e5d79 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/addperiodicaccrualentriesforloanswithincomepostedastransactions/AddPeriodicAccrualEntriesForLoansTasklet.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/addperiodicaccrualentriesforloanswithincomepostedastransactions/AddPeriodicAccrualEntriesForLoansTasklet.java
@@ -46,7 +46,7 @@ public class AddPeriodicAccrualEntriesForLoansTasklet implements Tasklet {
List<Throwable> errors = new ArrayList<>();
for (Long loanId : loanIds) {
try {
- loanAccrualsProcessingService.addIncomeAndAccrualTransactions(loanId);
+ loanAccrualsProcessingService.addIncomePostingAndAccruals(loanId);
} catch (Exception e) {
log.error("Failed to add income and accrual transaction for loan {}", loanId, e);
errors.add(e);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java
index 0d14d5841..e1df63373 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java
@@ -466,13 +466,13 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
- // update the loan object
- postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
- loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
- loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan, true);
+ loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, true, false);
businessEventNotifierService.notifyPostBusinessEvent(new LoanRescheduledDueAdjustScheduleBusinessEvent(loan));
+ postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+ loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
return new CommandProcessingResultBuilder().withCommandId(jsonCommand.commandId()).withEntityId(loanRescheduleRequestId)
.withLoanId(loanRescheduleRequest.getLoan().getId()).with(changes).withClientId(loan.getClientId())
.withOfficeId(loan.getOfficeId()).withGroupId(loan.getGroupId()).build();
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualActivityProcessingServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualActivityProcessingServiceImpl.java
index 36d067243..2f2373966 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualActivityProcessingServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualActivityProcessingServiceImpl.java
@@ -18,24 +18,29 @@
*/
package org.apache.fineract.portfolio.loanaccount.service;
+import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDate;
+import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
-import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-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.LoanAdjustTransactionBusinessEvent;
+import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionAccrualActivityPostBusinessEvent;
+import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionAccrualActivityPreBusinessEvent;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
-import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@@ -46,131 +51,179 @@ import org.springframework.transaction.annotation.Transactional;
public class LoanAccrualActivityProcessingServiceImpl implements LoanAccrualActivityProcessingService {
private final LoanRepositoryWrapper loanRepositoryWrapper;
- private final LoanWritePlatformService loanWritePlatformService;
private final ExternalIdFactory externalIdFactory;
private final BusinessEventNotifierService businessEventNotifierService;
- private final LoanChargeValidator loanChargeValidator;
+ private final LoanTransactionAssembler loanTransactionAssembler;
+ private final LoanAccountDomainService loanAccountDomainService;
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
- public void makeAccrualActivityTransaction(Long loanId, final LocalDate currentDate) {
+ public void makeAccrualActivityTransaction(@NotNull Long loanId, @NotNull LocalDate currentDate) {
Loan loan = loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
makeAccrualActivityTransaction(loan, currentDate);
}
@Override
- public Loan makeAccrualActivityTransaction(Loan loan, final LocalDate currentDate) {
- if (loan.getLoanProductRelatedDetail().isEnableAccrualActivityPosting()) {
- // check if loan has installment due on business day
- Optional<LoanRepaymentScheduleInstallment> first = loan.getRepaymentScheduleInstallments().stream()
- .filter(loanRepaymentScheduleInstallment -> loanRepaymentScheduleInstallment.getDueDate().isEqual(currentDate))
- .findFirst();
- if (first.isPresent()) {
- final LoanRepaymentScheduleInstallment installment = first.get();
- // check if there is no not-replayed-accrual-activity related to business date
- List<LoanTransaction> loanTransactions = loan.getLoanTransactions(loanTransaction -> loanTransaction.isNotReversed()
- && loanTransaction.isAccrualActivity() && loanTransaction.getTransactionDate().isEqual(currentDate));
- if (loanTransactions.isEmpty()) {
- loan = loanWritePlatformService.makeAccrualActivityTransaction(loan, installment, currentDate);
- }
+ public void makeAccrualActivityTransaction(@NotNull Loan loan, @NotNull LocalDate currentDate) {
+ if (!loan.getLoanProductRelatedDetail().isEnableAccrualActivityPosting()) {
+ return;
+ }
+ // check if loan has installment in the past or due on current date
+ List<LoanRepaymentScheduleInstallment> installments = loan
+ .getRepaymentScheduleInstallments(i -> !i.isDownPayment() && !DateUtils.isBefore(currentDate, i.getDueDate()));
+ for (LoanRepaymentScheduleInstallment installment : installments) {
+ LocalDate dueDate = installment.getDueDate();
+ // check if there is any not-replayed-accrual-activity related to business date
+ ArrayList<LoanTransaction> existingActivities = new ArrayList<>(
+ loan.getLoanTransactions(t -> t.isNotReversed() && t.isAccrualActivity() && t.getTransactionDate().isEqual(dueDate)));
+ boolean hasExisting = !existingActivities.isEmpty();
+ LoanTransaction existingActivity = hasExisting ? existingActivities.get(0) : null;
+ makeOrReplayActivity(loan, installment, existingActivity);
+ if (hasExisting) {
+ existingActivities.remove(existingActivity);
+ existingActivities.forEach(this::reverseAccrualActivityTransaction);
}
}
- return loan;
}
@Override
@Transactional
- public void processAccrualActivityForLoanClosure(Loan loan) {
- LocalDate date = loan.isOverPaid() ? loan.getOverpaidOnDate() : loan.getClosedOnDate();
- List<LoanTransaction> accrualActivityTransaction = loan.getLoanTransactions().stream().filter(LoanTransaction::isNotReversed)
- .filter(LoanTransaction::isAccrualActivity).filter(loanTransaction -> loanTransaction.getDateOf().isAfter(date)).toList();
- if (!accrualActivityTransaction.isEmpty()) {
- accrualActivityTransaction.forEach(this::reverseAccrualActivityTransaction);
- }
- LoanTransaction loanTransaction = assembleClosingAccrualActivityTransaction(loan, date);
- if (loanTransaction.getAmount().compareTo(BigDecimal.ZERO) != 0) {
- loanWritePlatformService.makeAccrualActivityTransaction(loan, loanTransaction);
+ public void processAccrualActivityForLoanClosure(@NotNull Loan loan) {
+ if (!loan.getLoanProductRelatedDetail().isEnableAccrualActivityPosting()) {
+ return;
}
- }
-
- private void reverseAccrualActivityTransaction(LoanTransaction loanTransaction) {
- loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(loanTransaction.getLoan(), loanTransaction, "reversed");
- loanTransaction.reverse();
- LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(loanTransaction);
- businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data));
- }
+ LocalDate date = loan.isOverPaid() ? loan.getOverpaidOnDate() : loan.getClosedOnDate();
+ // reverse after closure activities
+ loan.getLoanTransactions(t -> t.isAccrualActivity() && !t.isReversed() && t.getDateOf().isAfter(date))
+ .forEach(this::reverseAccrualActivityTransaction);
- private LoanTransaction assembleClosingAccrualActivityTransaction(Loan loan, LocalDate date) {
- // collect fees
+ // calculate activity amounts
BigDecimal feeChargesPortion = BigDecimal.ZERO;
- // collect penalties
BigDecimal penaltyChargesPortion = BigDecimal.ZERO;
- // collect interests
BigDecimal interestPortion = BigDecimal.ZERO;
- var currency = loan.getCurrency();
- // sum up all accruals
+ // collect installment amounts
for (LoanRepaymentScheduleInstallment installment : loan.getRepaymentScheduleInstallments()) {
- feeChargesPortion = installment.getFeeAccrued(currency).getAmount().add(feeChargesPortion);
- penaltyChargesPortion = installment.getPenaltyAccrued(currency).getAmount().add(penaltyChargesPortion);
- interestPortion = installment.getInterestAccrued(currency).getAmount().add(interestPortion);
+ feeChargesPortion = MathUtil.add(feeChargesPortion, installment.getFeeChargesCharged());
+ penaltyChargesPortion = MathUtil.add(penaltyChargesPortion, installment.getPenaltyCharges());
+ interestPortion = MathUtil.add(interestPortion, installment.getInterestCharged());
}
List<LoanTransaction> accrualActivities = loan.getLoanTransactions().stream().filter(LoanTransaction::isAccrualActivity)
.filter(LoanTransaction::isNotReversed).toList();
- // subtract already Posted accruals
+ // subtract already posted activities
for (LoanTransaction accrualActivity : accrualActivities) {
- if (accrualActivity.getFeeChargesPortion() != null) {
- feeChargesPortion = feeChargesPortion.subtract(accrualActivity.getFeeChargesPortion());
- }
- if (accrualActivity.getPenaltyChargesPortion() != null) {
- penaltyChargesPortion = penaltyChargesPortion.subtract(accrualActivity.getPenaltyChargesPortion());
- }
- if (accrualActivity.getInterestPortion() != null) {
- interestPortion = interestPortion.subtract(accrualActivity.getInterestPortion());
- }
+ feeChargesPortion = MathUtil.subtract(feeChargesPortion, accrualActivity.getFeeChargesPortion());
+ penaltyChargesPortion = MathUtil.subtract(penaltyChargesPortion, accrualActivity.getPenaltyChargesPortion());
+ interestPortion = MathUtil.subtract(interestPortion, accrualActivity.getInterestPortion());
}
-
- BigDecimal transactionAmount = feeChargesPortion.add(penaltyChargesPortion).add(interestPortion);
- ExternalId externalId = externalIdFactory.create();
-
- return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.ACCRUAL_ACTIVITY.getValue(), date, transactionAmount, null,
- interestPortion, feeChargesPortion, penaltyChargesPortion, null, false, null, externalId);
+ BigDecimal transactionAmount = MathUtil.add(feeChargesPortion, penaltyChargesPortion, interestPortion);
+ if (!MathUtil.isGreaterThanZero(transactionAmount)) {
+ return;
+ }
+ if (MathUtil.isLessThanZero(feeChargesPortion) || MathUtil.isLessThanZero(penaltyChargesPortion)
+ || MathUtil.isLessThanZero(interestPortion)) {
+ // TODO reverse latest accrual activity if any amount is negative
+ return;
+ }
+ LoanTransaction newActivity = new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.ACCRUAL_ACTIVITY.getValue(), date,
+ transactionAmount, null, interestPortion, feeChargesPortion, penaltyChargesPortion, null, false, null,
+ externalIdFactory.create());
+ makeAccrualActivityTransaction(loan, newActivity);
}
@Override
@Transactional
- public void processAccrualActivityForLoanReopen(Loan loan) {
- LoanTransaction lastAccrualActivityMarkedToReverse = null;
- List<LoanTransaction> accrualActivityTransaction = loan.getLoanTransactions().stream()
- .filter(loanTransaction -> loanTransaction.isNotReversed() && loanTransaction.isAccrualActivity())
- .sorted(Comparator.comparing(LoanTransaction::getDateOf)).toList();
+ public void processAccrualActivityForLoanReopen(@NotNull Loan loan) {
+ if (!loan.getLoanProductRelatedDetail().isEnableAccrualActivityPosting()) {
+ return;
+ }
// grab the latest AccrualActivityTransaction
// it does not matter if it is on an installment due date or not because it was posted due to loan close
- if (!accrualActivityTransaction.isEmpty()) {
- lastAccrualActivityMarkedToReverse = accrualActivityTransaction.get(accrualActivityTransaction.size() - 1);
- }
+ LoanTransaction lastAccrualActivityMarkedToReverse = loan.getLoanTransactions().stream()
+ .filter(loanTransaction -> loanTransaction.isNotReversed() && loanTransaction.isAccrualActivity())
+ .sorted(Comparator.comparing(LoanTransaction::getDateOf)).reduce((first, second) -> second).orElse(null);
final LocalDate lastAccrualActivityTransactionDate = lastAccrualActivityMarkedToReverse == null ? null
: lastAccrualActivityMarkedToReverse.getDateOf();
LocalDate today = DateUtils.getBusinessLocalDate();
final List<LoanRepaymentScheduleInstallment> installmentsBetweenBusinessDateAndLastAccrualActivityTransactionDate = loan
.getRepaymentScheduleInstallments().stream()
- .filter(installment -> installment.getDueDate().isBefore(today) && (lastAccrualActivityTransactionDate == null
- || installment.getDueDate().isAfter(lastAccrualActivityTransactionDate)
- // if close event happened on installment due date
- // we should reverse replay it to calculate installment related accrual parts only
- || installment.getDueDate().isEqual(lastAccrualActivityTransactionDate)))
+ .filter(installment -> installment.getDueDate().isBefore(today)
+ && (DateUtils.isAfter(installment.getDueDate(), lastAccrualActivityTransactionDate)
+ // if close event happened on installment due date
+ // we should reverse replay it to calculate installment related accrual parts only
+ || installment.getDueDate().isEqual(lastAccrualActivityTransactionDate)))
.sorted(Comparator.comparing(LoanRepaymentScheduleInstallment::getDueDate)).toList();
for (LoanRepaymentScheduleInstallment installment : installmentsBetweenBusinessDateAndLastAccrualActivityTransactionDate) {
- if (lastAccrualActivityMarkedToReverse != null) {
- loanWritePlatformService.reverseReplayAccrualActivityTransaction(loan, lastAccrualActivityMarkedToReverse, installment,
- installment.getDueDate());
- lastAccrualActivityMarkedToReverse = null;
- } else {
- loanWritePlatformService.makeAccrualActivityTransaction(loan, installment, installment.getDueDate());
- }
+ makeOrReplayActivity(loan, installment, lastAccrualActivityMarkedToReverse);
+ lastAccrualActivityMarkedToReverse = null;
}
if (lastAccrualActivityMarkedToReverse != null) {
reverseAccrualActivityTransaction(lastAccrualActivityMarkedToReverse);
}
}
+ private void makeOrReplayActivity(@NotNull Loan loan, @NotNull LoanRepaymentScheduleInstallment installment,
+ LoanTransaction existingActivity) {
+ LocalDate dueDate = installment.getDueDate();
+ if (existingActivity == null) {
+ makeAccrualActivityTransaction(loan, installment, dueDate);
+ } else {
+ reverseReplayAccrualActivityTransaction(loan, existingActivity, installment, dueDate);
+ }
+ }
+
+ private LoanTransaction reverseReplayAccrualActivityTransaction(@NotNull Loan loan, @NotNull LoanTransaction loanTransaction,
+ @NotNull LoanRepaymentScheduleInstallment installment, @NotNull LocalDate transactionDate) {
+ if (validateActivityTransaction(installment, loanTransaction)) {
+ return loanTransaction;
+ }
+
+ LoanTransaction newLoanTransaction = loanTransactionAssembler.assembleAccrualActivityTransaction(loan, installment,
+ transactionDate);
+ if (newLoanTransaction != null) {
+ newLoanTransaction.copyLoanTransactionRelations(loanTransaction.getLoanTransactionRelations());
+ newLoanTransaction.getLoanTransactionRelations().add(LoanTransactionRelation.linkToTransaction(newLoanTransaction,
+ loanTransaction, LoanTransactionRelationTypeEnum.REPLAYED));
+ loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(newLoanTransaction);
+ loan.addLoanTransaction(newLoanTransaction);
+
+ LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(loanTransaction);
+ data.setNewTransactionDetail(newLoanTransaction);
+ businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data));
+ }
+ reverseAccrualActivityTransaction(loanTransaction);
+ return newLoanTransaction;
+ }
+
+ private boolean validateActivityTransaction(@NotNull LoanRepaymentScheduleInstallment installment,
+ @NotNull LoanTransaction transaction) {
+ return DateUtils.isEqual(installment.getDueDate(), transaction.getDateOf())
+ && MathUtil.isEqualTo(transaction.getInterestPortion(), installment.getInterestCharged())
+ && MathUtil.isEqualTo(transaction.getFeeChargesPortion(), installment.getFeeChargesCharged())
+ && MathUtil.isEqualTo(transaction.getPenaltyChargesPortion(), installment.getPenaltyCharges());
+ }
+
+ private void reverseAccrualActivityTransaction(LoanTransaction loanTransaction) {
+ loanTransaction.reverse();
+ loanTransaction.updateExternalId(null);
+ LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(loanTransaction);
+ businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data));
+ }
+
+ private LoanTransaction makeAccrualActivityTransaction(@NotNull Loan loan, @NotNull LoanRepaymentScheduleInstallment installment,
+ @NotNull LocalDate transactionDate) {
+ LoanTransaction newAccrualActivityTransaction = loanTransactionAssembler.assembleAccrualActivityTransaction(loan, installment,
+ transactionDate);
+ return newAccrualActivityTransaction == null ? null : makeAccrualActivityTransaction(loan, newAccrualActivityTransaction);
+ }
+
+ private LoanTransaction makeAccrualActivityTransaction(@NotNull Loan loan, @NotNull LoanTransaction newAccrualActivityTransaction) {
+ businessEventNotifierService.notifyPreBusinessEvent(new LoanTransactionAccrualActivityPreBusinessEvent(loan));
+ newAccrualActivityTransaction = loanAccountDomainService
+ .saveLoanTransactionWithDataIntegrityViolationChecks(newAccrualActivityTransaction);
+
+ loan.addLoanTransaction(newAccrualActivityTransaction);
+ businessEventNotifierService
+ .notifyPostBusinessEvent(new LoanTransactionAccrualActivityPostBusinessEvent(newAccrualActivityTransaction));
+ return newAccrualActivityTransaction;
+ }
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanStatusChangePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualEventService.java
similarity index 52%
copy from fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanStatusChangePlatformServiceImpl.java
copy to fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualEventService.java
index 06f402c41..9697a56d0 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanStatusChangePlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualEventService.java
@@ -22,14 +22,15 @@ import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.event.business.BusinessEventListener;
-import org.apache.fineract.infrastructure.event.business.domain.loan.LoanStatusChangedBusinessEvent;
+import org.apache.fineract.infrastructure.event.business.domain.loan.LoanBalanceChangedBusinessEvent;
+import org.apache.fineract.infrastructure.event.business.domain.loan.LoanCloseBusinessEvent;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
@Slf4j
@RequiredArgsConstructor
-public class LoanStatusChangePlatformServiceImpl implements LoanStatusChangePlatformService {
+public class LoanAccrualEventService {
private final BusinessEventNotifierService businessEventNotifierService;
private final LoanAccrualsProcessingService loanAccrualsProcessingService;
@@ -37,44 +38,34 @@ public class LoanStatusChangePlatformServiceImpl implements LoanStatusChangePlat
@PostConstruct
public void addListeners() {
- businessEventNotifierService.addPostBusinessEventListener(LoanStatusChangedBusinessEvent.class, new LoanStatusChangedListener());
- businessEventNotifierService.addPostBusinessEventListener(LoanStatusChangedBusinessEvent.class,
- new LoanAccrualActivityPostingLoanStatusChangedListener());
+ businessEventNotifierService.addPostBusinessEventListener(LoanCloseBusinessEvent.class, new LoanCloseListener());
+ businessEventNotifierService.addPostBusinessEventListener(LoanBalanceChangedBusinessEvent.class, new LoanBalanceChangedListener());
}
- private final class LoanStatusChangedListener implements BusinessEventListener<LoanStatusChangedBusinessEvent> {
+ private final class LoanCloseListener implements BusinessEventListener<LoanCloseBusinessEvent> {
@Override
- public void onBusinessEvent(LoanStatusChangedBusinessEvent event) {
+ public void onBusinessEvent(LoanCloseBusinessEvent event) {
final Loan loan = event.get();
- log.debug("Loan Status change for loan {}", loan.getId());
- LoanStatus oldStatus = event.getOldStatus();
- LoanStatus newStatus = loan.getStatus();
- if (oldStatus.isActive() && (newStatus.isClosedObligationsMet() || newStatus.isOverpaid())) {
- log.debug("Loan Status {} for loan {}", newStatus.getCode(), loan.getId());
- loan.updateLoanSummaryDerivedFields();
+ LoanStatus status = loan.getStatus();
+ if (status.isClosedObligationsMet() || status.isOverpaid()) {
+ log.debug("Loan closure on accrual for loan {}", loan.getId());
loanAccrualsProcessingService.processAccrualsOnLoanClosure(loan);
- } else if ((oldStatus.isClosed() || oldStatus.isOverpaid()) && newStatus.isActive()) {
- loan.handleMaturityDateActivate();
- loanAccrualsProcessingService.processAccrualsOnLoanReopen(loan);
+ loanAccrualActivityProcessingService.processAccrualActivityForLoanClosure(loan);
}
}
}
- private final class LoanAccrualActivityPostingLoanStatusChangedListener
- implements BusinessEventListener<LoanStatusChangedBusinessEvent> {
+ private final class LoanBalanceChangedListener implements BusinessEventListener<LoanBalanceChangedBusinessEvent> {
@Override
- public void onBusinessEvent(LoanStatusChangedBusinessEvent event) {
+ public void onBusinessEvent(LoanBalanceChangedBusinessEvent event) {
final Loan loan = event.get();
- if (loan.getLoanProductRelatedDetail().isEnableAccrualActivityPosting()) {
- LoanStatus oldStatus = event.getOldStatus();
- LoanStatus newStatus = loan.getStatus();
- if (oldStatus.isActive() && (newStatus.isClosedObligationsMet() || newStatus.isOverpaid())) {
- loanAccrualActivityProcessingService.processAccrualActivityForLoanClosure(loan);
- } else if ((oldStatus.isClosed() || oldStatus.isOverpaid()) && newStatus.isActive()) {
- loanAccrualActivityProcessingService.processAccrualActivityForLoanReopen(loan);
- }
+ LoanStatus status = loan.getStatus();
+ if (status.isClosedObligationsMet() || status.isOverpaid()) {
+ log.debug("Loan balance change on accrual for loan {}", loan.getId());
+ loanAccrualsProcessingService.processAccrualsOnLoanClosure(loan);
+ loanAccrualActivityProcessingService.processAccrualActivityForLoanClosure(loan);
}
}
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java
index bb867e299..55d5de5b7 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java
@@ -56,14 +56,12 @@ import org.apache.fineract.infrastructure.event.business.service.BusinessEventNo
import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
-import org.apache.fineract.organisation.office.domain.OfficeRepository;
import org.apache.fineract.portfolio.loanaccount.data.AccrualChargeData;
import org.apache.fineract.portfolio.loanaccount.data.AccrualPeriodData;
import org.apache.fineract.portfolio.loanaccount.data.AccrualPeriodsData;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy;
-import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanInstallmentCharge;
import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalcualtionAdditionalDetails;
import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalculationDetails;
@@ -77,8 +75,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepayme
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
-import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
-import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
import org.apache.fineract.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.springframework.stereotype.Component;
@@ -102,9 +98,6 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
private final LoanTransactionRepository loanTransactionRepository;
private final LoanScheduleGeneratorFactory loanScheduleFactory;
private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory;
- private final OfficeRepository officeRepository;
- private final LoanChargeRepository loanChargeRepository;
- private final LoanChargeValidator loanChargeValidator;
/**
* method adds accrual for batch job "Add Periodic Accrual Transactions" and add accruals api for Loan
@@ -167,9 +160,6 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
*/
@Override
public void reprocessExistingAccruals(@NotNull Loan loan) {
- if (isProgressiveAccrual(loan)) {
- return;
- }
List<LoanTransaction> accrualTransactions = retrieveListOfAccrualTransactions(loan);
if (!accrualTransactions.isEmpty()) {
if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
@@ -185,7 +175,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
*/
@Override
@Transactional
- public void processAccrualsForInterestRecalculation(@NotNull Loan loan, boolean isInterestRecalculationEnabled) {
+ public void processAccrualsOnInterestRecalculation(@NotNull Loan loan, boolean isInterestRecalculationEnabled, boolean addJournal) {
if (isProgressiveAccrual(loan)) {
return;
}
@@ -194,13 +184,31 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
return;
}
try {
- addPeriodicAccruals(accruedTill, loan);
+ addAccruals(loan, accruedTill, true, false, addJournal);
} catch (Exception e) {
String globalisationMessageCode = "error.msg.accrual.exception";
throw new GeneralPlatformDomainRuleException(globalisationMessageCode, e.getMessage(), e);
}
}
+ @Transactional
+ @Override
+ public void addIncomePostingAndAccruals(Long loanId) throws LoanNotFoundException {
+ if (loanId == null) {
+ return;
+ }
+ Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
+ if (isProgressiveAccrual(loan)) {
+ return;
+ }
+ final List<Long> existingTransactionIds = new ArrayList<>(loan.findExistingTransactionIds());
+ final List<Long> existingReversedTransactionIds = new ArrayList<>(loan.findExistingReversedTransactionIds());
+ processIncomePostingAndAccruals(loan);
+ this.loanRepositoryWrapper.saveAndFlush(loan);
+ postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+ loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+ }
+
/**
* method calculates accruals for loan with interest recalculation and compounding to be posted as income
*/
@@ -228,26 +236,8 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
}
List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
LoanRepaymentScheduleInstallment lastInstallment = LoanRepaymentScheduleInstallment.getLastNonDownPaymentInstallment(installments);
- reverseTransactionsAfter(incomeTransactions, lastInstallment.getDueDate());
- reverseTransactionsAfter(accrualTransactions, lastInstallment.getDueDate());
- }
-
- @Transactional
- @Override
- public void addIncomeAndAccrualTransactions(Long loanId) throws LoanNotFoundException {
- if (loanId == null) {
- return;
- }
- Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
- if (isProgressiveAccrual(loan)) {
- return;
- }
- final List<Long> existingTransactionIds = new ArrayList<>(loan.findExistingTransactionIds());
- final List<Long> existingReversedTransactionIds = new ArrayList<>(loan.findExistingReversedTransactionIds());
- processIncomePostingAndAccruals(loan);
- this.loanRepositoryWrapper.saveAndFlush(loan);
- postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
- loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+ reverseTransactionsAfter(incomeTransactions, lastInstallment.getDueDate(), false);
+ reverseTransactionsAfter(accrualTransactions, lastInstallment.getDueDate(), false);
}
/**
@@ -264,32 +254,6 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
processIncomeAndAccrualTransactionOnLoanClosure(loan);
}
- @Override
- public void processAccrualsOnLoanReopen(@NotNull Loan loan) {
- if (isProgressiveAccrual(loan)) {
- List<LoanTransaction> accruals = retrieveListOfAccrualTransactions(loan);
- LocalDate accruedTill = loan.getAccruedTill();
- // if accruedTill is null, the all the accrual transactions will be reversed here
- if (reverseTransactionsAfter(accruals, accruedTill)) {
- return;
- }
- LoanTransaction closureAccrual = accruals.stream()
- .filter(t -> !t.isReversed() && t.isAccrual() && DateUtils.isEqual(t.getTransactionDate(), accruedTill))
- .sorted(LoanTransactionComparator.INSTANCE).reduce((first, second) -> second).orElse(null);
- if (closureAccrual == null) {
- return;
- }
- int firstInstallmentNumber = fetchFirstNormalInstallmentNumber(loan.getRepaymentScheduleInstallments());
- LoanRepaymentScheduleInstallment installment = loan.getRepaymentScheduleInstallment(
- i -> isInPeriod(closureAccrual.getDateOf(), i, i.getInstallmentNumber().equals(firstInstallmentNumber)));
- if (MathUtil.isGreaterThan(closureAccrual.getInterestPortion(), installment.getInterestCharged())
- || MathUtil.isGreaterThan(closureAccrual.getFeeChargesPortion(), installment.getFeeChargesCharged())
- || MathUtil.isGreaterThan(closureAccrual.getPenaltyChargesPortion(), installment.getPenaltyCharges())) {
- reverseAccrual(closureAccrual);
- }
- }
- }
-
/**
* method calculates accruals for loan on loan fore closure
*/
@@ -301,7 +265,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
&& (loan.getAccruedTill() == null || !DateUtils.isEqual(foreClosureDate, loan.getAccruedTill()))) {
final LoanRepaymentScheduleInstallment foreCloseDetail = loan.fetchLoanForeclosureDetail(foreClosureDate);
MonetaryCurrency currency = loan.getCurrency();
- reverseTransactionsAfter(retrieveListOfAccrualTransactions(loan), foreClosureDate);
+ reverseTransactionsAfter(retrieveListOfAccrualTransactions(loan), foreClosureDate, false);
HashMap<String, Object> incomeDetails = new HashMap<>();
@@ -331,20 +295,29 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
return;
}
List<LoanTransaction> existingAccruals = retrieveListOfAccrualTransactions(loan);
- reverseTransactionsAfter(existingAccruals, loan.getLastLoanRepaymentScheduleInstallment().getDueDate());
+ LocalDate lastDueDate = loan.getLastLoanRepaymentScheduleInstallment().getDueDate();
+ reverseTransactionsAfter(existingAccruals, lastDueDate, addJournal);
ensureAccrualTransactionMappings(loan);
+ if (DateUtils.isAfter(tillDate, lastDueDate)) {
+ tillDate = lastDueDate;
+ }
boolean progressiveAccrual = isProgressiveAccrual(loan);
LocalDate accruedTill = loan.getAccruedTill();
- if (progressiveAccrual && accruedTill != null && !DateUtils.isAfter(tillDate, accruedTill)
- && existingAccruals.stream().anyMatch(t -> !t.isReversed() && !DateUtils.isBefore(t.getDateOf(), tillDate))) {
- return;
+ LocalDate businessDate = DateUtils.getBusinessLocalDate();
+ LocalDate accrualDate = isFinal
+ ? (progressiveAccrual ? (DateUtils.isBefore(lastDueDate, businessDate) ? lastDueDate : businessDate)
+ : getFinalAccrualTransactionDate(loan))
+ : tillDate;
+ if (progressiveAccrual && accruedTill != null && !DateUtils.isAfter(tillDate, accruedTill)) {
+ if (isFinal) {
+ reverseTransactionsAfter(existingAccruals, accrualDate, addJournal);
+ } else if (existingAccruals.stream().anyMatch(t -> !t.isReversed() && !DateUtils.isBefore(t.getDateOf(), accrualDate))) {
+ return;
+ }
}
AccrualPeriodsData accrualPeriods = calculateAccrualAmounts(loan, tillDate, periodic);
-
- LocalDate accrualDate = isFinal ? (progressiveAccrual ? DateUtils.getBusinessLocalDate() : getFinalAccrualTransactionDate(loan))
- : tillDate;
boolean mergeTransactions = isFinal || progressiveAccrual;
MonetaryCurrency currency = loan.getLoanProductRelatedDetail().getCurrency();
List<LoanTransaction> accrualTransactions = new ArrayList<>();
@@ -399,17 +372,17 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
return;
}
- if (!isFinal) {
- loan.setAccruedTill(tillDate);
+ if (!isFinal || progressiveAccrual) {
+ loan.setAccruedTill(isFinal ? accrualDate : tillDate);
}
ArrayList<Map<String, Object>> newTransactionMapping = new ArrayList<>();
for (LoanTransaction accrualTransaction : accrualTransactions) {
accrualTransaction = loanTransactionRepository.saveAndFlush(accrualTransaction);
- LoanTransactionBusinessEvent businessEvent = accrualTransaction.isAccrual()
- ? new LoanAccrualTransactionCreatedBusinessEvent(accrualTransaction)
- : new LoanAccrualAdjustmentTransactionBusinessEvent(accrualTransaction);
- businessEventNotifierService.notifyPostBusinessEvent(businessEvent);
if (addJournal) {
+ LoanTransactionBusinessEvent businessEvent = accrualTransaction.isAccrual()
+ ? new LoanAccrualTransactionCreatedBusinessEvent(accrualTransaction)
+ : new LoanAccrualAdjustmentTransactionBusinessEvent(accrualTransaction);
+ businessEventNotifierService.notifyPostBusinessEvent(businessEvent);
newTransactionMapping.add(accrualTransaction.toMapData(currency.getCode()));
}
}
@@ -584,7 +557,9 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
Money unrecognizedWaived = MathUtil.toMoney(calcChargeUnrecognizedWaivedAmount(paidBys, tillDate), currency);
Money transactionWaived = MathUtil.minusToZero(waived, unrecognizedWaived);
- chargeData.setChargeAccrued(MathUtil.minusToZero(MathUtil.toMoney(calcChargeAccruedAmount(paidBys), currency), transactionWaived));
+ Money transactionAccrued = MathUtil.toMoney(calcChargeAccruedAmount(paidBys), currency);
+ chargeData.setTransactionAccrued(transactionAccrued);
+ chargeData.setChargeAccrued(MathUtil.minusToZero(transactionAccrued, transactionWaived));
duePeriod.addCharge(chargeData);
}
@@ -720,17 +695,29 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
return;
}
ensureAccrualTransactionMappings(loan);
- List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
- boolean isBasedOnSubmittedOnDate = !isChargeOnDueDate();
- for (LoanRepaymentScheduleInstallment installment : installments) {
- checkAndUpdateAccrualsForInstallment(loan, accrualTransactions, installments, isBasedOnSubmittedOnDate, installment);
+ LoanRepaymentScheduleInstallment lastInstallment = loan.getLastLoanRepaymentScheduleInstallment();
+ LocalDate lastDueDate = lastInstallment.getDueDate();
+ if (isProgressiveAccrual(loan)) {
+ AccrualPeriodsData accrualPeriods = calculateAccrualAmounts(loan, lastDueDate, true);
+ for (AccrualPeriodData period : accrualPeriods.getPeriods()) {
+ Money interestAccrued = period.getTransactionAccrued();
+ Money feeAccrued = period.getFeeTransactionAccrued();
+ Money penaltyAccrued = period.getPenaltyTransactionAccrued();
+ LoanRepaymentScheduleInstallment installment = loan.fetchRepaymentScheduleInstallment(period.getInstallmentNumber());
+ installment.updateAccrualPortion(interestAccrued, feeAccrued, penaltyAccrued);
+ }
+ } else {
+ List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
+ boolean isBasedOnSubmittedOnDate = !isChargeOnDueDate();
+ for (LoanRepaymentScheduleInstallment installment : installments) {
+ checkAndUpdateAccrualsForInstallment(loan, accrualTransactions, installments, isBasedOnSubmittedOnDate, installment);
+ }
}
// reverse accruals after last installment
- LoanRepaymentScheduleInstallment lastInstallment = loan.getLastLoanRepaymentScheduleInstallment();
- reverseTransactionsAfter(accrualTransactions, lastInstallment.getDueDate());
+ reverseTransactionsAfter(accrualTransactions, lastDueDate, false);
}
- private void reprocessNonPeriodicAccruals(Loan loan, final List<LoanTransaction> accruals) {
+ private void reprocessNonPeriodicAccruals(Loan loan, final List<LoanTransaction> accrualTransactions) {
if (isProgressiveAccrual(loan)) {
return;
}
@@ -738,29 +725,25 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
ExternalId externalId = ExternalId.empty();
boolean isExternalIdAutoGenerationEnabled = configurationDomainService.isExternalIdAutoGenerationEnabled();
- for (LoanTransaction loanTransaction : accruals) {
- if (loanTransaction.getInterestPortion(loan.getCurrency()).isGreaterThanZero()) {
- if (loanTransaction.getInterestPortion(loan.getCurrency()).isNotEqualTo(interestApplied)) {
- loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(loanTransaction.getLoan(), loanTransaction,
- "reversed");
- loanTransaction.reverse();
+ for (LoanTransaction accrualTransaction : accrualTransactions) {
+ if (accrualTransaction.getInterestPortion(loan.getCurrency()).isGreaterThanZero()) {
+ if (accrualTransaction.getInterestPortion(loan.getCurrency()).isNotEqualTo(interestApplied)) {
+ accrualTransaction.reverse();
if (isExternalIdAutoGenerationEnabled) {
externalId = ExternalId.generate();
}
- final LoanTransaction interestAppliedTransaction = LoanTransaction.accrueInterest(loan.getOffice(), loan,
+ final LoanTransaction interestAccrualTransaction = LoanTransaction.accrueInterest(loan.getOffice(), loan,
interestApplied, loan.getDisbursementDate(), externalId);
- loan.addLoanTransaction(interestAppliedTransaction);
+ loan.addLoanTransaction(interestAccrualTransaction);
}
} else {
- Set<LoanChargePaidBy> chargePaidBies = loanTransaction.getLoanChargesPaid();
+ Set<LoanChargePaidBy> chargePaidBies = accrualTransaction.getLoanChargesPaid();
for (final LoanChargePaidBy chargePaidBy : chargePaidBies) {
LoanCharge loanCharge = chargePaidBy.getLoanCharge();
Money chargeAmount = loanCharge.getAmount(loan.getCurrency());
- if (chargeAmount.isNotEqualTo(loanTransaction.getAmount(loan.getCurrency()))) {
- loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(loanTransaction.getLoan(),
- loanTransaction, "reversed");
- loanTransaction.reverse();
- loan.handleChargeAppliedTransaction(loanCharge, loanTransaction.getTransactionDate());
+ if (chargeAmount.isNotEqualTo(accrualTransaction.getAmount(loan.getCurrency()))) {
+ accrualTransaction.reverse();
+ loan.handleChargeAppliedTransaction(loanCharge, accrualTransaction.getTransactionDate());
}
}
}
@@ -786,8 +769,6 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
interest = interest.minus(accrualTransaction.getInterestPortion(currency));
fee = fee.minus(accrualTransaction.getFeeChargesPortion(currency));
penalty = penalty.minus(accrualTransaction.getPenaltyChargesPortion(currency));
- loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(accrualTransaction.getLoan(),
- accrualTransaction, "reversed");
accrualTransaction.reverse();
}
}
@@ -869,8 +850,6 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
compoundingDetail.getAmount(), interest, fee, penalties, externalId);
loan.addLoanTransaction(transaction);
} else if (existingIncomeTransaction.getAmount(loan.getCurrency()).getAmount().compareTo(compoundingDetail.getAmount()) != 0) {
- loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(existingIncomeTransaction.getLoan(),
- existingIncomeTransaction, "reversed");
existingIncomeTransaction.reverse();
LoanTransaction transaction = LoanTransaction.incomePosting(loan, loan.getOffice(), compoundingDetail.getEffectiveDate(),
compoundingDetail.getAmount(), interest, fee, penalties, externalId);
@@ -889,8 +868,6 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
if (existingAccrualTransaction == null
|| !MathUtil.isEqualTo(existingAccrualTransaction.getAmount(), compoundingDetail.getAmount())) {
if (existingAccrualTransaction != null) {
- loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(existingAccrualTransaction.getLoan(),
- existingAccrualTransaction, "reversed");
existingAccrualTransaction.reverse();
}
LoanTransaction accrual = LoanTransaction.accrueTransaction(loan, loan.getOffice(), compoundingDetail.getEffectiveDate(),
@@ -910,8 +887,8 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
&& loan.getStatus().isClosedObligationsMet() && !loan.isNpa() && !loan.isChargedOff()) {
LocalDate closedDate = loan.getClosedOnDate();
- reverseTransactionsOnOrAfter(retrieveListOfIncomePostingTransactions(loan), closedDate);
- reverseTransactionsOnOrAfter(retrieveListOfAccrualTransactions(loan), closedDate);
+ reverseTransactionsOnOrAfter(retrieveListOfIncomePostingTransactions(loan), closedDate, false);
+ reverseTransactionsOnOrAfter(retrieveListOfAccrualTransactions(loan), closedDate, false);
HashMap<String, BigDecimal> cumulativeIncomeFromInstallments = new HashMap<>();
determineCumulativeIncomeFromInstallments(loan, cumulativeIncomeFromInstallments);
@@ -1188,36 +1165,34 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
}
}
- private boolean reverseTransactionsAfter(List<LoanTransaction> transactions, LocalDate effectiveDate) {
+ private boolean reverseTransactionsAfter(List<LoanTransaction> accrualTransactions, LocalDate effectiveDate, boolean addEvent) {
boolean reversed = false;
- for (LoanTransaction loanTransaction : transactions) {
- if (DateUtils.isAfter(loanTransaction.getTransactionDate(), effectiveDate)) {
- loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(loanTransaction.getLoan(), loanTransaction,
- "reversed");
- reverseAccrual(loanTransaction);
+ for (LoanTransaction accrualTransaction : accrualTransactions) {
+ if (!accrualTransaction.isReversed() && DateUtils.isAfter(accrualTransaction.getTransactionDate(), effectiveDate)) {
+ reverseAccrual(accrualTransaction, addEvent);
reversed = true;
}
}
return reversed;
}
- private boolean reverseTransactionsOnOrAfter(List<LoanTransaction> transactions, LocalDate date) {
+ private boolean reverseTransactionsOnOrAfter(List<LoanTransaction> accrualTransactions, LocalDate date, boolean addEvent) {
boolean reversed = false;
- for (LoanTransaction loanTransaction : transactions) {
- if (!DateUtils.isBefore(loanTransaction.getTransactionDate(), date)) {
- loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(loanTransaction.getLoan(), loanTransaction,
- "reversed");
- reverseAccrual(loanTransaction);
+ for (LoanTransaction accrualTransaction : accrualTransactions) {
+ if (!accrualTransaction.isReversed() && !DateUtils.isBefore(accrualTransaction.getTransactionDate(), date)) {
+ reverseAccrual(accrualTransaction, addEvent);
reversed = true;
}
}
return reversed;
}
- private void reverseAccrual(LoanTransaction transaction) {
+ private void reverseAccrual(LoanTransaction transaction, boolean addEvent) {
transaction.reverse();
- LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(transaction);
- businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data));
+ if (addEvent) {
+ LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(transaction);
+ businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data));
+ }
}
private LocalDate getFinalAccrualTransactionDate(Loan loan) {
@@ -1229,7 +1204,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
}
public boolean isProgressiveAccrual(@NotNull Loan loan) {
- return loan.getLoanProductRelatedDetail().getLoanScheduleType() == LoanScheduleType.PROGRESSIVE;
+ return loan.isProgressiveSchedule();
}
private void setSetHelpers(Loan loan) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
index 3e3b8dee0..c8525e4de 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
@@ -301,17 +301,18 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
loan = loanAccountDomainService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
}
- postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
-
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && isAppliedOnBackDate
&& loan.isFeeCompoundingEnabledForInterestRecalculation()) {
- loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
- loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+ loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, true, false);
}
this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
businessEventNotifierService.notifyPostBusinessEvent(new LoanAddChargeBusinessEvent(loanCharge));
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
+
+ postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+ loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
return new CommandProcessingResultBuilder().withCommandId(command.commandId()) //
.withEntityId(loanCharge.getId()) //
.withEntityExternalId(loanCharge.getExternalId()) //
@@ -825,14 +826,14 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
loan = loanAccountDomainService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
}
- postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
-
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && runInterestRecalculation
&& loan.isFeeCompoundingEnabledForInterestRecalculation()) {
- loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
- loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+ loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, true, false);
}
this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
+
+ postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+ loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
}
}
@@ -1050,9 +1051,11 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|| (configurationDomainService.isImmediateChargeAccrualPostMaturityEnabled()
&& DateUtils.getBusinessLocalDate().isAfter(loan.getMaturityDate()))) {
final LoanTransaction applyLoanChargeTransaction = loan.handleChargeAppliedTransaction(loanCharge, null);
- this.loanTransactionRepository.saveAndFlush(applyLoanChargeTransaction);
- businessEventNotifierService
- .notifyPostBusinessEvent(new LoanAccrualTransactionCreatedBusinessEvent(applyLoanChargeTransaction));
+ if (applyLoanChargeTransaction != null) {
+ this.loanTransactionRepository.saveAndFlush(applyLoanChargeTransaction);
+ businessEventNotifierService
+ .notifyPostBusinessEvent(new LoanAccrualTransactionCreatedBusinessEvent(applyLoanChargeTransaction));
+ }
}
return DateUtils.isBeforeBusinessDate(loanCharge.getDueLocalDate());
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanStatusChangePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanStatusChangePlatformServiceImpl.java
index 06f402c41..7d4e5a1a7 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanStatusChangePlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanStatusChangePlatformServiceImpl.java
@@ -32,7 +32,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
public class LoanStatusChangePlatformServiceImpl implements LoanStatusChangePlatformService {
private final BusinessEventNotifierService businessEventNotifierService;
- private final LoanAccrualsProcessingService loanAccrualsProcessingService;
private final LoanAccrualActivityProcessingService loanAccrualActivityProcessingService;
@PostConstruct
@@ -42,21 +41,14 @@ public class LoanStatusChangePlatformServiceImpl implements LoanStatusChangePlat
new LoanAccrualActivityPostingLoanStatusChangedListener());
}
- private final class LoanStatusChangedListener implements BusinessEventListener<LoanStatusChangedBusinessEvent> {
+ private static final class LoanStatusChangedListener implements BusinessEventListener<LoanStatusChangedBusinessEvent> {
@Override
public void onBusinessEvent(LoanStatusChangedBusinessEvent event) {
final Loan loan = event.get();
log.debug("Loan Status change for loan {}", loan.getId());
- LoanStatus oldStatus = event.getOldStatus();
- LoanStatus newStatus = loan.getStatus();
- if (oldStatus.isActive() && (newStatus.isClosedObligationsMet() || newStatus.isOverpaid())) {
- log.debug("Loan Status {} for loan {}", newStatus.getCode(), loan.getId());
- loan.updateLoanSummaryDerivedFields();
- loanAccrualsProcessingService.processAccrualsOnLoanClosure(loan);
- } else if ((oldStatus.isClosed() || oldStatus.isOverpaid()) && newStatus.isActive()) {
+ if (loan.isOpen()) {
loan.handleMaturityDateActivate();
- loanAccrualsProcessingService.processAccrualsOnLoanReopen(loan);
}
}
}
@@ -70,9 +62,7 @@ public class LoanStatusChangePlatformServiceImpl implements LoanStatusChangePlat
if (loan.getLoanProductRelatedDetail().isEnableAccrualActivityPosting()) {
LoanStatus oldStatus = event.getOldStatus();
LoanStatus newStatus = loan.getStatus();
- if (oldStatus.isActive() && (newStatus.isClosedObligationsMet() || newStatus.isOverpaid())) {
- loanAccrualActivityProcessingService.processAccrualActivityForLoanClosure(loan);
- } else if ((oldStatus.isClosed() || oldStatus.isOverpaid()) && newStatus.isActive()) {
+ if ((oldStatus.isClosed() || oldStatus.isOverpaid()) && newStatus.isActive()) {
loanAccrualActivityProcessingService.processAccrualActivityForLoanReopen(loan);
}
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionAssembler.java
index 0e641a7df..11f60232a 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionAssembler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionAssembler.java
@@ -18,6 +18,7 @@
*/
package org.apache.fineract.portfolio.loanaccount.service;
+import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Map;
@@ -26,7 +27,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
-import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
+import org.apache.fineract.infrastructure.core.service.MathUtil;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
@@ -68,16 +69,17 @@ public class LoanTransactionAssembler {
txnExternalId, null);
}
- public LoanTransaction assembleAccrualActivityTransaction(Loan loan, final LoanRepaymentScheduleInstallment installment,
- final LocalDate transactionDate) {
- MonetaryCurrency currency = loan.getCurrency();
+ public LoanTransaction assembleAccrualActivityTransaction(@NotNull Loan loan, @NotNull LoanRepaymentScheduleInstallment installment,
+ @NotNull LocalDate transactionDate) {
ExternalId externalId = externalIdFactory.create();
- BigDecimal interestPortion = installment.getInterestCharged(currency).getAmount();
- BigDecimal feeChargesPortion = installment.getFeeChargesCharged(currency).getAmount();
- BigDecimal penaltyChargesPortion = installment.getPenaltyChargesCharged(currency).getAmount();
- BigDecimal transactionAmount = interestPortion.add(feeChargesPortion).add(penaltyChargesPortion);
- return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.ACCRUAL_ACTIVITY.getValue(), transactionDate,
- transactionAmount, null, interestPortion, feeChargesPortion, penaltyChargesPortion, null, false, null, externalId);
+ BigDecimal interestPortion = installment.getInterestCharged();
+ BigDecimal feeChargesPortion = installment.getFeeChargesCharged();
+ BigDecimal penaltyChargesPortion = installment.getPenaltyCharges();
+ BigDecimal transactionAmount = MathUtil.add(interestPortion, feeChargesPortion, penaltyChargesPortion);
+ return MathUtil.isGreaterThanZero(transactionAmount)
+ ? new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.ACCRUAL_ACTIVITY.getValue(), transactionDate,
+ transactionAmount, null, interestPortion, feeChargesPortion, penaltyChargesPortion, null, false, null, externalId)
+ : null;
}
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index a747373c7..08040cb1e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -95,8 +95,6 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.LoanWithdra
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanChargeOffPostBusinessEvent;
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanChargeOffPreBusinessEvent;
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanDisbursalTransactionBusinessEvent;
-import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionAccrualActivityPostBusinessEvent;
-import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionAccrualActivityPreBusinessEvent;
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionBusinessEvent;
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionInterestPaymentWaiverPostBusinessEvent;
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionInterestPaymentWaiverPreBusinessEvent;
@@ -230,7 +228,6 @@ import org.springframework.transaction.annotation.Transactional;
@RequiredArgsConstructor
public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatformService {
- private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory;
private final PlatformSecurityContext context;
private final LoanTransactionValidator loanTransactionValidator;
private final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer;
@@ -499,9 +496,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
createNote(loan, command, changes);
// auto create standing instruction
createStandingInstruction(loan);
-
- postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
- loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
}
final Set<LoanCharge> loanCharges = loan.getActiveCharges();
@@ -526,8 +520,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
}
updateRecurringCalendarDatesForInterestRecalculation(loan);
- loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
- loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+ loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+ loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
// Post Dated Checks
@@ -551,6 +545,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
businessEventNotifierService.notifyPostBusinessEvent(new LoanDisbursalTransactionBusinessEvent(disbursalTransaction));
}
+ postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+ loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(loan.getId()) //
@@ -869,8 +866,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
}
updateRecurringCalendarDatesForInterestRecalculation(loan);
- loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
- loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+ loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+ loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), true);
loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
businessEventNotifierService.notifyPostBusinessEvent(new LoanDisbursalBusinessEvent(loan));
}
@@ -1085,8 +1082,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private ChangedTransactionDetail recalculateLoanWithInterestPaymentWaiverTxn(Loan loan,
LoanTransaction newInterestPaymentWaiverTransaction) {
LocalDate recalculateFrom = null;
+ LocalDate transactionDate = newInterestPaymentWaiverTransaction.getTransactionDate();
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
- recalculateFrom = newInterestPaymentWaiverTransaction.getTransactionDate();
+ recalculateFrom = transactionDate;
}
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom, null);
@@ -1102,14 +1100,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loan.getTransactionProcessor();
- final LoanRepaymentScheduleInstallment currentInstallment = loan
- .fetchLoanRepaymentScheduleInstallmentByDueDate(newInterestPaymentWaiverTransaction.getTransactionDate());
+ final LoanRepaymentScheduleInstallment currentInstallment = loan.fetchLoanRepaymentScheduleInstallmentByDueDate(transactionDate);
boolean reprocess = true;
-
- if (!loan.isForeclosure() && isTransactionChronologicallyLatest
- && DateUtils.isEqualBusinessDate(newInterestPaymentWaiverTransaction.getTransactionDate()) && currentInstallment != null
- && currentInstallment.getTotalOutstanding(loan.getCurrency())
+ if (!loan.isForeclosure() && isTransactionChronologicallyLatest && DateUtils.isEqualBusinessDate(transactionDate)
+ && currentInstallment != null && currentInstallment.getTotalOutstanding(loan.getCurrency())
.isEqualTo(newInterestPaymentWaiverTransaction.getAmount(loan.getCurrency()))) {
reprocess = false;
}
@@ -1155,66 +1150,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
return loan.reprocessTransactions();
}
- @Transactional
- @Override
- public Loan reverseReplayAccrualActivityTransaction(Loan loan, final LoanTransaction loanTransaction,
- final LoanRepaymentScheduleInstallment installment, final LocalDate transactionDate) {
-
- LoanTransaction newLoanTransaction = loanTransactionAssembler.assembleAccrualActivityTransaction(loan, installment,
- transactionDate);
- if (!newLoanTransaction.getDateOf().isEqual(loanTransaction.getDateOf())
- || !LoanTransaction.transactionAmountsMatch(loan.getCurrency(), loanTransaction, newLoanTransaction)) {
- loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(loanTransaction.getLoan(), loanTransaction,
- "reversed");
- loanTransaction.reverse();
- loanTransaction.updateExternalId(null);
- newLoanTransaction.copyLoanTransactionRelations(loanTransaction.getLoanTransactionRelations());
- newLoanTransaction.getLoanTransactionRelations().add(LoanTransactionRelation.linkToTransaction(newLoanTransaction,
- loanTransaction, LoanTransactionRelationTypeEnum.REPLAYED));
- loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(newLoanTransaction);
- loan.addLoanTransaction(newLoanTransaction);
-
- LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(loanTransaction);
- data.setNewTransactionDetail(newLoanTransaction);
- businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data));
- }
- return loan;
- }
-
- @Transactional
- @Override
- public Loan makeAccrualActivityTransaction(Loan loan, final LoanRepaymentScheduleInstallment installment,
- final LocalDate transactionDate) {
-
- LoanTransaction newAccrualActivityTransaction = loanTransactionAssembler.assembleAccrualActivityTransaction(loan, installment,
- transactionDate);
-
- if (newAccrualActivityTransaction.getAmount().compareTo(BigDecimal.ZERO) == 0) {
- return loan;
- }
-
- return makeAccrualActivityTransaction(loan, newAccrualActivityTransaction);
- }
-
- @Transactional
- @Override
- public Loan makeAccrualActivityTransaction(Loan loan, LoanTransaction newAccrualActivityTransaction) {
- businessEventNotifierService.notifyPreBusinessEvent(new LoanTransactionAccrualActivityPreBusinessEvent(loan));
-
- loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(newAccrualActivityTransaction);
-
- loan.addLoanTransaction(newAccrualActivityTransaction);
-
- businessEventNotifierService
- .notifyPostBusinessEvent(new LoanTransactionAccrualActivityPostBusinessEvent(newAccrualActivityTransaction));
-
- return loan;
- }
-
@Transactional
@Override
public CommandProcessingResult makeInterestPaymentWaiver(final JsonCommand command) {
-
loanTransactionValidator.validateLoanTransactionInterestPaymentWaiver(command);
final Long loanId = command.getLoanId();
@@ -1245,10 +1183,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
saveNote(noteText, loan, newInterestPaymentWaiverTransaction);
- postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
- loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
- loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
- loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+ loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+ loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
loanAccountDomainService.setLoanDelinquencyTag(loan, newInterestPaymentWaiverTransaction.getTransactionDate());
// disable all active standing orders linked to this loan if status changes to closed
@@ -1264,6 +1200,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
businessEventNotifierService.notifyPostBusinessEvent(transactionRepaymentEvent);
+ postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+ loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
return new CommandProcessingResultBuilder().withCommandId(command.commandId()) //
.withLoanId(loan.getId()) //
.withEntityId(newInterestPaymentWaiverTransaction.getId()) //
@@ -1564,11 +1503,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loan.updateLoanSummaryAndStatus();
}
- postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
- loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
-
- loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
- loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+ loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+ loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
@@ -1586,6 +1522,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(eventData));
+ postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+ loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(entityId) //
@@ -1647,9 +1586,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
writeOffTransaction.reverse();
}
- if (loan.isClosedObligationsMet() || loan.isClosedWrittenOff() || loan.isClosedWithOutstandingAmountMarkedForReschedule()) {
- loanLifecycleStateMachine.transition(LoanEvent.LOAN_ADJUST_TRANSACTION, loan);
- }
+ loan.updateLoanSummaryAndStatus();
if (newTransactionDetail.isRepaymentLikeType() || newTransactionDetail.isInterestWaiver()) {
changedTransactionDetail = loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan,
@@ -1850,14 +1787,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.noteRepository.save(note);
}
- postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
- loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
- loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
- loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+ loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+ loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
businessEventNotifierService.notifyPostBusinessEvent(new LoanWaiveInterestBusinessEvent(waiveInterestTransaction));
+
+ postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+ loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(waiveInterestTransaction.getId()) //
@@ -1918,44 +1856,48 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.WRITE_OFF_OUTSTANDING);
loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.WRITE_OFF_OUTSTANDING,
writtenOffOnLocalDate);
- final ChangedTransactionDetail changedTransactionDetail = closeAsWrittenOff(loan, command, loanLifecycleStateMachine, changes,
- existingTransactionIds, existingReversedTransactionIds, currentUser, scheduleGeneratorDTO);
- loanAccrualsProcessingService.reprocessExistingAccruals(loan);
- if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) {
- loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
- }
-
- LoanTransaction writeOff = changedTransactionDetail.getNewTransactionMappings().remove(0L);
- this.loanTransactionRepository.saveAndFlush(writeOff);
- for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
- this.loanTransactionRepository.save(mapEntry.getValue());
- this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
- }
- saveLoanWithDataIntegrityViolationChecks(loan);
- final String noteText = command.stringValueOfParameterNamed("note");
- if (StringUtils.isNotBlank(noteText)) {
- changes.put("note", noteText);
- final Note note = Note.loanTransactionNote(loan, writeOff, noteText);
- this.noteRepository.save(note);
- }
-
- postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
- loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
- loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
- loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
- loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
- businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
- businessEventNotifierService.notifyPostBusinessEvent(new LoanWrittenOffPostBusinessEvent(writeOff));
- return new CommandProcessingResultBuilder() //
+ CommandProcessingResultBuilder builder = new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
- .withEntityId(writeOff.getId()) //
- .withEntityExternalId(writeOff.getExternalId()) //
.withOfficeId(loan.getOfficeId()) //
.withClientId(loan.getClientId()) //
.withGroupId(loan.getGroupId()) //
.withLoanId(loanId) //
- .with(changes).build();
+ .with(changes);
+
+ final ChangedTransactionDetail changedTransactionDetail = closeAsWrittenOff(loan, command, loanLifecycleStateMachine, changes,
+ existingTransactionIds, existingReversedTransactionIds, currentUser, scheduleGeneratorDTO);
+
+ if (changedTransactionDetail != null) {
+ loanAccrualsProcessingService.reprocessExistingAccruals(loan);
+ if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) {
+ loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
+ }
+ LoanTransaction writeOff = changedTransactionDetail.getNewTransactionMappings().remove(0L);
+ this.loanTransactionRepository.saveAndFlush(writeOff);
+ for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
+ this.loanTransactionRepository.save(mapEntry.getValue());
+ this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
+ }
+ saveLoanWithDataIntegrityViolationChecks(loan);
+ final String noteText = command.stringValueOfParameterNamed("note");
+ if (StringUtils.isNotBlank(noteText)) {
+ changes.put("note", noteText);
+ final Note note = Note.loanTransactionNote(loan, writeOff, noteText);
+ this.noteRepository.save(note);
+ }
+ loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+ loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
+ loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
+ businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
+ businessEventNotifierService.notifyPostBusinessEvent(new LoanWrittenOffPostBusinessEvent(writeOff));
+
+ postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+ loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
+ builder.withEntityId(writeOff.getId()).withEntityExternalId(writeOff.getExternalId());
+ }
+ return builder.build();
}
@Transactional
@@ -2020,12 +1962,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.noteRepository.save(note);
}
- if (possibleClosingTransaction != null) {
- postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
- }
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
- loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
- loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+ loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+ loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
@@ -2037,6 +1976,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
// disable all active standing instructions linked to the loan
this.loanAccountDomainService.disableStandingInstructionsLinkedToClosedLoan(loan);
+ postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+ loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
CommandProcessingResult result;
if (possibleClosingTransaction != null) {
@@ -2654,15 +2596,17 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
+ loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+ loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
+
+ businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
+ businessEventNotifierService.notifyPostBusinessEvent(new LoanUndoWrittenOffBusinessEvent(writeOffTransaction));
+
+ this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
+
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
- loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
- loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
- if (writeOffTransaction != null) {
- businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
- businessEventNotifierService.notifyPostBusinessEvent(new LoanUndoWrittenOffBusinessEvent(writeOffTransaction));
- }
- this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
+
return new CommandProcessingResultBuilder() //
.withOfficeId(loan.getOfficeId()) //
.withClientId(loan.getClientId()) //
@@ -2811,11 +2755,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
createLoanScheduleArchive(loan, scheduleGeneratorDTO);
}
+ loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+ loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
+ this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
+
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
- loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
- loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
- this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
+
return new CommandProcessingResultBuilder() //
.withOfficeId(loan.getOfficeId()) //
.withClientId(loan.getClientId()) //
@@ -2879,11 +2825,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
+ loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+ loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
+ businessEventNotifierService.notifyPostBusinessEvent(new LoanInterestRecalculationBusinessEvent(loan));
+
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
- loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
- loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
- businessEventNotifierService.notifyPostBusinessEvent(new LoanInterestRecalculationBusinessEvent(loan));
return loan;
}
@@ -3396,8 +3343,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
- // Create journal entries for the new transaction(s)
- postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
// Calculate and apply delinquency tag (if applicable)
loanAccountDomainService.setLoanDelinquencyTag(loan, transactionDate);
// disable all active standing orders linked to this loan if status
@@ -3414,11 +3359,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loanAccountDomainService.updateAndSaveLoanCollateralTransactionsForIndividualAccounts(loan, refundTransaction);
}
// Raise business events
- loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
- loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
- loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+ loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+ loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
+ // Create journal entries for the new transaction(s)
+ postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+ loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
Long entityId = refundTransaction.getId();
ExternalId entityExternalId = refundTransaction.getExternalId();
Long subEntityId = interestRefundTransaction != null ? interestRefundTransaction.getId() : null;
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
index fdfaa6076..1f0605de6 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
@@ -100,6 +100,7 @@ import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoReadPlat
import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoWritePlatformService;
import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoWritePlatformServiceImpl;
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualActivityProcessingService;
+import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualEventService;
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualTransactionBusinessEventService;
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualTransactionBusinessEventServiceImpl;
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualsProcessingService;
@@ -332,9 +333,16 @@ public class LoanAccountConfiguration {
@Bean
@ConditionalOnMissingBean(LoanStatusChangePlatformService.class)
public LoanStatusChangePlatformService loanStatusChangePlatformService(BusinessEventNotifierService businessEventNotifierService,
+ LoanAccrualActivityProcessingService loanAccrualActivityProcessingService) {
+ return new LoanStatusChangePlatformServiceImpl(businessEventNotifierService, loanAccrualActivityProcessingService);
+ }
+
+ @Bean
+ @ConditionalOnMissingBean(LoanAccrualEventService.class)
+ public LoanAccrualEventService loanAccrualEventService(BusinessEventNotifierService businessEventNotifierService,
LoanAccrualsProcessingService loanAccrualsProcessingService,
LoanAccrualActivityProcessingService loanAccrualActivityProcessingService) {
- return new LoanStatusChangePlatformServiceImpl(businessEventNotifierService, loanAccrualsProcessingService,
+ return new LoanAccrualEventService(businessEventNotifierService, loanAccrualsProcessingService,
loanAccrualActivityProcessingService);
}
@@ -351,8 +359,8 @@ public class LoanAccountConfiguration {
@Bean
@ConditionalOnMissingBean(LoanWritePlatformService.class)
- public LoanWritePlatformService loanWritePlatformService(LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory,
- PlatformSecurityContext context, LoanTransactionValidator loanTransactionValidator,
+ public LoanWritePlatformService loanWritePlatformService(PlatformSecurityContext context,
+ LoanTransactionValidator loanTransactionValidator,
LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer, LoanRepositoryWrapper loanRepositoryWrapper,
LoanAccountDomainService loanAccountDomainService, NoteRepository noteRepository,
LoanTransactionRepository loanTransactionRepository, LoanTransactionRelationRepository loanTransactionRelationRepository,
@@ -381,18 +389,17 @@ public class LoanAccountConfiguration {
LoanOfficerValidator loanOfficerValidator, LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator,
LoanDisbursementService loanDisbursementService, LoanScheduleService loanScheduleService,
LoanChargeValidator loanChargeValidator, LoanOfficerService loanOfficerService) {
- return new LoanWritePlatformServiceJpaRepositoryImpl(transactionProcessorFactory, context, loanTransactionValidator,
- loanUpdateCommandFromApiJsonDeserializer, loanRepositoryWrapper, loanAccountDomainService, noteRepository,
- loanTransactionRepository, loanTransactionRelationRepository, loanAssembler, journalEntryWritePlatformService,
- calendarInstanceRepository, paymentDetailWritePlatformService, holidayRepository, configurationDomainService,
- workingDaysRepository, accountTransfersWritePlatformService, accountTransfersReadPlatformService,
- accountAssociationsReadPlatformService, loanReadPlatformService, fromApiJsonHelper, calendarRepository,
- loanScheduleHistoryWritePlatformService, loanApplicationValidator, accountAssociationRepository,
- accountTransferDetailRepository, businessEventNotifierService, guarantorDomainService, loanUtilService, loanSummaryWrapper,
- entityDatatableChecksWritePlatformService, transactionProcessingStrategy, codeValueRepository,
- cashierTransactionDataValidator, glimRepository, loanRepository, repaymentWithPostDatedChecksAssembler,
- postDatedChecksRepository, loanRepaymentScheduleInstallmentRepository, defaultLoanLifecycleStateMachine,
- loanAccountLockService, externalIdFactory, replayedTransactionBusinessEventService,
+ return new LoanWritePlatformServiceJpaRepositoryImpl(context, loanTransactionValidator, loanUpdateCommandFromApiJsonDeserializer,
+ loanRepositoryWrapper, loanAccountDomainService, noteRepository, loanTransactionRepository,
+ loanTransactionRelationRepository, loanAssembler, journalEntryWritePlatformService, calendarInstanceRepository,
+ paymentDetailWritePlatformService, holidayRepository, configurationDomainService, workingDaysRepository,
+ accountTransfersWritePlatformService, accountTransfersReadPlatformService, accountAssociationsReadPlatformService,
+ loanReadPlatformService, fromApiJsonHelper, calendarRepository, loanScheduleHistoryWritePlatformService,
+ loanApplicationValidator, accountAssociationRepository, accountTransferDetailRepository, businessEventNotifierService,
+ guarantorDomainService, loanUtilService, loanSummaryWrapper, entityDatatableChecksWritePlatformService,
+ transactionProcessingStrategy, codeValueRepository, cashierTransactionDataValidator, glimRepository, loanRepository,
+ repaymentWithPostDatedChecksAssembler, postDatedChecksRepository, loanRepaymentScheduleInstallmentRepository,
+ defaultLoanLifecycleStateMachine, loanAccountLockService, externalIdFactory, replayedTransactionBusinessEventService,
loanAccrualTransactionBusinessEventService, errorHandler, loanDownPaymentHandlerService, accountTransferRepository,
loanTransactionAssembler, loanAccrualsProcessingService, loanOfficerValidator, loanDownPaymentTransactionValidator,
loanDisbursementService, loanScheduleService, loanChargeValidator, loanOfficerService);
diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImplTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImplTest.java
index b593f2b3a..17f633d3e 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImplTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImplTest.java
@@ -128,6 +128,9 @@ class LoanChargeWritePlatformServiceImplTest {
@Mock
private LoanChargeValidator loanChargeValidator;
+ @Mock
+ private LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService;
+
@BeforeEach
void setUp() {
when(loanAssembler.assembleFrom(LOAN_ID)).thenReturn(loan);
diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImplTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImplTest.java
index 3f3923e8e..4574dc5d8 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImplTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImplTest.java
@@ -240,8 +240,6 @@ class LoanWritePlatformServiceJpaRepositoryImplTest {
when(loan.findExistingReversedTransactionIds()).thenReturn(Collections.emptyList());
doNothing().when(loanTransactionValidator).validateActivityNotBeforeClientOrGroupTransferDate(any(), any(), any());
when(loan.isClosedWrittenOff()).thenReturn(false);
- when(loan.isClosedObligationsMet()).thenReturn(false);
- when(loan.isClosedWithOutstandingAmountMarkedForReschedule()).thenReturn(false);
when(newTransactionDetail.isRepaymentLikeType()).thenReturn(true);
// Act
@@ -287,8 +285,6 @@ class LoanWritePlatformServiceJpaRepositoryImplTest {
when(loan.findExistingReversedTransactionIds()).thenReturn(Collections.emptyList());
doNothing().when(loanTransactionValidator).validateActivityNotBeforeClientOrGroupTransferDate(any(), any(), any());
when(loan.isClosedWrittenOff()).thenReturn(false);
- when(loan.isClosedObligationsMet()).thenReturn(false);
- when(loan.isClosedWithOutstandingAmountMarkedForReschedule()).thenReturn(false);
when(newTransactionDetail.isRepaymentLikeType()).thenReturn(true);
// Act
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
index c494a0595..b164f88a7 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
@@ -553,7 +553,7 @@ public abstract class BaseLoanIntegrationTest {
protected void verifyTransactions(Long loanId, Transaction... transactions) {
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
if (transactions == null || transactions.length == 0) {
- Assertions.assertTrue(loanDetails.getTransactions().isEmpty(), "No transaction is expected");
+ Assertions.assertTrue(loanDetails.getTransactions().isEmpty(), "No transaction is expected on loan " + loanId);
} else {
Assertions.assertEquals(transactions.length, loanDetails.getTransactions().size());
Arrays.stream(transactions).forEach(tr -> {
@@ -562,12 +562,13 @@ public abstract class BaseLoanIntegrationTest {
&& Objects.equals(item.getType().getValue(), tr.type) //
&& Objects.equals(item.getDate(), LocalDate.parse(tr.date, dateTimeFormatter)))
.findFirst();
- Assertions.assertTrue(optTx.isPresent(), "Required transaction not found: " + tr);
+ Assertions.assertTrue(optTx.isPresent(), "Required transaction not found: " + tr + " on loan " + loanId);
GetLoansLoanIdTransactions tx = optTx.get();
if (tr.reversed != null) {
- Assertions.assertEquals(tr.reversed, tx.getManuallyReversed(), "Transaction is not reversed: " + tr);
+ Assertions.assertEquals(tr.reversed, tx.getManuallyReversed(),
+ "Transaction is not reversed: " + tr + " on loan " + loanId);
}
});
}
@@ -576,9 +577,9 @@ public abstract class BaseLoanIntegrationTest {
protected void verifyTransactions(Long loanId, TransactionExt... transactions) {
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
if (transactions == null || transactions.length == 0) {
- assertNull(loanDetails.getTransactions(), "No transaction is expected");
+ assertNull(loanDetails.getTransactions(), "No transaction is expected on loan " + loanId);
} else {
- Assertions.assertEquals(transactions.length, loanDetails.getTransactions().size());
+ Assertions.assertEquals(transactions.length, loanDetails.getTransactions().size(), "Number of transactions on loan " + loanId);
Arrays.stream(transactions).forEach(tr -> {
boolean found = loanDetails.getTransactions().stream().anyMatch(item -> Objects.equals(item.getAmount(), tr.amount) //
&& Objects.equals(item.getType().getValue(), tr.type) //
@@ -591,7 +592,7 @@ public abstract class BaseLoanIntegrationTest {
&& Objects.equals(item.getOverpaymentPortion(), tr.overpaymentPortion) //
&& Objects.equals(item.getUnrecognizedIncomePortion(), tr.unrecognizedPortion) //
);
- Assertions.assertTrue(found, "Required transaction not found: " + tr);
+ Assertions.assertTrue(found, "Required transaction not found: " + tr + " on loan " + loanId);
});
}
}
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeProgressiveTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeProgressiveTest.java
index 5b7de1503..a217e704b 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeProgressiveTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeProgressiveTest.java
@@ -27,6 +27,7 @@ import org.apache.fineract.client.models.PostLoanProductsResponse;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.infrastructure.configuration.api.GlobalConfigurationConstants;
import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -78,15 +79,20 @@ public class LoanChargeProgressiveTest extends BaseLoanIntegrationTest {
addLoanCharge(loanId, chargeResponse.getResourceId(), "03 October 2024", 20.0d);
final GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
Assertions.assertTrue(
- loanDetails.getTransactions().stream().anyMatch(t -> t.getType().getAccrual() && t.getAmount().equals(20.0d)));
+ loanDetails.getTransactions().stream().noneMatch(t -> t.getType().getAccrual() && t.getAmount().equals(20.0d)));
});
runAt("04 October 2024", () -> {
globalConfigurationHelper.manageConfigurations(GlobalConfigurationConstants.ENABLE_IMMEDIATE_CHARGE_ACCRUAL_POST_MATURITY,
false);
executeInlineCOB(loanId);
final GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
- Assertions.assertTrue(
- loanDetails.getTransactions().stream().anyMatch(t -> t.getType().getAccrual() && t.getAmount().equals(20.0d)));
+ Assertions.assertTrue(loanDetails.getTransactions().stream()
+ .anyMatch(t -> t.getType().getAccrual() && t.getFeeChargesPortion().equals(20.0d)));
});
}
+
+ @AfterEach
+ public void afterEach() {
+ globalConfigurationHelper.manageConfigurations(GlobalConfigurationConstants.ENABLE_IMMEDIATE_CHARGE_ACCRUAL_POST_MATURITY, false);
+ }
}
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargebackWithCreditAllocationsIntegrationTests.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargebackWithCreditAllocationsIntegrationTests.java
index 70b42eecb..696fafe63 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargebackWithCreditAllocationsIntegrationTests.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargebackWithCreditAllocationsIntegrationTests.java
@@ -300,6 +300,7 @@ public class LoanChargebackWithCreditAllocationsIntegrationTests extends BaseLoa
transaction(312.0, "Repayment", "20 February 2023", 626.0, 312.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(312.0, "Repayment", "20 March 2023", 314.0, 312.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(384.0, "Repayment", "20 April 2023", 0.0, 314.0, 0.0, 50.0, 20.0, 0.0, 0.0), //
+ transaction(70.0, "Accrual", "20 April 2023", 0.0, 0.0, 0.0, 50.0, 20.0, 0.0, 0.0), //
transaction(100.0, "Chargeback", "02 May 2023", 30.0, 30.0, 0.0, 50.0, 20.0, 0.0, 0.0) //
);
@@ -584,6 +585,7 @@ public class LoanChargebackWithCreditAllocationsIntegrationTests extends BaseLoa
verifyTransactions(loanId, //
transaction(1250.0, "Disbursement", "01 January 2023", 1250.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(1370.0, "Repayment", "20 January 2023", 0, 1250.0, 0.0, 50.0, 20.0, 0.0, 50.0), //
+ transaction(70.0, "Accrual", "20 January 2023", 0.0, 0.0, 0.0, 50.0, 20.0, 0.0, 0.0), //
transaction(100.0, "Chargeback", "02 May 2023", 50.0, 100.0, 0.0, 0.0, 0.0, 0.0, 50.0) //
);
@@ -649,6 +651,7 @@ public class LoanChargebackWithCreditAllocationsIntegrationTests extends BaseLoa
verifyTransactions(loanId, //
transaction(1250.0, "Disbursement", "01 January 2023", 1250.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(1370.0, "Repayment", "20 January 2023", 0, 1250.0, 0.0, 50.0, 20.0, 0.0, 50.0), //
+ transaction(70.0, "Accrual", "20 January 2023", 0.0, 0.0, 0.0, 50.0, 20.0, 0.0, 0.0), //
transaction(100.0, "Chargeback", "20 January 2023", 30.0, 30.0, 0.0, 50.0, 20.0, 0.0, 50.0) //
);
@@ -715,6 +718,7 @@ public class LoanChargebackWithCreditAllocationsIntegrationTests extends BaseLoa
verifyTransactions(loanId, //
transaction(1250.0, "Disbursement", "01 January 2023", 1250.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
transaction(1370.0, "Repayment", "20 January 2023", 0, 1250.0, 0.0, 50.0, 20.0, 0.0, 50.0), //
+ transaction(70.0, "Accrual", "20 January 2023", 0.0, 0.0, 0.0, 50.0, 20.0, 0.0, 0.0), //
transaction(100.0, "Chargeback", "20 January 2023", 0.0, 30.0, 0.0, 50.0, 20.0, 0.0, 50.0) //
);
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRefundTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRefundTest.java
index 3af858d15..c9cc50765 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRefundTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRefundTest.java
@@ -210,9 +210,11 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
logLoanTransactions(loanId);
- verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"),
- transaction(1000.0, "Merchant Issued Refund", "22 January 2021"), transaction(5.74, "Accrual", "22 January 2021"),
- transaction(5.74, "Interest Refund", "22 January 2021"));
+ verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), //
+ transaction(1000.0, "Merchant Issued Refund", "22 January 2021"), //
+ transaction(5.74, "Accrual", "22 January 2021"), //
+ transaction(5.74, "Interest Refund", "22 January 2021") //
+ );
});
}
@@ -241,9 +243,11 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
logLoanTransactions(loanId);
- verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"),
- transaction(1000.0, "Payout Refund", "22 January 2021"), transaction(5.74, "Accrual", "22 January 2021"),
- transaction(5.74, "Interest Refund", "22 January 2021"));
+ verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), //
+ transaction(1000.0, "Payout Refund", "22 January 2021"), //
+ transaction(5.74, "Accrual", "22 January 2021"), //
+ transaction(5.74, "Interest Refund", "22 January 2021") //
+ );
});
}
@@ -272,9 +276,10 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
logLoanTransactions(loanId);
- verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"),
- transaction(1000.0, "Payout Refund", "01 February 2021"), transaction(8.48, "Accrual", "01 February 2021"),
- transaction(8.48, "Interest Refund", "01 February 2021"));
+ verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), //
+ transaction(1000.0, "Payout Refund", "01 February 2021"), //
+ transaction(8.48, "Accrual", "01 February 2021"), //
+ transaction(8.48, "Interest Refund", "01 February 2021")); //
});
}
@@ -315,9 +320,12 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
logLoanTransactions(loanId);
- verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"),
- transaction(87.89, "Repayment", "01 February 2021"), transaction(1000.0, "Payout Refund", "09 February 2021"),
- transaction(10.49, "Interest Refund", "09 February 2021"), transaction(10.49, "Accrual", "09 February 2021"));
+ verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), //
+ transaction(87.89, "Repayment", "01 February 2021"), //
+ transaction(1000.0, "Payout Refund", "09 February 2021"), //
+ transaction(10.49, "Interest Refund", "09 February 2021"), //
+ transaction(10.49, "Accrual", "09 February 2021") //
+ );
});
}
@@ -350,9 +358,12 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
logLoanTransactions(loanId);
- verifyTransactions(loanId, transaction(750.0, "Disbursement", "01 January 2021"),
- transaction(250.0, "Disbursement", "01 January 2021"), transaction(1000.0, "Payout Refund", "22 January 2021"),
- transaction(5.74, "Accrual", "22 January 2021"), transaction(5.74, "Interest Refund", "22 January 2021"));
+ verifyTransactions(loanId, transaction(750.0, "Disbursement", "01 January 2021"), //
+ transaction(250.0, "Disbursement", "01 January 2021"), //
+ transaction(1000.0, "Payout Refund", "22 January 2021"), //
+ transaction(5.74, "Accrual", "22 January 2021"), //
+ transaction(5.74, "Interest Refund", "22 January 2021") //
+ );
});
}
@@ -386,9 +397,12 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
logLoanTransactions(loanId);
- verifyTransactions(loanId, transaction(750.0, "Disbursement", "04 January 2021"),
- transaction(250.0, "Disbursement", "01 January 2021"), transaction(1000.0, "Payout Refund", "22 January 2021"),
- transaction(5.13, "Accrual", "22 January 2021"), transaction(5.13, "Interest Refund", "22 January 2021"));
+ verifyTransactions(loanId, transaction(750.0, "Disbursement", "04 January 2021"), //
+ transaction(250.0, "Disbursement", "01 January 2021"), //
+ transaction(1000.0, "Payout Refund", "22 January 2021"), //
+ transaction(5.13, "Accrual", "22 January 2021"), //
+ transaction(5.13, "Interest Refund", "22 January 2021") //
+ );
});
}
@@ -435,10 +449,13 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
logLoanTransactions(loanId);
- verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 January 2021"),
- transaction(500.0, "Disbursement", "07 January 2021"), transaction(1000.0, "Payout Refund", "09 February 2021"),
- transaction(87.82, "Repayment", "01 February 2021"), transaction(9.67, "Interest Refund", "09 February 2021"),
- transaction(9.67, "Accrual", "09 February 2021"));
+ verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 January 2021"), //
+ transaction(500.0, "Disbursement", "07 January 2021"), //
+ transaction(1000.0, "Payout Refund", "09 February 2021"), //
+ transaction(87.82, "Repayment", "01 February 2021"), //
+ transaction(9.67, "Interest Refund", "09 February 2021"), //
+ transaction(9.67, "Accrual", "09 February 2021") //
+ );
});
}
@@ -1115,9 +1132,10 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), //
transaction(85.63, "Repayment", "10 January 2021"), //
- transaction(5.7, "Accrual", "22 January 2021"), //
+ transaction(5.70, "Accrual", "22 January 2021"), //
transaction(1000.0, "Merchant Issued Refund", "22 January 2021"), //
- transaction(5.42, "Interest Refund", "22 January 2021") //
+ transaction(5.42, "Interest Refund", "22 January 2021"), //
+ transaction(0.28, "Accrual Adjustment", "22 January 2021") //
);
});
}
@@ -1202,7 +1220,8 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
reversedTransaction(85.63, "Repayment", "10 January 2021"), //
transaction(1000.0, "Merchant Issued Refund", "22 January 2021"), //
transaction(5.70, "Interest Refund", "22 January 2021"), //
- transaction(5.42, "Accrual", "22 January 2021") //
+ transaction(5.42, "Accrual", "22 January 2021"), //
+ transaction(0.28, "Accrual", "22 January 2021") //
);
});
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAccrualActivityPostingTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAccrualActivityPostingTest.java
index a531b6374..622394ea8 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAccrualActivityPostingTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAccrualActivityPostingTest.java
@@ -569,8 +569,8 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
verifyTransactions(loanId.get(),
transaction(1000.0, "Disbursement", disbursementDay, 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
transaction(1070.0, "Repayment", repaymentDate1, 0.0, 1000.0, 0.0, 40.0, 30, 0.0, 0.0, true), //
+ transaction(70.0, "Accrual", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
transaction(70.0, "Accrual Activity", repaymentPeriod1DueDate, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false)); //
-
});
}
@@ -652,32 +652,36 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
addRepaymentForLoan(loanId.get(), 650.0, repaymentDate1);
- verifyTransactions(loanId.get(), transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 0.0, 40.0, 30.0, 0.0, 80.0, false), //
+ verifyTransactions(loanId.get(),
+ transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
+ transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 0.0, 40.0, 30.0, 0.0, 80.0, false), //
transaction(70.0, "Accrual", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
- transaction(70.0, "Accrual Activity", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
- transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)); //
-
+ transaction(70.0, "Accrual Activity", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false) //
+ );
});
runAt(disbursementDay2, () -> {
loanTransactionHelper.disburseLoan(loanId.get(), new PostLoansLoanIdRequest().actualDisbursementDate(disbursementDay2)
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(500.0)).locale("en"));
- verifyTransactions(loanId.get(), transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 0.0, 40.0, 30.0, 0.0, 80.0, false), //
- transaction(500.0, "Disbursement", disbursementDay2, 420.0, 0.0, 0.0, 0.0, 0.0, 0.0, 80.0, false), //
- transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)); //
-
+ verifyTransactions(loanId.get(),
+ transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
+ transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 0.0, 40.0, 30.0, 0.0, 80.0, false), //
+ transaction(70.0, "Accrual", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
+ transaction(500.0, "Disbursement", disbursementDay2, 420.0, 0.0, 0.0, 0.0, 0.0, 0.0, 80.0, false) //
+ );
});
runAt(repaymentPeriod1CloseDate, () -> {
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.get()));
- verifyTransactions(loanId.get(), transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 0.0, 40.0, 30.0, 0.0, 80.0, false), //
+ verifyTransactions(loanId.get(),
+ transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
+ transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 0.0, 40.0, 30.0, 0.0, 80.0, false), //
+ transaction(70.0, "Accrual", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
transaction(500.0, "Disbursement", disbursementDay2, 420.0, 0.0, 0.0, 0.0, 0.0, 0.0, 80.0, false), //
- transaction(70.0, "Accrual", repaymentPeriod1DueDate, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
- transaction(70.0, "Accrual Activity", repaymentPeriod1DueDate, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
- transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)); //
-
+ transaction(70.0, "Accrual Activity", repaymentPeriod1DueDate, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false) //
+ );
});
}
@@ -727,6 +731,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
verifyTransactions(loanId.get(), transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 0.0, 40.0, 30.0, 0.0, 80.0, false), //
transaction(500.0, "Disbursement", disbursementDay2, 420.0, 0.0, 0.0, 0.0, 0.0, 0.0, 80.0, false), //
+ transaction(70.0, "Accrual", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
transaction(70.0, "Accrual Activity", repaymentPeriod1DueDate, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)); //
@@ -828,6 +833,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
verifyTransactions(loanId.get(), transaction(500.0, "Disbursement", disbursementDay, 500.0, 0, 0, 0, 0, 0, 0, false),
transaction(570, "Repayment", repaymentDate1, 0.0, 500.0, 0, 40.0, 30.0, 0, 0, false),
+ transaction(70.0, "Accrual", repaymentDate1, 0, 0, 0.0, 40.0, 30.0, 0, 0, false),
transaction(70.0, "Accrual Activity", repaymentPeriod1DueDate, 0, 0, 0.0, 40.0, 30.0, 0, 0, false),
transaction(500.0, "Disbursement", disbursementDay2, 500, 0, 0, 0, 0, 0, 0, false));
});
@@ -837,9 +843,9 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
verifyTransactions(loanId.get(), transaction(500.0, "Disbursement", disbursementDay, 500.0, 0, 0, 0, 0, 0, 0, false),
transaction(570.0, "Repayment", repaymentDate1, 0.0, 500.0, 0, 40.0, 30.0, 0, 0, false),
+ transaction(70.0, "Accrual", repaymentDate1, 0, 0, 0.0, 40.0, 30.0, 0, 0, false),
transaction(70.0, "Accrual Activity", repaymentPeriod1DueDate, 0, 0, 0.0, 40.0, 30.0, 0, 0, false),
- transaction(500.0, "Disbursement", disbursementDay2, 500.0, 0, 0, 0, 0, 0, 0, false),
- transaction(70.0, "Accrual", repaymentPeriod2DueDate, 0, 0, 0.0, 40.0, 30.0, 0, 0, false));
+ transaction(500.0, "Disbursement", disbursementDay2, 500.0, 0, 0, 0, 0, 0, 0, false));
});
}
@@ -867,18 +873,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
chargeFee(loanId.get(), 40.0, repaymentPeriod1DueDate);
addRepaymentForLoan(loanId.get(), 650.0, repaymentDate1);
verifyTransactions(loanId.get(),
- transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 39.45, 40.0, 30.0, 0.0, 40.55, false), // transaction(109.45,
- // "Accrual",
- // repaymentDate1,
- // 0.0,
- // 0,
- // 39.45,
- // 40.0,
- // 30.0,
- // 0,
- // 0,
- // false),
- // //
+ transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 39.45, 40.0, 30.0, 0.0, 40.55, false), //
transaction(109.45, "Accrual Activity", repaymentDate1, 0.0, 0.0, 39.45, 40.0, 30.0, 0.0, 0.0, false), //
transaction(109.45, "Accrual", repaymentDate1, 0.0, 0.0, 39.45, 40.0, 30.0, 0.0, 0.0, false), //
transaction(500.0, "Disbursement", disbursementDay, 500.0, 0, 0, 0, 0, 0, 0, false) //
@@ -887,18 +882,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
runAt(repaymentPeriod1CloseDate, () -> {
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.get()));
verifyTransactions(loanId.get(),
- transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 39.45, 40.0, 30.0, 0.0, 40.55, false), // transaction(109.45,
- // "Accrual",
- // repaymentDate1,
- // 0.0,
- // 0,
- // 39.45,
- // 40.0,
- // 30.0,
- // 0,
- // 0,
- // false),
- // //
+ transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 39.45, 40.0, 30.0, 0.0, 40.55, false), //
transaction(109.45, "Accrual Activity", repaymentDate1, 0.0, 0.0, 39.45, 40.0, 30.0, 0.0, 0.0, false), //
transaction(109.45, "Accrual", repaymentDate1, 0.0, 0.0, 39.45, 40.0, 30.0, 0.0, 0.0, false), //
transaction(500.0, "Disbursement", disbursementDay, 500.0, 0, 0, 0, 0, 0, 0, false) //