FINERACT-1971: added test scenarios to cover repayment schedule with large charge amounts

This commit is contained in:
Rustam Zeinalov
2025-02-27 16:37:29 +01:00
committed by Adam Saghy
parent c1ec3d7fc7
commit e1165e813a
4 changed files with 127 additions and 14 deletions
@@ -1,6 +1,6 @@
{ {
"logicalType": "decimal", "logicalType": "decimal",
"precision": 20, "precision": 27,
"scale": 8, "scale": 8,
"type": "bytes" "type": "bytes"
} }
@@ -48,4 +48,26 @@ public final class Utils {
public static LocalDate now() { public static LocalDate now() {
return LocalDate.now(Clock.systemUTC()); return LocalDate.now(Clock.systemUTC());
} }
/**
* A record that formats a double value based on whether it's a whole number or not.
* <p>
* If the value is a whole number, the output will have one decimal place (e.g., 16.0). Otherwise, it will have two
* decimal places (e.g., 16.90), but if the second decimal place is zero, it will be removed (so 16.90 becomes
* 16.9).
*/
public record DoubleFormatter(double value) {
public String format() {
boolean isWholeNumber = (value % 1.0 == 0);
String result = isWholeNumber ? String.format("%.1f", value) : String.format("%.2f", value);
// For non-whole numbers, remove trailing '0' if it exists
if (!isWholeNumber && result.endsWith("0")) {
result = result.substring(0, result.length() - 1);
}
return result;
}
}
} }
@@ -1989,7 +1989,6 @@ public class LoanStepDef extends AbstractStepDef {
private List<List<String>> getActualValuesList(List<GetLoansLoanIdLoanChargeData> charges, String paymentDueAtExpected, private List<List<String>> getActualValuesList(List<GetLoansLoanIdLoanChargeData> charges, String paymentDueAtExpected,
String dueAsOfExpected) { String dueAsOfExpected) {
List<GetLoansLoanIdLoanChargeData> result; List<GetLoansLoanIdLoanChargeData> result;
if (dueAsOfExpected != null) { if (dueAsOfExpected != null) {
result = charges.stream().filter(t -> { result = charges.stream().filter(t -> {
LocalDate dueDate = t.getDueDate(); LocalDate dueDate = t.getDueDate();
@@ -1999,7 +1998,6 @@ public class LoanStepDef extends AbstractStepDef {
result = charges.stream().filter(t -> paymentDueAtExpected.equals(t.getChargeTimeType().getValue())) result = charges.stream().filter(t -> paymentDueAtExpected.equals(t.getChargeTimeType().getValue()))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
return result.stream().map(t -> { return result.stream().map(t -> {
List<String> actualValues = new ArrayList<>(); List<String> actualValues = new ArrayList<>();
actualValues.add(t.getName() == null ? null : t.getName()); actualValues.add(t.getName() == null ? null : t.getName());
@@ -2007,10 +2005,13 @@ public class LoanStepDef extends AbstractStepDef {
actualValues.add(t.getChargeTimeType().getValue() == null ? null : t.getChargeTimeType().getValue()); actualValues.add(t.getChargeTimeType().getValue() == null ? null : t.getChargeTimeType().getValue());
actualValues.add(t.getDueDate() == null ? null : FORMATTER.format(t.getDueDate())); actualValues.add(t.getDueDate() == null ? null : FORMATTER.format(t.getDueDate()));
actualValues.add(t.getChargeCalculationType().getValue() == null ? null : t.getChargeCalculationType().getValue()); actualValues.add(t.getChargeCalculationType().getValue() == null ? null : t.getChargeCalculationType().getValue());
actualValues.add(t.getAmount() == null ? null : String.valueOf(t.getAmount()));
actualValues.add(t.getAmountPaid() == null ? null : String.valueOf(t.getAmountPaid())); actualValues.add(t.getAmount() == null ? null : new Utils.DoubleFormatter(t.getAmount()).format());
actualValues.add(t.getAmountWaived() == null ? null : String.valueOf(t.getAmountWaived()));
actualValues.add(t.getAmountOutstanding() == null ? null : String.valueOf(t.getAmountOutstanding())); actualValues.add(t.getAmountPaid() == null ? null : new Utils.DoubleFormatter(t.getAmountPaid()).format());
actualValues.add(t.getAmountWaived() == null ? null : new Utils.DoubleFormatter(t.getAmountWaived()).format());
actualValues.add(t.getAmountOutstanding() == null ? null : new Utils.DoubleFormatter(t.getAmountOutstanding()).format());
return actualValues; return actualValues;
}).collect(Collectors.toList()); }).collect(Collectors.toList());
} }
@@ -3203,12 +3204,12 @@ public class LoanStepDef extends AbstractStepDef {
actualValues.add(repaymentPeriod.getPrincipalDue() == null ? null : String.valueOf(repaymentPeriod.getPrincipalDue())); actualValues.add(repaymentPeriod.getPrincipalDue() == null ? null : String.valueOf(repaymentPeriod.getPrincipalDue()));
case "Interest" -> case "Interest" ->
actualValues.add(repaymentPeriod.getInterestDue() == null ? null : String.valueOf(repaymentPeriod.getInterestDue())); actualValues.add(repaymentPeriod.getInterestDue() == null ? null : String.valueOf(repaymentPeriod.getInterestDue()));
case "Fees" -> actualValues case "Fees" -> actualValues.add(repaymentPeriod.getFeeChargesDue() == null ? null
.add(repaymentPeriod.getFeeChargesDue() == null ? null : String.valueOf(repaymentPeriod.getFeeChargesDue())); : new Utils.DoubleFormatter(repaymentPeriod.getFeeChargesDue()).format());
case "Penalties" -> actualValues.add( case "Penalties" -> actualValues.add(repaymentPeriod.getPenaltyChargesDue() == null ? null
repaymentPeriod.getPenaltyChargesDue() == null ? null : String.valueOf(repaymentPeriod.getPenaltyChargesDue())); : new Utils.DoubleFormatter(repaymentPeriod.getPenaltyChargesDue()).format());
case "Due" -> actualValues.add( case "Due" -> actualValues.add(repaymentPeriod.getTotalDueForPeriod() == null ? null
repaymentPeriod.getTotalDueForPeriod() == null ? null : String.valueOf(repaymentPeriod.getTotalDueForPeriod())); : new Utils.DoubleFormatter(repaymentPeriod.getTotalDueForPeriod()).format());
case "Paid" -> actualValues.add( case "Paid" -> actualValues.add(
repaymentPeriod.getTotalPaidForPeriod() == null ? null : String.valueOf(repaymentPeriod.getTotalPaidForPeriod())); repaymentPeriod.getTotalPaidForPeriod() == null ? null : String.valueOf(repaymentPeriod.getTotalPaidForPeriod()));
case "In advance" -> actualValues.add(repaymentPeriod.getTotalPaidInAdvanceForPeriod() == null ? null case "In advance" -> actualValues.add(repaymentPeriod.getTotalPaidInAdvanceForPeriod() == null ? null
@@ -3218,7 +3219,7 @@ public class LoanStepDef extends AbstractStepDef {
case "Waived" -> actualValues.add(repaymentPeriod.getTotalWaivedForPeriod() == null ? null case "Waived" -> actualValues.add(repaymentPeriod.getTotalWaivedForPeriod() == null ? null
: String.valueOf(repaymentPeriod.getTotalWaivedForPeriod())); : String.valueOf(repaymentPeriod.getTotalWaivedForPeriod()));
case "Outstanding" -> actualValues.add(repaymentPeriod.getTotalOutstandingForPeriod() == null ? null case "Outstanding" -> actualValues.add(repaymentPeriod.getTotalOutstandingForPeriod() == null ? null
: String.valueOf(repaymentPeriod.getTotalOutstandingForPeriod())); : new Utils.DoubleFormatter(repaymentPeriod.getTotalOutstandingForPeriod()).format());
default -> throw new IllegalStateException(String.format("Header name %s cannot be found", headerName)); default -> throw new IllegalStateException(String.format("Header name %s cannot be found", headerName));
} }
} }
@@ -2534,3 +2534,93 @@ Feature: LoanCharge
| 01 January 2024 | Repayment | 60.0 | 60.0 | 0.0 | 0.0 | 0.0 | 40.0 | | 01 January 2024 | Repayment | 60.0 | 60.0 | 0.0 | 0.0 | 0.0 | 40.0 |
| 01 February 2024 | Charge Adjustment | 20.0 | 0.0 | 0.0 | 0.0 | 20.0 | 40.0 | | 01 February 2024 | Charge Adjustment | 20.0 | 0.0 | 0.0 | 0.0 | 20.0 | 40.0 |
And LoanChargeAdjustmentPostBusinessEvent is raised on "01 February 2024" And LoanChargeAdjustmentPostBusinessEvent is raised on "01 February 2024"
@TestRailId:C3501
Scenario: Verify repayment schedule amounts after large charge amount added - UC1
When Admin sets the business date to "20 February 2025"
And Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 20 December 2024 | 800 | 0 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 4 | MONTHS | 1 | MONTHS | 4 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "20 December 2024" with "800" amount and expected disbursement date on "20 December 2024"
When Admin successfully disburse the loan on "20 December 2024" with "800" EUR transaction amount
Then Loan Repayment schedule has 4 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 |
| | | 20 December 2024 | | 800.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 20 January 2025 | | 600.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 |
| 2 | 31 | 20 February 2025 | | 400.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 |
| 3 | 28 | 20 March 2025 | | 200.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 |
| 4 | 31 | 20 April 2025 | | 0.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 |
And Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 800.0 | 0.0 | 0.0 | 0.0 | 800.0 | 0.0 | 0.0 | 0.0 | 800.0 |
And Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 20 December 2024 | Disbursement | 800.0 | 0.0 | 0.0 | 0.0 | 0.0 | 800.0 | false | false |
And Admin adds "LOAN_NSF_FEE" due date charge with "25 December 2024" due date and 123456789012.12 EUR transaction amount
Then Loan Repayment schedule has 4 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 |
| | | 20 December 2024 | | 800.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 20 January 2025 | | 600.0 | 200.0 | 0.0 | 0.0 | 123456789012.12 | 123456789212.12 | 0.0 | 0.0 | 0.0 | 123456789212.12 |
| 2 | 31 | 20 February 2025 | | 400.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 |
| 3 | 28 | 20 March 2025 | | 200.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 |
| 4 | 31 | 20 April 2025 | | 0.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 |
And Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 800.0 | 0.0 | 0.0 | 123456789012.12 | 123456789812.12 | 0.0 | 0.0 | 0.0 | 123456789812.12 |
And Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 20 December 2024 | Disbursement | 800.0 | 0.0 | 0.0 | 0.0 | 0.0 | 800.0 | false | false |
Then Loan Charges tab has a given charge with the following data:
| Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding |
| NSF fee | true | Specified due date | 25 December 2024 | Flat | 123456789012.12 | 0.0 | 0.0 | 123456789012.12 |
@TestRailId:C3502
Scenario: Verify repayment schedule amounts after a few large charges amount added - UC2
When Admin sets the business date to "20 February 2025"
And Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 20 December 2024 | 800 | 0 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 4 | MONTHS | 1 | MONTHS | 4 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "20 December 2024" with "800" amount and expected disbursement date on "20 December 2024"
When Admin successfully disburse the loan on "20 December 2024" with "800" EUR transaction amount
Then Loan Repayment schedule has 4 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 |
| | | 20 December 2024 | | 800.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 20 January 2025 | | 600.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 |
| 2 | 31 | 20 February 2025 | | 400.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 |
| 3 | 28 | 20 March 2025 | | 200.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 |
| 4 | 31 | 20 April 2025 | | 0.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 | 0.0 | 0.0 | 0.0 | 200.0 |
And Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 800.0 | 0.0 | 0.0 | 0.0 | 800.0 | 0.0 | 0.0 | 0.0 | 800.0 |
And Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 20 December 2024 | Disbursement | 800.0 | 0.0 | 0.0 | 0.0 | 0.0 | 800.0 | false | false |
And Admin adds "LOAN_NSF_FEE" due date charge with "25 December 2024" due date and 123456789012.12 EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "28 December 2024" due date and 1003456789012.12 EUR transaction amount
When Admin adds "LOAN_NSF_FEE" due date charge with "31 January 2025" due date and 5503456789012.12 EUR transaction amount
When Admin adds "LOAN_NSF_FEE" due date charge with "23 February 2025" due date and 1003456789012.12 EUR transaction amount
When Admin adds "LOAN_NSF_FEE" due date charge with "03 April 2025" due date and 1503456789012.12 EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "09 April 2025" due date and 103456789037.12 EUR transaction amount
Then Loan Repayment schedule has 4 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 |
| | | 20 December 2024 | | 800.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 20 January 2025 | | 600.0 | 200.0 | 0.0 | 1003456789012.12 | 123456789012.12 | 1126913578224.24 | 0.0 | 0.0 | 0.0 | 1126913578224.24 |
| 2 | 31 | 20 February 2025 | | 400.0 | 200.0 | 0.0 | 0.0 | 5503456789012.12 | 5503456789212.12 | 0.0 | 0.0 | 0.0 | 5503456789212.12 |
| 3 | 28 | 20 March 2025 | | 200.0 | 200.0 | 0.0 | 0.0 | 1003456789012.12 | 1003456789212.12 | 0.0 | 0.0 | 0.0 | 1003456789212.12 |
| 4 | 31 | 20 April 2025 | | 0.0 | 200.0 | 0.0 | 103456789037.12 | 1503456789012.12 | 1606913578249.24 | 0.0 | 0.0 | 0.0 | 1606913578249.24 |
And Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 800.0 | 0.0 | 1106913578049.24 | 8133827156048.48 | 9240740734897.72 | 0.0 | 0.0 | 0.0 | 9240740734897.72 |
And Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 20 December 2024 | Disbursement | 800.0 | 0.0 | 0.0 | 0.0 | 0.0 | 800.0 | false | false |
Then Loan Charges tab has a given charge with the following data:
| Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding |
| NSF fee | true | Specified due date | 25 December 2024 | Flat | 123456789012.12 | 0.0 | 0.0 | 123456789012.12 |
| Snooze fee | false | Specified due date | 28 December 2024 | Flat | 1003456789012.12 | 0.0 | 0.0 | 1003456789012.12 |
| NSF fee | true | Specified due date | 31 January 2025 | Flat | 5503456789012.12 | 0.0 | 0.0 | 5503456789012.12 |
| NSF fee | true | Specified due date | 23 February 2025 | Flat | 1003456789012.12 | 0.0 | 0.0 | 1003456789012.12 |
| NSF fee | true | Specified due date | 03 April 2025 | Flat | 1503456789012.12 | 0.0 | 0.0 | 1503456789012.12 |
| Snooze fee | false | Specified due date | 09 April 2025 | Flat | 103456789037.12 | 0.0 | 0.0 | 103456789037.12 |