Skip to content

Commit ad90d98

Browse files
mariiaKraievskaadamsaghy
authored andcommitted
FINERACT-2398: Re-amortization:- Preview Schedule API during re-amortization
1 parent c70edf6 commit ad90d98

File tree

10 files changed

+738
-29
lines changed

10 files changed

+738
-29
lines changed

fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanReAgingStepDef.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ public void loanRepaymentSchedulePreviewPeriodsCheck(int linesExpected, DataTabl
413413
}
414414
}
415415

416-
@Then("Loan Repayment schedule preview has the following data in Total row:")
416+
@Then("Loan Re-Aged Repayment schedule preview has the following data in Total row:")
417417
public void loanRepaymentScheduleAmountCheck(DataTable table) {
418418
List<List<String>> data = table.asLists();
419419
List<String> header = data.get(0);

fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanReAmortizationStepDef.java

Lines changed: 172 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,28 @@
1919
package org.apache.fineract.test.stepdef.loan;
2020

2121
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
22+
import static org.assertj.core.api.Assertions.assertThat;
2223

24+
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
25+
import io.cucumber.datatable.DataTable;
2326
import io.cucumber.java.en.Then;
2427
import io.cucumber.java.en.When;
25-
import java.io.IOException;
28+
import java.math.BigDecimal;
29+
import java.math.RoundingMode;
30+
import java.time.format.DateTimeFormatter;
31+
import java.util.ArrayList;
32+
import java.util.List;
2633
import java.util.Map;
34+
import java.util.stream.Collectors;
2735
import org.apache.fineract.client.feign.FineractFeignClient;
36+
import org.apache.fineract.client.models.LoanScheduleData;
37+
import org.apache.fineract.client.models.LoanSchedulePeriodData;
2838
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
2939
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
3040
import org.apache.fineract.client.models.PostLoansResponse;
3141
import org.apache.fineract.test.factory.LoanRequestFactory;
42+
import org.apache.fineract.test.helper.ErrorMessageHelper;
43+
import org.apache.fineract.test.helper.Utils;
3244
import org.apache.fineract.test.messaging.EventAssertion;
3345
import org.apache.fineract.test.messaging.event.loan.LoanReAmortizeEvent;
3446
import org.apache.fineract.test.stepdef.AbstractStepDef;
@@ -37,14 +49,17 @@
3749

3850
public class LoanReAmortizationStepDef extends AbstractStepDef {
3951

52+
private static final String DATE_FORMAT = "dd MMMM yyyy";
53+
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT);
54+
4055
@Autowired
4156
private FineractFeignClient fineractClient;
4257

4358
@Autowired
4459
private EventAssertion eventAssertion;
4560

4661
@When("When Admin creates a Loan re-amortization transaction on current business date")
47-
public void createLoanReAmortization() throws IOException {
62+
public void createLoanReAmortization() {
4863
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
4964
long loanId = loanResponse.getLoanId();
5065

@@ -56,7 +71,7 @@ public void createLoanReAmortization() throws IOException {
5671
}
5772

5873
@When("When Admin creates a Loan re-amortization transaction on current business date by loan external ID")
59-
public void createLoanReAmortizationByLoanExternalId() throws IOException {
74+
public void createLoanReAmortizationByLoanExternalId() {
6075
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
6176
String loanExternalId = loanResponse.getResourceExternalId();
6277

@@ -68,7 +83,7 @@ public void createLoanReAmortizationByLoanExternalId() throws IOException {
6883
}
6984

7085
@When("When Admin undo Loan re-amortization transaction on current business date")
71-
public void undoLoanReAmortization() throws IOException {
86+
public void undoLoanReAmortization() {
7287
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
7388
long loanId = loanResponse.getLoanId();
7489

@@ -84,4 +99,157 @@ public void checkLoanReAmortizeBusinessEventCreated() {
8499

85100
eventAssertion.assertEventRaised(LoanReAmortizeEvent.class, loanId);
86101
}
102+
103+
@When("Admin creates a Loan re-amortization preview by Loan external ID with the following data:")
104+
public void createReAmortizedPreviewByLoanExternalId(final DataTable table) {
105+
final LoanScheduleData response = reAmortizedPreviewByLoanExternalId(table);
106+
testContext().set(TestContextKey.LOAN_REAMORTIZATION_PREVIEW_RESPONSE, response);
107+
}
108+
109+
@Then("Loan Re-Amortization Repayment schedule preview has the following data in Total row:")
110+
public void loanRepaymentDScheduleAmountCheck(final DataTable table) {
111+
final List<List<String>> data = table.asLists();
112+
final List<String> header = data.get(0);
113+
final List<String> expectedValues = data.get(1);
114+
final LoanScheduleData scheduleResponse = testContext().get(TestContextKey.LOAN_REAMORTIZATION_PREVIEW_RESPONSE);
115+
validateRepaymentScheduleTotal(header, scheduleResponse, expectedValues);
116+
}
117+
118+
@Then("Loan Re-Amortization Repayment schedule preview has {int} periods, with the following data for periods:")
119+
public void loanRepaymentSchedulePreviewPeriodsCheck(final int linesExpected, final DataTable table) {
120+
final LoanScheduleData scheduleResponse = testContext().get(TestContextKey.LOAN_REAMORTIZATION_PREVIEW_RESPONSE);
121+
final List<LoanSchedulePeriodData> repaymentPeriods = scheduleResponse.getPeriods();
122+
123+
final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
124+
final String resourceId = String.valueOf(loanResponse.getLoanId());
125+
final List<List<String>> data = table.asLists();
126+
final int nrLines = data.size();
127+
final int linesActual = (int) repaymentPeriods.stream().filter(r -> r.getPeriod() != null).count();
128+
129+
for (int i = 1; i < nrLines; i++) {
130+
List<String> expectedValues = data.get(i);
131+
String dueDateExpected = expectedValues.get(2);
132+
133+
List<List<String>> actualValuesList = repaymentPeriods.stream()
134+
.filter(r -> dueDateExpected.equals(FORMATTER.format(r.getDueDate())))
135+
.map(r -> fetchValuesOfRepaymentSchedule(data.get(0), r)).collect(Collectors.toList());
136+
137+
boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValues));
138+
assertThat(containsExpectedValues)
139+
.as(ErrorMessageHelper.wrongValueInLineInRepaymentSchedule(resourceId, i, actualValuesList, expectedValues)).isTrue();
140+
141+
assertThat(linesActual).as(ErrorMessageHelper.wrongNumberOfLinesInRepaymentSchedule(resourceId, linesActual, linesExpected))
142+
.isEqualTo(linesExpected);
143+
}
144+
}
145+
146+
private LoanScheduleData reAmortizedPreviewByLoanExternalId(final DataTable table) {
147+
final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
148+
final String loanExternalId = loanResponse.getResourceExternalId();
149+
150+
final List<String> data = table.asLists().get(1);
151+
final String reAmortizationInterestHandling = data.getFirst();
152+
153+
final Map<String, Object> queryParams = Map.of("reAmortizationInterestHandling", reAmortizationInterestHandling);
154+
return ok(() -> fineractClient.loanTransactions().previewReAmortizationSchedule1(loanExternalId, queryParams));
155+
}
156+
157+
@SuppressFBWarnings("SF_SWITCH_NO_DEFAULT")
158+
private void validateRepaymentScheduleTotal(final List<String> header, final LoanScheduleData repaymentSchedule,
159+
final List<String> expectedAmounts) {
160+
Double paidActual = 0.0;
161+
final List<LoanSchedulePeriodData> periods = repaymentSchedule.getPeriods();
162+
for (LoanSchedulePeriodData period : periods) {
163+
if (null != period.getTotalPaidForPeriod()) {
164+
paidActual += period.getTotalPaidForPeriod().doubleValue();
165+
}
166+
}
167+
final BigDecimal paidActualBd = new BigDecimal(paidActual).setScale(2, RoundingMode.HALF_DOWN);
168+
169+
for (int i = 0; i < header.size(); i++) {
170+
final String headerName = header.get(i);
171+
final String expectedValue = expectedAmounts.get(i);
172+
switch (headerName) {
173+
case "Principal due" -> assertThat(repaymentSchedule.getTotalPrincipalExpected())//
174+
.as(ErrorMessageHelper.wrongAmountInRepaymentSchedulePrincipal(
175+
repaymentSchedule.getTotalPrincipalExpected().doubleValue(), Double.valueOf(expectedValue)))//
176+
.isEqualByComparingTo(new BigDecimal(expectedValue));//
177+
case "Interest" -> assertThat(repaymentSchedule.getTotalInterestCharged())//
178+
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleInterest(
179+
repaymentSchedule.getTotalInterestCharged().doubleValue(), Double.valueOf(expectedValue)))//
180+
.isEqualByComparingTo(new BigDecimal(expectedValue));//
181+
case "Fees" -> assertThat(repaymentSchedule.getTotalFeeChargesCharged())//
182+
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleFees(
183+
repaymentSchedule.getTotalFeeChargesCharged().doubleValue(), Double.valueOf(expectedValue)))//
184+
.isEqualByComparingTo(new BigDecimal(expectedValue));//
185+
case "Penalties" -> assertThat(repaymentSchedule.getTotalPenaltyChargesCharged())//
186+
.as(ErrorMessageHelper.wrongAmountInRepaymentSchedulePenalties(
187+
repaymentSchedule.getTotalPenaltyChargesCharged().doubleValue(), Double.valueOf(expectedValue)))//
188+
.isEqualByComparingTo(new BigDecimal(expectedValue));//
189+
case "Due" -> assertThat(repaymentSchedule.getTotalRepaymentExpected())//
190+
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleDue(
191+
repaymentSchedule.getTotalRepaymentExpected().doubleValue(), Double.valueOf(expectedValue)))//
192+
.isEqualByComparingTo(new BigDecimal(expectedValue));//
193+
case "Paid" -> assertThat(paidActualBd)//
194+
.as(ErrorMessageHelper.wrongAmountInRepaymentSchedulePaid(paidActualBd.doubleValue(),
195+
Double.valueOf(expectedValue)))//
196+
.isEqualByComparingTo(new BigDecimal(expectedValue));//
197+
case "In advance" -> assertThat(repaymentSchedule.getTotalPaidInAdvance())//
198+
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleInAdvance(
199+
repaymentSchedule.getTotalPaidInAdvance().doubleValue(), Double.valueOf(expectedValue)))//
200+
.isEqualByComparingTo(new BigDecimal(expectedValue));//
201+
case "Late" -> assertThat(repaymentSchedule.getTotalPaidLate())//
202+
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleLate(repaymentSchedule.getTotalPaidLate().doubleValue(),
203+
Double.valueOf(expectedValue)))//
204+
.isEqualByComparingTo(new BigDecimal(expectedValue));//
205+
case "Waived" -> assertThat(repaymentSchedule.getTotalWaived())//
206+
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleWaived(repaymentSchedule.getTotalWaived().doubleValue(),
207+
Double.valueOf(expectedValue)))//
208+
.isEqualByComparingTo(new BigDecimal(expectedValue));//
209+
case "Outstanding" -> assertThat(repaymentSchedule.getTotalOutstanding())//
210+
.as(ErrorMessageHelper.wrongAmountInRepaymentScheduleOutstanding(
211+
repaymentSchedule.getTotalOutstanding().doubleValue(), Double.valueOf(expectedValue)))//
212+
.isEqualByComparingTo(new BigDecimal(expectedValue));//
213+
}
214+
}
215+
}
216+
217+
private List<String> fetchValuesOfRepaymentSchedule(final List<String> header, final LoanSchedulePeriodData repaymentPeriod) {
218+
final List<String> actualValues = new ArrayList<>();
219+
for (String headerName : header) {
220+
switch (headerName) {
221+
case "Nr" -> actualValues.add(repaymentPeriod.getPeriod() == null ? null : String.valueOf(repaymentPeriod.getPeriod()));
222+
case "Days" ->
223+
actualValues.add(repaymentPeriod.getDaysInPeriod() == null ? null : String.valueOf(repaymentPeriod.getDaysInPeriod()));
224+
case "Date" ->
225+
actualValues.add(repaymentPeriod.getDueDate() == null ? null : FORMATTER.format(repaymentPeriod.getDueDate()));
226+
case "Paid date" -> actualValues.add(repaymentPeriod.getObligationsMetOnDate() == null ? null
227+
: FORMATTER.format(repaymentPeriod.getObligationsMetOnDate()));
228+
case "Balance of loan" -> actualValues.add(repaymentPeriod.getPrincipalLoanBalanceOutstanding() == null ? null
229+
: new Utils.DoubleFormatter(repaymentPeriod.getPrincipalLoanBalanceOutstanding().doubleValue()).format());
230+
case "Principal due" -> actualValues.add(repaymentPeriod.getPrincipalDue() == null ? null
231+
: new Utils.DoubleFormatter(repaymentPeriod.getPrincipalDue().doubleValue()).format());
232+
case "Interest" -> actualValues.add(repaymentPeriod.getInterestDue() == null ? null
233+
: new Utils.DoubleFormatter(repaymentPeriod.getInterestDue().doubleValue()).format());
234+
case "Fees" -> actualValues.add(repaymentPeriod.getFeeChargesDue() == null ? null
235+
: new Utils.DoubleFormatter(repaymentPeriod.getFeeChargesDue().doubleValue()).format());
236+
case "Penalties" -> actualValues.add(repaymentPeriod.getPenaltyChargesDue() == null ? null
237+
: new Utils.DoubleFormatter(repaymentPeriod.getPenaltyChargesDue().doubleValue()).format());
238+
case "Due" -> actualValues.add(repaymentPeriod.getTotalDueForPeriod() == null ? null
239+
: new Utils.DoubleFormatter(repaymentPeriod.getTotalDueForPeriod().doubleValue()).format());
240+
case "Paid" -> actualValues.add(repaymentPeriod.getTotalPaidForPeriod() == null ? null
241+
: new Utils.DoubleFormatter(repaymentPeriod.getTotalPaidForPeriod().doubleValue()).format());
242+
case "In advance" -> actualValues.add(repaymentPeriod.getTotalPaidInAdvanceForPeriod() == null ? null
243+
: new Utils.DoubleFormatter(repaymentPeriod.getTotalPaidInAdvanceForPeriod().doubleValue()).format());
244+
case "Late" -> actualValues.add(repaymentPeriod.getTotalPaidLateForPeriod() == null ? null
245+
: new Utils.DoubleFormatter(repaymentPeriod.getTotalPaidLateForPeriod().doubleValue()).format());
246+
case "Waived" -> actualValues.add(repaymentPeriod.getTotalWaivedForPeriod() == null ? null
247+
: new Utils.DoubleFormatter(repaymentPeriod.getTotalWaivedForPeriod().doubleValue()).format());
248+
case "Outstanding" -> actualValues.add(repaymentPeriod.getTotalOutstandingForPeriod() == null ? null
249+
: new Utils.DoubleFormatter(repaymentPeriod.getTotalOutstandingForPeriod().doubleValue()).format());
250+
default -> throw new IllegalStateException(String.format("Header name %s cannot be found", headerName));
251+
}
252+
}
253+
return actualValues;
254+
}
87255
}

fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public abstract class TestContextKey {
5252
public static final String LOAN_REAGING_UNDO_RESPONSE = "loanReAgingUndoResponse";
5353
public static final String LOAN_REAGING_PREVIEW_RESPONSE = "loanReAgingPreviewResponse";
5454
public static final String LOAN_REAMORTIZATION_RESPONSE = "loanReAmortizationResponse";
55+
public static final String LOAN_REAMORTIZATION_PREVIEW_RESPONSE = "loanReAmortizationPreviewResponse";
5556
public static final String LOAN_REAMORTIZATION_UNDO_RESPONSE = "loanReAmortizationUndoResponse";
5657
public static final String BUSINESS_DATE_RESPONSE = "businessDateResponse";
5758
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1 = "loanProductCreateResponseLP1";

0 commit comments

Comments
 (0)