Skip to content

Commit

Permalink
FINERACT-2060: Accrual reverse replay logic and Handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Marta Jankovics committed Nov 26, 2024
1 parent ccfb8c3 commit c5c6955
Show file tree
Hide file tree
Showing 76 changed files with 2,445 additions and 2,748 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package org.apache.fineract.portfolio.charge.service;

import java.util.Collection;
import java.util.List;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;

Expand Down Expand Up @@ -53,7 +54,7 @@ public interface ChargeReadPlatformService {
* Excludes Given List of Charge Types from the response
* @return
*/
Collection<ChargeData> retrieveLoanAccountApplicableCharges(Long loanId, ChargeTimeType[] excludeChargeTimes);
List<ChargeData> retrieveLoanAccountApplicableCharges(Long loanId, ChargeTimeType[] excludeChargeTimes);

/**
* Returns all charges applicable for a given loan product (filter based on Currency of Selected Loan Product)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,23 @@ public static int compare(OffsetDateTime first, OffsetDateTime second) {
}

public static int compare(OffsetDateTime first, OffsetDateTime second, ChronoUnit truncate) {
return compare(first, second, truncate, true);
}

public static int compareWithNullsLast(OffsetDateTime first, OffsetDateTime second) {
return compare(first, second, null, false);
}

public static int compareWithNullsLast(@NotNull Optional<OffsetDateTime> first, @NotNull Optional<OffsetDateTime> second) {
return compareWithNullsLast(first.orElse(null), second.orElse(null));
}

public static int compare(OffsetDateTime first, OffsetDateTime second, ChronoUnit truncate, boolean nullFirst) {
if (first == null) {
return second == null ? 0 : -1;
return second == null ? 0 : (nullFirst ? -1 : 1);
}
if (second == null) {
return 1;
return nullFirst ? 1 : -1;
}
first = first.withOffsetSameInstant(ZoneOffset.UTC);
second = second.withOffsetSameInstant(ZoneOffset.UTC);
Expand Down Expand Up @@ -291,7 +303,23 @@ public static boolean isDateInTheFuture(final LocalDate localDate) {
}

public static int compare(LocalDate first, LocalDate second) {
return first == null ? (second == null ? 0 : -1) : (second == null ? 1 : first.compareTo(second));
return compare(first, second, true);
}

/**
* Comparing dates. Null will be considered as last elements
*
* @param first
* @param second
* @return
*/
public static int compareWithNullsLast(LocalDate first, LocalDate second) {
return compare(first, second, false);
}

public static int compare(LocalDate first, LocalDate second, boolean nullFirst) {
return first == null ? (second == null ? 0 : (nullFirst ? -1 : 1))
: (second == null ? (nullFirst ? 1 : -1) : first.compareTo(second));
}

public static boolean isEqual(LocalDate first, LocalDate second) {
Expand Down Expand Up @@ -426,23 +454,4 @@ private static DateTimeFormatter getDateTimeFormatter(String format, Locale loca
}
return formatter;
}

/**
* Comparing dates. Null will be considered as last elements
*
* @param first
* @param second
* @return
*/
public static int compareWithNullsLast(LocalDate first, LocalDate second) {
return first == null ? (second == null ? 0 : 1) : (second == null ? -1 : first.compareTo(second));
}

public static int compareWithNullsLast(@NotNull Optional<OffsetDateTime> first, @NotNull Optional<OffsetDateTime> second) {
return DateUtils.compareWithNullsLast(first.orElse(null), second.orElse(null));
}

public static int compareWithNullsLast(OffsetDateTime first, OffsetDateTime second) {
return first == null ? (second == null ? 0 : 1) : (second == null ? -1 : first.compareTo(second));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ public static BigDecimal abs(BigDecimal value) {
* if true then null parameter is omitted, otherwise returns null
*/
public static BigDecimal min(BigDecimal first, BigDecimal second, boolean notNull) {
return notNull ? first == null ? second : second == null ? first : min(first, second, false)
return notNull ? (first == null ? second : (second == null ? first : min(first, second, false)))
: isLessThan(first, second) ? first : second;
}

Expand Down Expand Up @@ -291,6 +291,11 @@ public static BigDecimal subtract(BigDecimal first, BigDecimal second) {
return subtract(first, second, MoneyHelper.getMathContext());
}

/** @return first minus the others considering null values, maybe negative */
public static BigDecimal subtract(BigDecimal first, BigDecimal second, BigDecimal third) {
return subtract(subtract(first, second), third);
}

/** @return first minus second considering null values, maybe negative */
public static BigDecimal subtract(BigDecimal first, BigDecimal second, MathContext mc) {
return first == null ? null : second == null ? first : first.subtract(second, mc);
Expand Down Expand Up @@ -336,6 +341,10 @@ public static String formatToSql(BigDecimal amount) {
return amount == null ? null : amount.toPlainString();
}

public static Money toMoney(BigDecimal amount, @NotNull MonetaryCurrency currency) {
return amount == null ? null : Money.of(currency, amount);
}

// ----------------- Money -----------------

public static BigDecimal toBigDecimal(Money value) {
Expand Down Expand Up @@ -454,6 +463,16 @@ public static Money min(Money first, Money second, Money third, boolean notNull)
return min(min(first, second, notNull), third, notNull);
}

/** @return Money null safe negate */
public static Money negate(Money amount) {
return negate(amount, MoneyHelper.getMathContext());
}

/** @return Money null safe negate */
public static Money negate(Money amount, MathContext mc) {
return isEmpty(amount) ? amount : amount.negated(mc);
}

/**
* Calculate percentage of a value
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -447,47 +447,75 @@ public static String idempotencyKeyNoMatch(String actual, String 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);
return String.format("Number of lines in Repayment schedule is not correct. Actual value is: %s - Expected value is: %s", actualStr,
expectedStr);
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);
}

public static String wrongValueInLineInRepaymentSchedule(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());
}

return String.format(
"%nWrong value in Repayment schedule tab line %s. %nActual values in line (with the same due date) are: %n%s %nExpected values in line: %n%s",
lineStr, sb.toString(), expectedStr);
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());
}

return String.format(
"%nWrong value in Transactions tab line %s. %nActual values in line (with the same date) are: %n%s %nExpected values in line: %n%s",
lineStr, sb.toString(), expectedStr);
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);
}

public static String nrOfLinesWrongInTransactionsTab(int actual, int expected) {
return nrOfLinesWrongInTransactionsTab(null, actual, expected);
}

public static String nrOfLinesWrongInTransactionsTab(String resourceId, int actual, int expected) {
String actualStr = String.valueOf(actual);
String expectedStr = String.valueOf(expected);

return String.format(
"%nNumber of lines does not match in Transactions tab and expected datatable. %nNumber of transaction tab lines: %s %nNumber of expected datatable lines: %s%n",
actualStr, expectedStr);
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);
}

public static String wrongValueInLineInChargesTab(int line, List<List<String>> actual, List<String> expected) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1613,8 +1613,10 @@ public void loanLastPaymentAmount(double lastPaymentAmountExpected) throws IOExc

@Then("Loan Repayment schedule has {int} periods, with the following data for periods:")
public void loanRepaymentSchedulePeriodsCheck(int linesExpected, 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, "repaymentSchedule", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
Expand All @@ -1634,9 +1636,9 @@ public void loanRepaymentSchedulePeriodsCheck(int linesExpected, DataTable table

boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValues));
assertThat(containsExpectedValues)
.as(ErrorMessageHelper.wrongValueInLineInRepaymentSchedule(i, actualValuesList, expectedValues)).isTrue();
.as(ErrorMessageHelper.wrongValueInLineInRepaymentSchedule(resourceId, i, actualValuesList, expectedValues)).isTrue();

assertThat(linesActual).as(ErrorMessageHelper.wrongNumberOfLinesInRepaymentSchedule(linesActual, linesExpected))
assertThat(linesActual).as(ErrorMessageHelper.wrongNumberOfLinesInRepaymentSchedule(resourceId, linesActual, linesExpected))
.isEqualTo(linesExpected);
}
}
Expand All @@ -1660,6 +1662,7 @@ public void loanRepaymentScheduleAmountCheck(DataTable table) throws IOException
public void loanTransactionsTransactionWithGivenDateDataCheck(String date, 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, "transactions", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
Expand All @@ -1673,14 +1676,16 @@ public void loanTransactionsTransactionWithGivenDateDataCheck(String date, DataT
.map(t -> fetchValuesOfTransaction(data.get(0), t)).collect(Collectors.toList());
boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValues));

assertThat(containsExpectedValues).as(ErrorMessageHelper.wrongValueInLineInTransactionsTab(1, actualValuesList, expectedValues))
.isTrue();
assertThat(containsExpectedValues)
.as(ErrorMessageHelper.wrongValueInLineInTransactionsTab(resourceId, 1, actualValuesList, expectedValues)).isTrue();
}

@Then("Loan Transactions tab has the following data:")
public void loanTransactionsTabCheck(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, "transactions", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions();
Expand All @@ -1694,10 +1699,11 @@ public void loanTransactionsTabCheck(DataTable table) throws IOException {
.collect(Collectors.toList());//
boolean containsExpectedValues = actualValuesList.stream()//
.anyMatch(actualValues -> actualValues.equals(expectedValues));//
assertThat(containsExpectedValues).as(ErrorMessageHelper.wrongValueInLineInTransactionsTab(i, actualValuesList, expectedValues))
.isTrue();
assertThat(containsExpectedValues)
.as(ErrorMessageHelper.wrongValueInLineInTransactionsTab(resourceId, i, actualValuesList, expectedValues)).isTrue();
}
assertThat(transactions.size()).as(ErrorMessageHelper.nrOfLinesWrongInTransactionsTab(transactions.size(), data.size() - 1))
assertThat(transactions.size())
.as(ErrorMessageHelper.nrOfLinesWrongInTransactionsTab(resourceId, transactions.size(), data.size() - 1))
.isEqualTo(data.size() - 1);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@
@RunWith(Cucumber.class)
@CucumberOptions(features = "src/test/resources/features", glue = { "org.apache.fineract.test.stepdef",
"org.apache.fineract.test.stepdef.common", "org.apache.fineract.test.stepdef.hook", "org.apache.fineract.test.stepdef.loan",
"org.apache.fineract.test.stepdef.saving", "org.apache.fineract.test.config" })
"org.apache.fineract.test.stepdef.saving", "org.apache.fineract.test.config" }, tags = "not @Skip")
public class TestRunner {}
Loading

0 comments on commit c5c6955

Please sign in to comment.