diff --git a/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java b/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java index 2769591c3b8..524d42a8bd5 100644 --- a/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java +++ b/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java @@ -922,6 +922,14 @@ public CommandWrapperBuilder creditBalanceRefundWorkingCapitalLoanTransaction(fi return this; } + public CommandWrapperBuilder updatePeriodPaymentRateWorkingCapitalLoanApplication(final Long loanId) { + this.actionName = "UPDATERATE"; + this.entityName = "WORKINGCAPITALLOAN"; + this.entityId = loanId; + this.href = "/workingcapitalloans/" + loanId; + return this; + } + public CommandWrapperBuilder createClientIdentifier(final Long clientId) { this.actionName = ACTION_CREATE; this.entityName = ENTITY_CLIENTIDENTIFIER; diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/workingcapitalproduct/DefaultWorkingCapitalLoanProduct.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/workingcapitalproduct/DefaultWorkingCapitalLoanProduct.java index 89fbebfb829..073a100561c 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/workingcapitalproduct/DefaultWorkingCapitalLoanProduct.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/workingcapitalproduct/DefaultWorkingCapitalLoanProduct.java @@ -29,7 +29,8 @@ public enum DefaultWorkingCapitalLoanProduct implements WorkingCapitalLoanProduc WCLP_BREACH, // WCLP_BREACH_NEAR_BREACH, // WCLP_BREACH_DISALLOW_ATTRIBUTES_OVERRIDE, // - WCLP_BREACH_NEAR_BREACH_DISALLOW_ATTRIBUTES_OVERRIDE; // + WCLP_BREACH_NEAR_BREACH_DISALLOW_ATTRIBUTES_OVERRIDE, // + WCLP_PERIOD_PAYMENT_RATE; // @Override public String getName() { diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/WorkingCapitalLoanRequestFactory.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/WorkingCapitalLoanRequestFactory.java index ed6bf6e7e16..4530254080a 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/WorkingCapitalLoanRequestFactory.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/WorkingCapitalLoanRequestFactory.java @@ -24,6 +24,8 @@ import org.apache.fineract.client.models.PostWorkingCapitalLoansDelinquencyActionRequest; import org.apache.fineract.client.models.PostWorkingCapitalLoansLoanIdRequest; import org.apache.fineract.client.models.PostWorkingCapitalLoansRequest; +import org.apache.fineract.client.models.PutWorkingCapitalLoansLoanIdDiscountRequest; +import org.apache.fineract.client.models.PutWorkingCapitalLoansLoanIdRateRequest; import org.apache.fineract.client.models.PutWorkingCapitalLoansLoanIdRequest; import org.apache.fineract.test.data.workingcapitalproduct.DefaultWorkingCapitalLoanProduct; import org.apache.fineract.test.data.workingcapitalproduct.WorkingCapitalLoanProductResolver; @@ -40,9 +42,11 @@ public class WorkingCapitalLoanRequestFactory { public static final String DEFAULT_LOCALE = "en"; public static final DefaultWorkingCapitalLoanProduct DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT = DefaultWorkingCapitalLoanProduct.WCLP; public static final BigDecimal DEFAULT_PRINCIPAL = new BigDecimal(100); + public static final BigDecimal DEFAULT_DISCOUNT = new BigDecimal(100); public static final BigDecimal DEFAULT_TOTAL_PAYMENT = new BigDecimal(100); public static final BigDecimal DEFAULT_PERIOD_PAYMENT_RATE = new BigDecimal(1); public static final BigDecimal DEFAULT_DISCOUNT_ZERO = BigDecimal.ZERO; + public static final BigDecimal DEFAULT_PAYMENT_RATE = new BigDecimal(15); public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT); public static final String DATE_SUBMIT_STRING = FORMATTER.format(Utils.now().minusMonths(1L)); @@ -118,4 +122,17 @@ public PostWorkingCapitalLoansDelinquencyActionRequest defaultWorkingCapitalLoan .dateFormat(DATE_FORMAT)// .locale(DEFAULT_LOCALE);// } + + public PutWorkingCapitalLoansLoanIdDiscountRequest defaultWorkingCapitalLoanUpdateDiscountRequest() { + return new PutWorkingCapitalLoansLoanIdDiscountRequest()// + .discountAmount(DEFAULT_DISCOUNT).note("")// + .dateFormat(DATE_FORMAT)// + .locale(DEFAULT_LOCALE);// + } + + public PutWorkingCapitalLoansLoanIdRateRequest defaultWorkingCapitalLoanUpdateRateRequest() { + return new PutWorkingCapitalLoansLoanIdRateRequest() // + .periodPaymentRate(DEFAULT_PAYMENT_RATE) // + .locale(DEFAULT_LOCALE); // + } } 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 0b1b6f60900..c8f2873e316 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 @@ -1117,4 +1117,8 @@ public static String nearBreachMustBeLowerThenBreachFailure() { public static String nearBreachIdNotFoundFailure(long nearBreachId) { return String.format("Working Capital Near Breach with id %s was not found.", nearBreachId); } + + public static String periodPaymentRateOnNonActiveLoanFailure() { + return "Period payment rate change is allowed only for active loans"; + } } diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalAmortizationScheduleStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalAmortizationScheduleStepDef.java index 5908d52fecc..b6cd0a50a10 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalAmortizationScheduleStepDef.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalAmortizationScheduleStepDef.java @@ -49,14 +49,14 @@ public class WorkingCapitalAmortizationScheduleStepDef extends AbstractStepDef { private final FineractFeignClient fineractFeignClient; - @When("Admin generates a projected amortization schedule with originationFeeAmount {double}, netDisbursementAmount {double}, totalPaymentValue {double}, periodPaymentRate {double}, npvDayCount {int}, expectedDisbursementDate {string}") - public void generateAmortizationSchedule(final double originationFeeAmount, final double netDisbursementAmount, + @When("Admin generates a projected amortization schedule with discountFeeAmount {double}, netDisbursementAmount {double}, totalPaymentValue {double}, periodPaymentRate {double}, npvDayCount {int}, expectedDisbursementDate {string}") + public void generateAmortizationSchedule(final double discountFeeAmount, final double netDisbursementAmount, final double totalPaymentValue, final double periodPaymentRate, final int npvDayCount, final String expectedDisbursementDate) { final Long loanId = extractLoanId(); final WorkingCapitalLoansApi api = fineractFeignClient.create(WorkingCapitalLoansApi.class); final ProjectedAmortizationScheduleGenerateRequest request = new ProjectedAmortizationScheduleGenerateRequest(); - request.setOriginationFeeAmount(BigDecimal.valueOf(originationFeeAmount)); + request.setDiscountFeeAmount(BigDecimal.valueOf(discountFeeAmount)); request.setNetDisbursementAmount(BigDecimal.valueOf(netDisbursementAmount)); request.setTotalPaymentValue(BigDecimal.valueOf(totalPaymentValue)); request.setPeriodPaymentRate(BigDecimal.valueOf(periodPaymentRate)); @@ -94,13 +94,13 @@ private void verifySummaryFields(final DataTable dataTable) { final Map expected = dataTable.asMaps().getFirst(); final SoftAssertions assertions = new SoftAssertions(); - assertDecimal(assertions, "originationFeeAmount", response.getOriginationFeeAmount(), expected.get("originationFeeAmount")); + assertDecimal(assertions, "discountFeeAmount", response.getDiscountFeeAmount(), expected.get("discountFeeAmount")); assertDecimal(assertions, "netDisbursementAmount", response.getNetDisbursementAmount(), expected.get("netDisbursementAmount")); assertDecimal(assertions, "totalPaymentValue", response.getTotalPaymentValue(), expected.get("totalPaymentValue")); assertDecimal(assertions, "periodPaymentRate", response.getPeriodPaymentRate(), expected.get("periodPaymentRate")); assertInt(assertions, "npvDayCount", response.getNpvDayCount(), expected.get("npvDayCount")); assertDecimal(assertions, "expectedPaymentAmount", response.getExpectedPaymentAmount(), expected.get("expectedPaymentAmount")); - assertInt(assertions, "loanTerm", response.getLoanTerm(), expected.get("loanTerm")); + assertInt(assertions, "originalPaymentNumber", response.getOriginalPaymentNumber(), expected.get("originalPaymentNumber")); assertions.assertAll(); } @@ -123,18 +123,13 @@ private void verifyPaymentDetails(final DataTable dataTable) { assertInt(assertions, p + "paymentNo", actual.getPaymentNo(), expected.get("paymentNo")); assertDate(assertions, p + "date", actual.getPaymentDate(), expected.get("date")); - assertLong(assertions, p + "paymentsLeft", actual.getPaymentsLeft(), expected.get("paymentsLeft")); assertNullableDecimal(assertions, p + "expectedPaymentAmount", actual.getExpectedPaymentAmount(), expected.get("expectedPaymentAmount")); - assertNullableDecimal(assertions, p + "forecastPaymentAmount", actual.getForecastPaymentAmount(), - expected.get("forecastPaymentAmount")); assertOptionalDecimal(assertions, p + "discountFactor", actual.getDiscountFactor(), expected.get("discountFactor")); assertNullableDecimal(assertions, p + "npvValue", actual.getNpvValue(), expected.get("npvValue")); assertNullableDecimal(assertions, p + "balance", actual.getBalance(), expected.get("balance")); assertNullableDecimal(assertions, p + "expectedAmortizationAmount", actual.getExpectedAmortizationAmount(), expected.get("expectedAmortizationAmount")); - assertNullableDecimal(assertions, p + "netAmortizationAmount", actual.getNetAmortizationAmount(), - expected.get("netAmortizationAmount")); assertNullableDecimal(assertions, p + "actualPaymentAmount", actual.getActualPaymentAmount(), expected.get("actualPaymentAmount")); assertNullableDecimal(assertions, p + "actualAmortizationAmount", actual.getActualAmortizationAmount(), @@ -171,13 +166,6 @@ private static void assertInt(final SoftAssertions assertions, final String fiel assertions.assertThat(WorkingCapitalScheduleMatcher.matchesInteger(actual, expectedStr)).as(field).isTrue(); } - private static void assertLong(final SoftAssertions assertions, final String field, final Long actual, final String expectedStr) { - if (WorkingCapitalScheduleMatcher.isBlank(expectedStr)) { - return; - } - assertions.assertThat(WorkingCapitalScheduleMatcher.matchesLong(actual, expectedStr)).as(field).isTrue(); - } - private static void assertDate(final SoftAssertions assertions, final String field, final LocalDate actual, final String expectedStr) { if (WorkingCapitalScheduleMatcher.isBlank(expectedStr)) { return; diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalLoanAccountStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalLoanAccountStepDef.java index 186df4f2c0e..554a2802593 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalLoanAccountStepDef.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalLoanAccountStepDef.java @@ -75,9 +75,11 @@ import org.apache.fineract.client.models.PostWorkingCapitalLoansResponse; import org.apache.fineract.client.models.ProjectedAmortizationScheduleData; import org.apache.fineract.client.models.ProjectedAmortizationSchedulePaymentData; +import org.apache.fineract.client.models.PutWorkingCapitalLoansLoanIdRateRequest; import org.apache.fineract.client.models.PutWorkingCapitalLoansLoanIdRequest; import org.apache.fineract.client.models.PutWorkingCapitalLoansLoanIdResponse; import org.apache.fineract.client.models.WorkingCapitalLoanCommandTemplateData; +import org.apache.fineract.client.models.WorkingCapitalLoanPeriodPaymentRateChangeData; import org.apache.fineract.test.data.LoanStatus; import org.apache.fineract.test.data.paymenttype.DefaultPaymentType; import org.apache.fineract.test.data.paymenttype.PaymentTypeResolver; @@ -327,6 +329,273 @@ public void creatingAWorkingCapitalLoanWithMissingMandatoryFieldsWillResultAnErr log.info("Verified working capital loan creation failed with missing mandatory fields"); } + @Then("Creating a working capital loan with near breachId {long} on {string} will result with error") + public void createLoanWithInvalidNearBreachId(final long nearBreachId, final String submittedOnDate) { + final Long breachId = createBreachAndGetId(); + final PostWorkingCapitalLoansRequest request = createWorkingCapitalLoanAccountDefaultRequest(submittedOnDate).breachId(breachId) + .nearBreachId(nearBreachId); + + final CallFailedRuntimeException exception = fail( + () -> fineractClient.workingCapitalLoans().submitWorkingCapitalLoanApplication(request)); + assertThat(exception.getDeveloperMessage()) + .contains(String.format("Working Capital Near Breach with id %s was not found.", nearBreachId)); + assertThat(exception.getStatus()).as("HTTP status").isEqualTo(404); + } + + @Then("Admin creates working capital loan with breach override allowed with breach override and the following data:") + public void createLoanWithBreachOverrideAllowedWithBreach(final DataTable table) { + final List> data = table.asLists(); + final List loanData = data.get(1); + final Long overrideBreachId = createBreachAndGetId(); + createWorkingCapitalLoanAccountWithBreachNearBreachData(loanData, overrideBreachId, null); + } + + @Then("Admin creates working capital loan with breach override allowed with breach and near breach override and the following data:") + public void createLoanWithBreachOverrideAllowedWithBreachAndNearBreachOverride(final DataTable table) { + final List> data = table.asLists(); + final List loanData = data.get(1); + final Long overrideBreachId = createBreachAndGetId(); + final Long overrideNearBreachId = createNearBreachAndGetId(); + createWorkingCapitalLoanAccountWithBreachNearBreachData(loanData, overrideBreachId, overrideNearBreachId); + } + + @Then("Admin creates working capital loan with {int} {string} breach override and the following data:") + public void createLoanWithBreachOverrideAllowedWithBreachOverrideData(int breachFrequency, String breachFrequencyType, + final DataTable table) { + final List> data = table.asLists(); + final List loanData = data.get(1); + final Long overrideBreachId = createBreachOverrideAndGetId(breachFrequency, breachFrequencyType); + createWorkingCapitalLoanAccountWithBreachNearBreachData(loanData, overrideBreachId, null); + } + + @Then("Admin creates working capital loan with {int} {string} breach and {int} {string} near breach override and the following data:") + public void createLoanWithBreachOverrideAllowedWithBreachAndNearBreachOverrideData(int breachFrequency, String breachFrequencyType, + int nearBreachFrequency, String nearBreachFrequencyType, final DataTable table) { + final List> data = table.asLists(); + final Long overrideBreachId = createBreachOverrideAndGetId(breachFrequency, breachFrequencyType); + final Long overrideNearBreachId = createNearBreachOverrideAndGetId(nearBreachFrequency, nearBreachFrequencyType); + createWorkingCapitalLoanAccountWithBreachNearBreachData(data.get(1), overrideBreachId, overrideNearBreachId); + } + + @Then("Admin creates working capital loan with breach override allowed with {int} {string} breach and the following data:") + public void createLoanWithBreachOverrideAllowedWithBreachhData(final DataTable table, int breachFrequency, String breachFrequencyType, + int nearBreachFrequency, String nearBreachFrequencyType) { + final List> data = table.asLists(); + final Long overrideBreachId = createBreachOverrideAndGetId(breachFrequency, breachFrequencyType); + final Long overrideNearBreachId = createNearBreachOverrideAndGetId(nearBreachFrequency, nearBreachFrequencyType); + createWorkingCapitalLoanAccountWithBreachNearBreachData(data.get(1), overrideBreachId, overrideNearBreachId); + } + + @Then("Admin creates working capital loan with breach override allowed with {int} {string} breach and {int} {string} near breach and the following data:") + public void createLoanWithBreachOverrideAllowedWithBreachAndNearBreachData(int breachFrequency, String breachFrequencyType, + int nearBreachFrequency, String nearBreachFrequencyType, final DataTable table) { + final List> data = table.asLists(); + final Long overrideBreachId = createBreachAndGetId(breachFrequency, breachFrequencyType); + final Long overrideNearBreachId = createNearBreachAndGetId(nearBreachFrequency, nearBreachFrequencyType); + createWorkingCapitalLoanAccountWithBreachNearBreachData(data.get(1), overrideBreachId, overrideNearBreachId); + } + + @Then("Admin creates working capital loan with breach from WCLP while override is allowed and the following data:") + public void createLoanWithBreachFromWCLPOverrideAllowedData(DataTable table) { + final List> data = table.asLists(); + final List loanData = data.get(1); + + final String loanProduct = loanData.get(0); + final Long loanProductId = resolveLoanProductId(loanProduct); + final Long breachIdFromWCLP = getBreachIdFromWCLP(loanProductId); + testContext().set(TestContextKey.WORKING_CAPITAL_BREACH_ID, breachIdFromWCLP); + + createWorkingCapitalLoanAccountWithBreachNearBreachData(loanData, breachIdFromWCLP, null); + } + + @Then("Admin creates working capital loan with breach and near breach from WCLP while override is allowed and the following data:") + public void createLoanWithBreachNearBreachFromWCLPOverrideAllowedData(DataTable table) { + final List> data = table.asLists(); + final List loanData = data.get(1); + final String loanProduct = loanData.get(0); + final Long loanProductId = resolveLoanProductId(loanProduct); + + final Long breachIdFromWCLP = getBreachIdFromWCLP(loanProductId); + final Long nearBreachIdFromWCLP = getNearBreachIdFromWCLP(loanProductId); + testContext().set(TestContextKey.WORKING_CAPITAL_BREACH_ID, breachIdFromWCLP); + testContext().set(TestContextKey.WORKING_CAPITAL_NEAR_BREACH_ID, nearBreachIdFromWCLP); + + createWorkingCapitalLoanAccountWithBreachNearBreachData(loanData, breachIdFromWCLP, nearBreachIdFromWCLP); + } + + @Then("Admin creates working capital loan with with breach and near breach on {string} date") + public void createLoanWithBreachOverrideAllowedWithBreachAndNearBreachData(String submittedOnDate) { + final Long breachId = createBreachAndGetId(); + final Long nearBreachId = createNearBreachAndGetId(); + + final PostWorkingCapitalLoansRequest loansRequest = createWorkingCapitalLoanAccountDefaultRequest(submittedOnDate) + .breachId(breachId).nearBreachId(nearBreachId); + createWorkingCapitalLoanAccount(loansRequest); + } + + @Then("Verify working capital loan account has been created with correct breach data") + public void checkCreateWCLoanAccountBreachData() { + final Long breachId = testContext().get(TestContextKey.WORKING_CAPITAL_BREACH_ID); + checkCreateWCLoanAccountBreachData(breachId); + } + + @Then("Verify working capital loan account has been created with correct breach data inherited from WCLP level") + public void checkCreateWCLoanAccountBreachDataFromWCLP() { + final PostWorkingCapitalLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); + long loanId = loanResponse.getLoanId(); + + GetWorkingCapitalLoansLoanIdResponse loanProductResponse = fineractClient.workingCapitalLoans() + .retrieveWorkingCapitalLoanById(loanId); + + final Long loanProductId = loanProductResponse.getProduct().getId(); + final Long breachIdFromWCLP = getBreachIdFromWCLP(loanProductId); + + checkCreateWCLoanAccountBreachData(breachIdFromWCLP); + } + + @Then("Verify working capital loan account has been created with correct breach override data") + public void checkCreateWCLoanAccountBreachOverrideData() { + final Long breachIdFromWCLP = testContext().get(TestContextKey.WORKING_CAPITAL_BREACH_ID_OVERRIDE); + checkCreateWCLoanAccountBreachData(breachIdFromWCLP); + } + + @Then("Verify working capital loan account has been created with correct breach and near breach data") + public void checkCreateWCLoanAccountBreachAndNearBreachData() { + final Long breachIdFromWCLP = testContext().get(TestContextKey.WORKING_CAPITAL_BREACH_ID); + final Long nearBreachIdFromWCLP = testContext().get(TestContextKey.WORKING_CAPITAL_NEAR_BREACH_ID); + checkCreateWCLoanAccountBreachNearBreachData(breachIdFromWCLP, nearBreachIdFromWCLP); + } + + @Then("Verify working capital loan account has been created with correct breach and near breach override data") + public void checkCreateWCLoanAccountBreachAndNearBreachOverrideData() { + final Long breachIdFromWCLP = testContext().get(TestContextKey.WORKING_CAPITAL_BREACH_ID_OVERRIDE); + final Long nearBreachIdFromWCLP = testContext().get(TestContextKey.WORKING_CAPITAL_NEAR_BREACH_ID_OVERRIDE); + checkCreateWCLoanAccountBreachNearBreachData(breachIdFromWCLP, nearBreachIdFromWCLP); + } + + @Then("Verify working capital loan account has been created with correct breach and near breach data inherited from WCLP level") + public void checkCreateWCLoanAccountBreachNearBreachDataFromWCLP() { + final PostWorkingCapitalLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); + long loanId = loanResponse.getLoanId(); + + GetWorkingCapitalLoansLoanIdResponse loanProductResponse = fineractClient.workingCapitalLoans() + .retrieveWorkingCapitalLoanById(loanId); + + final Long loanProductId = loanProductResponse.getProduct().getId(); + final Long breachIdFromWCLP = getBreachIdFromWCLP(loanProductId); + final Long nearBreachIdFromWCLP = getNearBreachIdFromWCLP(loanProductId); + + checkCreateWCLoanAccountBreachNearBreachData(breachIdFromWCLP, nearBreachIdFromWCLP); + } + + @Then("Verify working capital loan account has been created with none breach nor near breach data") + public void checkCreateWCLoanAccountNoneBreachNearBreachData() { + final PostWorkingCapitalLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); + Long loanId = loanResponse.getLoanId(); + + final GetWorkingCapitalLoansLoanIdResponse loanAccountResponse = retrieveLoanDetails(loanId); + assertThat(loanAccountResponse.getBreach()).isNull(); + assertThat(loanAccountResponse.getNearBreach()).isNull(); + } + + @Then("Admin failed to create working capital loan while breach override disallowed with breach override and the following data:") + public void createLoanWithBreachOverrideDisallowedWithBreachFailure(final DataTable table) { + final List> data = table.asLists(); + final List loanData = data.get(1); + final Long overrideBreachId = createBreachOverrideAndGetId(); + + final PostWorkingCapitalLoansRequest loansRequest = createWorkingCapitalLoanAccountWithBreachNearBreachRequest(loanData, + overrideBreachId, null); + String message = ErrorMessageHelper.overrideDisallowedByProductFailure(); + verifyCreateWorkingCapitalLoanAccountFailure(loansRequest, 400, message); + } + + @Then("Admin failed to create working capital loan while breach override disallowed with breach override and default following data:") + public void createLoanWithBreachOverrideDisallowedWithBreachDefaultFailure(final DataTable table) { + final List loanData = table.asLists().get(1); + final String loanProduct = loanData.get(0); + final String submittedOnDate = loanData.get(1); + + final Long overrideBreachId = createBreachOverrideAndGetId(); + final PostWorkingCapitalLoansRequest loansRequest = createWorkingCapitalLoanAccountDefaultRequest(loanProduct, submittedOnDate) + .breachId(overrideBreachId); + String message = ErrorMessageHelper.overrideDisallowedByProductFailure(); + verifyCreateWorkingCapitalLoanAccountFailure(loansRequest, 400, message); + } + + @Then("Admin failed to create working capital loan while breach override disallowed with breach and near breach override and the following data:") + public void createLoanWithBreachOverrideDisallowedWithBreachAndNearBreachFailure(final DataTable table) { + final List> data = table.asLists(); + final List loanData = data.get(1); + final Long overrideBreachId = createBreachOverrideAndGetId(); + final Long overrideNearBreachId = createNearBreachOverrideAndGetId(); + + final PostWorkingCapitalLoansRequest loansRequest = createWorkingCapitalLoanAccountWithBreachNearBreachRequest(loanData, + overrideBreachId, overrideNearBreachId); + String message = ErrorMessageHelper.overrideDisallowedByProductFailure(); + verifyCreateWorkingCapitalLoanAccountFailure(loansRequest, 400, message); + } + + @Then("Admin failed to create working capital loan while breach override disallowed with breach and near breach override and default following data:") + public void createLoanWithBreachOverrideDisallowedWithBreachAndNearBreachDefaultFailure(final DataTable table) { + final List> data = table.asLists(); + final List loanData = data.get(1); + final String loanProduct = loanData.get(0); + final String submittedOnDate = loanData.get(1); + + final Long overrideBreachId = createBreachOverrideAndGetId(); + final Long overrideNearBreachId = createNearBreachOverrideAndGetId(); + + final PostWorkingCapitalLoansRequest loansRequest = createWorkingCapitalLoanAccountDefaultRequest(loanProduct, submittedOnDate) + .breachId(overrideBreachId).nearBreachId(overrideNearBreachId); + + String message = ErrorMessageHelper.overrideDisallowedByProductFailure(); + verifyCreateWorkingCapitalLoanAccountFailure(loansRequest, 400, message); + } + + @Then("Admin failed to create WC loan account on {string} with breach {int} {string} frequency lower then near breach {int} {string} frequency") + public void createLoanWithBreachLowerThenNearBreachFailure(String submittedOnDate, int breachFrequency, String breachFrequencyType, + int nearBreachFrequency, String nearBreachFrequencyType) { + final Long breachId = createBreachAndGetId(breachFrequency, breachFrequencyType); + final Long nearBreachId = createNearBreachAndGetId(nearBreachFrequency, nearBreachFrequencyType); + + final PostWorkingCapitalLoansRequest loansRequest = createWorkingCapitalLoanAccountDefaultRequest(submittedOnDate) + .breachId(breachId).nearBreachId(nearBreachId); + String message = ErrorMessageHelper.nearBreachMustBeLowerThenBreachFailure(); + verifyCreateWorkingCapitalLoanAccountFailure(loansRequest, 400, message); + } + + @Then("Admin failed to create WC loan account on {string} without breach, but with near breach") + public void createLoanWithoutBreachButWithNearBreachFailure(String submittedOnDate) { + final Long nearBreachId = createNearBreachAndGetId(); + + final PostWorkingCapitalLoansRequest loansRequest = createWorkingCapitalLoanAccountDefaultRequest(submittedOnDate) + .nearBreachId(nearBreachId); + String message = ErrorMessageHelper.nearBreachCannotEnableWithoutBreachFailure(); + verifyCreateWorkingCapitalLoanAccountFailure(loansRequest, 400, message); + } + + @When("Admin failed to create Working Capital on {string} with period payment rate {string} value and outcomes with {} error message") + public void adminAddWorkingCapitalPeriodPaymentRateInvalidDataFailure(String submittedOnDate, final String periodPaymentRate, + final String errorMessage) { + final PostWorkingCapitalLoansRequest loansRequest = createWorkingCapitalLoanAccountDefaultRequest(submittedOnDate) + .periodPaymentRate(new BigDecimal(periodPaymentRate)); + verifyCreateWorkingCapitalLoanAccountFailure(loansRequest, 400, errorMessage); + } + + @When("Admin failed to create Working Capital with period payment rate {string} value and outcomes with {} error message with default following data:") + public void createWorkingCapitalWithPeriodPaymentRateInvalidDataFailure(final String periodPaymentRate, final String errorMessage, + final DataTable table) { + final List> data = table.asLists(); + final List loanData = data.get(1); + final String loanProduct = loanData.get(0); + final String submittedOnDate = loanData.get(1); + + final PostWorkingCapitalLoansRequest loansRequest = createWorkingCapitalLoanAccountDefaultRequest(loanProduct, submittedOnDate) + .periodPaymentRate(new BigDecimal(periodPaymentRate)); + verifyCreateWorkingCapitalLoanAccountFailure(loansRequest, 400, errorMessage); + } + @When("Admin modifies the working capital loan with the following data:") public void modifyWorkingCapitalLoan(final DataTable table) { final List> data = table.asLists(); @@ -471,6 +740,13 @@ public void modifyLoanWithInvalidNearBreachId(final long nearBreachId) { verifyModifyWorkingCapitalLoanAccountFailure(modifyRequest, 404, errorMessage); } + @Then("Admin failed to modify WC loan account with period payment rate {string} value and outcomes with {} error message") + public void modifyLoanWithInvalidPeriodPaymentRateFailure(String periodPaymentRate, String errorMessage) { + final PutWorkingCapitalLoansLoanIdRequest modifyRequest = workingCapitalLoanRequestFactory.defaultModifyWorkingCapitalLoansRequest() // + .periodPaymentRate(new BigDecimal(periodPaymentRate));// + verifyModifyWorkingCapitalLoanAccountFailure(modifyRequest, 400, errorMessage); + } + @When("Admin deletes the working capital loan account") public void deleteWorkingCapitalLoanAccount() { deleteLoan(false); @@ -990,11 +1266,84 @@ public void addDiscountFeeWCLoanExceedDiscountAmountProductFailure(String discou addDiscountFeeFailedCheck(discountAmount, errorMessage); } - @And("Working Capital Loan has transactions:") - public void workingCapitalLoanHasTransactions(final DataTable dataTable) throws InvocationTargetException, IllegalAccessException { - GetWorkingCapitalLoansLoanIdResponse getWorkingCapitalLoansLoanIdResponse = retrieveLoanDetails(getCreatedLoanId()); - List actualTransactions = getWorkingCapitalLoansLoanIdResponse.getTransactions(); - assertTable(GetWorkingCapitalLoanTransactionIdResponse.class, dataTable, actualTransactions); + @And("Working Capital Loan has transactions:") + public void workingCapitalLoanHasTransactions(final DataTable dataTable) throws InvocationTargetException, IllegalAccessException { + GetWorkingCapitalLoansLoanIdResponse getWorkingCapitalLoansLoanIdResponse = retrieveLoanDetails(getCreatedLoanId()); + List actualTransactions = getWorkingCapitalLoansLoanIdResponse.getTransactions(); + assertTable(GetWorkingCapitalLoanTransactionIdResponse.class, dataTable, actualTransactions); + } + + @When("Admin update Working Capital period payment rate with {string} value") + public void adminAddWorkingCapitalPeriodPaymentRate(String periodPaymentRate) { + final PostWorkingCapitalLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); + long loanId = loanResponse.getLoanId(); + + PutWorkingCapitalLoansLoanIdRateRequest rateChangeRequest = workingCapitalLoanRequestFactory + .defaultWorkingCapitalLoanUpdateRateRequest().periodPaymentRate(new BigDecimal(periodPaymentRate)); + + final CommandProcessingResult rateChangeResponse = ok( + () -> fineractClient.workingCapitalLoans().updateWorkingCapitalLoanRateById(loanId, rateChangeRequest)); + final Long rateChangeId = rateChangeResponse.getResourceId(); + + testContext().set(TestContextKey.WORKING_CAPITAL_LOAN_RATE_CHANGE_ID, rateChangeId); + assertThat(rateChangeResponse.getChanges()).isNotNull(); + checkWorkingCapitalPeriodPaymentRate(loanId, periodPaymentRate); + } + + @When("Admin update Working Capital period payment rate with {string} value by externalId") + public void adminAddWorkingCapitalPeriodPaymentRateByExternalId(String periodPaymentRate) { + final Long loanId = getCreatedLoanId(); + final String externalId = retrieveLoanExternalId(loanId); + + PutWorkingCapitalLoansLoanIdRateRequest rateChangeRequest = workingCapitalLoanRequestFactory + .defaultWorkingCapitalLoanUpdateRateRequest().periodPaymentRate(new BigDecimal(periodPaymentRate)); + + final CommandProcessingResult rateChangeResponse = ok( + () -> fineractClient.workingCapitalLoans().updateWorkingCapitalLoanRateByExternalId(externalId, rateChangeRequest)); + final Long rateChangeId = rateChangeResponse.getResourceId(); + + testContext().set(TestContextKey.WORKING_CAPITAL_LOAN_RATE_CHANGE_ID, rateChangeId); + assertThat(rateChangeResponse.getChanges()).isNotNull(); + checkWorkingCapitalPeriodPaymentRate(loanId, periodPaymentRate); + } + + @When("Admin update Working Capital period payment rate failed with {string} value on non active loan") + public void adminAddWorkingCapitalPeriodPaymentRateNonActiveLoanFailure(final String periodPaymentRate) { + String errorMessage = ErrorMessageHelper.periodPaymentRateOnNonActiveLoanFailure(); + updatePeriodPaymentRateFailed(periodPaymentRate, errorMessage); + } + + @When("Admin update Working Capital period payment rate failed with {string} value with {} error message") + public void adminAddWorkingCapitalPeriodPaymentRateInvalidDataFailure(final String periodPaymentRate, final String errorMessage) { + updatePeriodPaymentRateFailed(periodPaymentRate, errorMessage); + } + + @When("Working Capital Loan Period Payment Rate changes history contains the following data:") + public void adminChecksWorkingCapitalPeriodPaymentRateChangesHistory(DataTable table) { + PostWorkingCapitalLoansResponse loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); + long loanId = loanCreateResponse.getLoanId(); + String resourceId = String.valueOf(loanId); + + List rateChangesResponse = ok( + () -> fineractClient.workingCapitalLoans().getWorkingCapitalLoanRateChangeHistoryById(loanId)); + + List> data = table.asLists(); + List header = table.row(0); + checkPeriodPaymentRateChangeHistory(data, rateChangesResponse, header, resourceId); + } + + @When("Working Capital Loan Period Payment Rate changes history by externalId contains the following data:") + public void adminChecksWorkingCapitalPeriodPaymentRateChangesHistoryByExternalId(DataTable table) { + final Long loanId = getCreatedLoanId(); + String resourceId = String.valueOf(loanId); + final String externalId = retrieveLoanExternalId(loanId); + + List rateChangesResponse = ok( + () -> fineractClient.workingCapitalLoans().getWorkingCapitalLoanRateChangeHistoryByExternalId(externalId)); + + List> data = table.asLists(); + List header = table.row(0); + checkPeriodPaymentRateChangeHistory(data, rateChangesResponse, header, resourceId); } // ==================================== @@ -1349,408 +1698,162 @@ private void submitLoanAndStore(final PostWorkingCapitalLoansRequest request) { () -> fineractClient.workingCapitalLoans().submitWorkingCapitalLoanApplication(request)); testContext().set(TestContextKey.LOAN_CREATE_RESPONSE, response); testContext().set(TestContextKey.WORKING_CAPITAL_LOAN_CREATE_RESPONSE, response); - log.info("Working Capital Loan created, loan ID: {}", response.getLoanId()); - } - - @Then("Working capital loan account has delinquencyGraceDays {int} and delinquencyStartType {string}") - public void verifyLoanGraceDays(int expectedGraceDays, String expectedStartType) { - final PostWorkingCapitalLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); - final Long loanId = loanResponse.getLoanId(); - - final GetWorkingCapitalLoansLoanIdResponse response = ok( - () -> fineractClient.workingCapitalLoans().retrieveWorkingCapitalLoanById(loanId)); - - assertThat(response.getDelinquencyGraceDays()).as("delinquencyGraceDays").isEqualTo(expectedGraceDays); - assertThat(response.getDelinquencyStartType()).as("delinquencyStartType").isNotNull(); - assertThat(response.getDelinquencyStartType().getCode()).as("delinquencyStartType code").isEqualTo(expectedStartType); - } - - @When("Admin modifies the working capital loan with grace days:") - public void modifyLoanWithGraceDays(final DataTable table) { - final Map row = table.asMaps().get(0); - final PostWorkingCapitalLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); - final Long loanId = loanResponse.getLoanId(); - - final PutWorkingCapitalLoansLoanIdRequest modifyRequest = workingCapitalLoanRequestFactory.defaultModifyWorkingCapitalLoansRequest() // - .delinquencyGraceDays( - Optional.ofNullable(row.get("delinquencyGraceDays")).filter(s -> !s.isEmpty()).map(Integer::valueOf).orElse(null)) // - .delinquencyStartType(row.get("delinquencyStartType")); - - final PutWorkingCapitalLoansLoanIdResponse response = ok( - () -> fineractClient.workingCapitalLoans().modifyWorkingCapitalLoanApplicationById(loanId, modifyRequest, "")); - testContext().set(TestContextKey.LOAN_MODIFY_RESPONSE, response); - } - - @When("Admin approves the working capital loan on {string}") - public void approveWorkingCapitalLoan(final String approvedOnDate) { - final PostWorkingCapitalLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); - final Long loanId = loanResponse.getLoanId(); - - final PostWorkingCapitalLoansLoanIdRequest approveRequest = new PostWorkingCapitalLoansLoanIdRequest() // - .approvedOnDate(approvedOnDate) // - .expectedDisbursementDate(approvedOnDate) // - .dateFormat(DATE_FORMAT) // - .locale(WorkingCapitalLoanRequestFactory.DEFAULT_LOCALE); - - ok(() -> fineractClient.workingCapitalLoans().stateTransitionWorkingCapitalLoanById(loanId, "approve", approveRequest)); - log.info("Approved working capital loan {}", loanId); - } - - @Then("Creating a working capital loan with invalid delinquencyGraceDays {int} will result with status code {int}") - public void createLoanWithInvalidGraceDays(int graceDays, int expectedStatus) { - final Long clientId = extractClientId(); - final Long productId = testContext().get(TestContextKey.WORKING_CAPITAL_LOAN_PRODUCT_FOR_LOAN_TEST); - - final PostWorkingCapitalLoansRequest request = workingCapitalLoanRequestFactory.defaultWorkingCapitalLoansRequest(clientId) - .productId(productId) // - .delinquencyGraceDays(graceDays); - - final CallFailedRuntimeException exception = fail( - () -> fineractClient.workingCapitalLoans().submitWorkingCapitalLoanApplication(request)); - assertThat(exception.getStatus()).as("HTTP status").isEqualTo(expectedStatus); - } - - @Then("Creating a working capital loan with invalid delinquencyStartType {string} will result with status code {int}") - public void createLoanWithInvalidStartType(String startType, int expectedStatus) { - final Long clientId = extractClientId(); - final Long productId = testContext().get(TestContextKey.WORKING_CAPITAL_LOAN_PRODUCT_FOR_LOAN_TEST); - - final PostWorkingCapitalLoansRequest request = workingCapitalLoanRequestFactory.defaultWorkingCapitalLoansRequest(clientId) - .productId(productId) // - .delinquencyStartType(startType); - - final CallFailedRuntimeException exception = fail( - () -> fineractClient.workingCapitalLoans().submitWorkingCapitalLoanApplication(request)); - assertThat(exception.getStatus()).as("HTTP status").isEqualTo(expectedStatus); - } - - @Then("Creating a working capital loan with breachId {long} on {string} will result with status code {int}") - public void createLoanWithInvalidBreachId(final long breachId, final String submittedOnDate, final int expectedStatus) { - final Long clientId = extractClientId(); - final Long loanProductId = resolveLoanProductId(DefaultWorkingCapitalLoanProduct.WCLP.name()); - - final PostWorkingCapitalLoansRequest request = workingCapitalLoanRequestFactory.defaultWorkingCapitalLoansRequest(clientId) - .productId(loanProductId) // - .submittedOnDate(submittedOnDate) // - .expectedDisbursementDate(submittedOnDate) // - .principalAmount(new BigDecimal("100")) // - .totalPayment(new BigDecimal("100")) // - .periodPaymentRate(new BigDecimal("1")) // - .discount(BigDecimal.ZERO) // - .breachId(breachId); - - final CallFailedRuntimeException exception = fail( - () -> fineractClient.workingCapitalLoans().submitWorkingCapitalLoanApplication(request)); - assertThat(exception.getStatus()).as("HTTP status").isEqualTo(expectedStatus); - } - - @Then("Creating a working capital loan with breach override allowed {string} on {string} will result with status code {int}") - public void createLoanWithBreachOverrideAllowed(final String breachOverrideAllowed, final String submittedOnDate, - final int expectedStatus) { - final Long clientId = extractClientId(); - final boolean overrideAllowed = Boolean.parseBoolean(breachOverrideAllowed); - - final Long productBreachId = createBreachAndGetId(); - final Long overrideBreachId = createBreachAndGetId(); - final Long productId = createWorkingCapitalProductForBreachOverride(overrideAllowed, productBreachId); - - final PostWorkingCapitalLoansRequest request = workingCapitalLoanRequestFactory.defaultWorkingCapitalLoansRequest(clientId) - .productId(productId) // - .submittedOnDate(submittedOnDate) // - .expectedDisbursementDate(submittedOnDate) // - .principalAmount(new BigDecimal("100")) // - .totalPayment(new BigDecimal("100")) // - .periodPaymentRate(new BigDecimal("1")) // - .discount(null) // - .breachId(overrideBreachId); - - if (expectedStatus == 200) { - final PostWorkingCapitalLoansResponse response = ok( - () -> fineractClient.workingCapitalLoans().submitWorkingCapitalLoanApplication(request)); - assertThat(response).isNotNull(); - assertThat(response.getLoanId()).isNotNull(); - return; - } - - final CallFailedRuntimeException exception = fail( - () -> fineractClient.workingCapitalLoans().submitWorkingCapitalLoanApplication(request)); - assertThat(exception.getStatus()).as("HTTP status").isEqualTo(expectedStatus); - } - - @Then("Creating a working capital loan with near breachId {long} on {string} will result with error") - public void createLoanWithInvalidNearBreachId(final long nearBreachId, final String submittedOnDate) { - final Long breachId = createBreachAndGetId(); - final PostWorkingCapitalLoansRequest request = createWorkingCapitalLoanAccountDefaultRequest(submittedOnDate).breachId(breachId) - .nearBreachId(nearBreachId); - - final CallFailedRuntimeException exception = fail( - () -> fineractClient.workingCapitalLoans().submitWorkingCapitalLoanApplication(request)); - assertThat(exception.getDeveloperMessage()) - .contains(String.format("Working Capital Near Breach with id %s was not found.", nearBreachId)); - assertThat(exception.getStatus()).as("HTTP status").isEqualTo(404); - } - - @Then("Admin creates working capital loan with breach override allowed with breach override and the following data:") - public void createLoanWithBreachOverrideAllowedWithBreach(final DataTable table) { - final List> data = table.asLists(); - final List loanData = data.get(1); - final Long overrideBreachId = createBreachAndGetId(); - createWorkingCapitalLoanAccountWithBreachNearBreachData(loanData, overrideBreachId, null); - } - - @Then("Admin creates working capital loan with breach override allowed with breach and near breach override and the following data:") - public void createLoanWithBreachOverrideAllowedWithBreachAndNearBreachOverride(final DataTable table) { - final List> data = table.asLists(); - final List loanData = data.get(1); - final Long overrideBreachId = createBreachAndGetId(); - final Long overrideNearBreachId = createNearBreachAndGetId(); - createWorkingCapitalLoanAccountWithBreachNearBreachData(loanData, overrideBreachId, overrideNearBreachId); - } - - @Then("Admin creates working capital loan with {int} {string} breach override and the following data:") - public void createLoanWithBreachOverrideAllowedWithBreachOverrideData(int breachFrequency, String breachFrequencyType, - final DataTable table) { - final List> data = table.asLists(); - final List loanData = data.get(1); - final Long overrideBreachId = createBreachOverrideAndGetId(breachFrequency, breachFrequencyType); - createWorkingCapitalLoanAccountWithBreachNearBreachData(loanData, overrideBreachId, null); - } - - @Then("Admin creates working capital loan with {int} {string} breach and {int} {string} near breach override and the following data:") - public void createLoanWithBreachOverrideAllowedWithBreachAndNearBreachOverrideData(int breachFrequency, String breachFrequencyType, - int nearBreachFrequency, String nearBreachFrequencyType, final DataTable table) { - final List> data = table.asLists(); - final Long overrideBreachId = createBreachOverrideAndGetId(breachFrequency, breachFrequencyType); - final Long overrideNearBreachId = createNearBreachOverrideAndGetId(nearBreachFrequency, nearBreachFrequencyType); - createWorkingCapitalLoanAccountWithBreachNearBreachData(data.get(1), overrideBreachId, overrideNearBreachId); - } - - @Then("Admin creates working capital loan with breach override allowed with {int} {string} breach and the following data:") - public void createLoanWithBreachOverrideAllowedWithBreachhData(final DataTable table, int breachFrequency, String breachFrequencyType, - int nearBreachFrequency, String nearBreachFrequencyType) { - final List> data = table.asLists(); - final Long overrideBreachId = createBreachOverrideAndGetId(breachFrequency, breachFrequencyType); - final Long overrideNearBreachId = createNearBreachOverrideAndGetId(nearBreachFrequency, nearBreachFrequencyType); - createWorkingCapitalLoanAccountWithBreachNearBreachData(data.get(1), overrideBreachId, overrideNearBreachId); - } - - @Then("Admin creates working capital loan with breach override allowed with {int} {string} breach and {int} {string} near breach and the following data:") - public void createLoanWithBreachOverrideAllowedWithBreachAndNearBreachData(int breachFrequency, String breachFrequencyType, - int nearBreachFrequency, String nearBreachFrequencyType, final DataTable table) { - final List> data = table.asLists(); - final Long overrideBreachId = createBreachAndGetId(breachFrequency, breachFrequencyType); - final Long overrideNearBreachId = createNearBreachAndGetId(nearBreachFrequency, nearBreachFrequencyType); - createWorkingCapitalLoanAccountWithBreachNearBreachData(data.get(1), overrideBreachId, overrideNearBreachId); - } - - @Then("Admin creates working capital loan with breach from WCLP while override is allowed and the following data:") - public void createLoanWithBreachFromWCLPOverrideAllowedData(DataTable table) { - final List> data = table.asLists(); - final List loanData = data.get(1); - - final String loanProduct = loanData.get(0); - final Long loanProductId = resolveLoanProductId(loanProduct); - final Long breachIdFromWCLP = getBreachIdFromWCLP(loanProductId); - testContext().set(TestContextKey.WORKING_CAPITAL_BREACH_ID, breachIdFromWCLP); - - createWorkingCapitalLoanAccountWithBreachNearBreachData(loanData, breachIdFromWCLP, null); - } - - @Then("Admin creates working capital loan with breach and near breach from WCLP while override is allowed and the following data:") - public void createLoanWithBreachNearBreachFromWCLPOverrideAllowedData(DataTable table) { - final List> data = table.asLists(); - final List loanData = data.get(1); - final String loanProduct = loanData.get(0); - final Long loanProductId = resolveLoanProductId(loanProduct); - - final Long breachIdFromWCLP = getBreachIdFromWCLP(loanProductId); - final Long nearBreachIdFromWCLP = getNearBreachIdFromWCLP(loanProductId); - testContext().set(TestContextKey.WORKING_CAPITAL_BREACH_ID, breachIdFromWCLP); - testContext().set(TestContextKey.WORKING_CAPITAL_NEAR_BREACH_ID, nearBreachIdFromWCLP); - - createWorkingCapitalLoanAccountWithBreachNearBreachData(loanData, breachIdFromWCLP, nearBreachIdFromWCLP); - } - - @Then("Admin creates working capital loan with with breach on {string} date") - public void createLoanWithBreachOverrideAllowedWithBreachData(String submittedOnDate) { - final Long breachId = createBreachAndGetId(); - - final PostWorkingCapitalLoansRequest loansRequest = createWorkingCapitalLoanAccountDefaultRequest(submittedOnDate) - .breachId(breachId); - createWorkingCapitalLoanAccount(loansRequest); - } - - @Then("Admin creates working capital loan with with breach and near breach on {string} date") - public void createLoanWithBreachOverrideAllowedWithBreachAndNearBreachData(String submittedOnDate) { - final Long breachId = createBreachAndGetId(); - final Long nearBreachId = createNearBreachAndGetId(); - - final PostWorkingCapitalLoansRequest loansRequest = createWorkingCapitalLoanAccountDefaultRequest(submittedOnDate) - .breachId(breachId).nearBreachId(nearBreachId); - createWorkingCapitalLoanAccount(loansRequest); - } - - public void checkCreateWCLoanAccountBreachData(Long breachId) { - final PostWorkingCapitalLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); - Long loanId = loanResponse.getLoanId(); - - final GetWorkingCapitalLoansLoanIdResponse loanAccountResponse = retrieveLoanDetails(loanId); - assert loanAccountResponse.getBreach() != null; - assertThat(loanAccountResponse.getBreach().getId()).isEqualTo(breachId); - assertThat(loanAccountResponse.getNearBreach()).isNull(); + log.info("Working Capital Loan created, loan ID: {}", response.getLoanId()); } - public void checkCreateWCLoanAccountBreachNearBreachData(Long breachId, Long nearBreachId) { + @Then("Working capital loan account has delinquencyGraceDays {int} and delinquencyStartType {string}") + public void verifyLoanGraceDays(int expectedGraceDays, String expectedStartType) { final PostWorkingCapitalLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); - Long loanId = loanResponse.getLoanId(); + final Long loanId = loanResponse.getLoanId(); - final GetWorkingCapitalLoansLoanIdResponse loanAccountResponse = retrieveLoanDetails(loanId); - assert loanAccountResponse.getBreach() != null; - assert loanAccountResponse.getNearBreach() != null; - assertThat(loanAccountResponse.getBreach().getId()).isEqualTo(breachId); - assertThat(loanAccountResponse.getNearBreach().getId()).isEqualTo(nearBreachId); - } + final GetWorkingCapitalLoansLoanIdResponse response = ok( + () -> fineractClient.workingCapitalLoans().retrieveWorkingCapitalLoanById(loanId)); - @Then("Verify working capital loan account has been created with correct breach data") - public void checkCreateWCLoanAccountBreachData() { - final Long breachId = testContext().get(TestContextKey.WORKING_CAPITAL_BREACH_ID); - checkCreateWCLoanAccountBreachData(breachId); + assertThat(response.getDelinquencyGraceDays()).as("delinquencyGraceDays").isEqualTo(expectedGraceDays); + assertThat(response.getDelinquencyStartType()).as("delinquencyStartType").isNotNull(); + assertThat(response.getDelinquencyStartType().getCode()).as("delinquencyStartType code").isEqualTo(expectedStartType); } - @Then("Verify working capital loan account has been created with correct breach data inherited from WCLP level") - public void checkCreateWCLoanAccountBreachDataFromWCLP() { + @When("Admin modifies the working capital loan with grace days:") + public void modifyLoanWithGraceDays(final DataTable table) { + final Map row = table.asMaps().get(0); final PostWorkingCapitalLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); - long loanId = loanResponse.getLoanId(); - - GetWorkingCapitalLoansLoanIdResponse loanProductResponse = fineractClient.workingCapitalLoans() - .retrieveWorkingCapitalLoanById(loanId); + final Long loanId = loanResponse.getLoanId(); - final Long loanProductId = loanProductResponse.getProduct().getId(); - final Long breachIdFromWCLP = getBreachIdFromWCLP(loanProductId); + final PutWorkingCapitalLoansLoanIdRequest modifyRequest = workingCapitalLoanRequestFactory.defaultModifyWorkingCapitalLoansRequest() // + .delinquencyGraceDays( + Optional.ofNullable(row.get("delinquencyGraceDays")).filter(s -> !s.isEmpty()).map(Integer::valueOf).orElse(null)) // + .delinquencyStartType(row.get("delinquencyStartType")); - checkCreateWCLoanAccountBreachData(breachIdFromWCLP); + final PutWorkingCapitalLoansLoanIdResponse response = ok( + () -> fineractClient.workingCapitalLoans().modifyWorkingCapitalLoanApplicationById(loanId, modifyRequest, "")); + testContext().set(TestContextKey.LOAN_MODIFY_RESPONSE, response); } - @Then("Verify working capital loan account has been created with correct breach override data") - public void checkCreateWCLoanAccountBreachOverrideData() { - final Long breachIdFromWCLP = testContext().get(TestContextKey.WORKING_CAPITAL_BREACH_ID_OVERRIDE); - checkCreateWCLoanAccountBreachData(breachIdFromWCLP); - } + @When("Admin approves the working capital loan on {string}") + public void approveWorkingCapitalLoan(final String approvedOnDate) { + final PostWorkingCapitalLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); + final Long loanId = loanResponse.getLoanId(); - @Then("Verify working capital loan account has been created with correct breach and near breach data") - public void checkCreateWCLoanAccountBreachAndNearBreachData() { - final Long breachIdFromWCLP = testContext().get(TestContextKey.WORKING_CAPITAL_BREACH_ID); - final Long nearBreachIdFromWCLP = testContext().get(TestContextKey.WORKING_CAPITAL_NEAR_BREACH_ID); - checkCreateWCLoanAccountBreachNearBreachData(breachIdFromWCLP, nearBreachIdFromWCLP); - } + final PostWorkingCapitalLoansLoanIdRequest approveRequest = new PostWorkingCapitalLoansLoanIdRequest() // + .approvedOnDate(approvedOnDate) // + .expectedDisbursementDate(approvedOnDate) // + .dateFormat(DATE_FORMAT) // + .locale(WorkingCapitalLoanRequestFactory.DEFAULT_LOCALE); - @Then("Verify working capital loan account has been created with correct breach and near breach override data") - public void checkCreateWCLoanAccountBreachAndNearBreachOverrideData() { - final Long breachIdFromWCLP = testContext().get(TestContextKey.WORKING_CAPITAL_BREACH_ID_OVERRIDE); - final Long nearBreachIdFromWCLP = testContext().get(TestContextKey.WORKING_CAPITAL_NEAR_BREACH_ID_OVERRIDE); - checkCreateWCLoanAccountBreachNearBreachData(breachIdFromWCLP, nearBreachIdFromWCLP); + ok(() -> fineractClient.workingCapitalLoans().stateTransitionWorkingCapitalLoanById(loanId, "approve", approveRequest)); + log.info("Approved working capital loan {}", loanId); } - @Then("Verify working capital loan account has been created with correct breach and near breach data inherited from WCLP level") - public void checkCreateWCLoanAccountBreachNearBreachDataFromWCLP() { - final PostWorkingCapitalLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); - long loanId = loanResponse.getLoanId(); - - GetWorkingCapitalLoansLoanIdResponse loanProductResponse = fineractClient.workingCapitalLoans() - .retrieveWorkingCapitalLoanById(loanId); + @Then("Creating a working capital loan with invalid delinquencyGraceDays {int} will result with status code {int}") + public void createLoanWithInvalidGraceDays(int graceDays, int expectedStatus) { + final Long clientId = extractClientId(); + final Long productId = testContext().get(TestContextKey.WORKING_CAPITAL_LOAN_PRODUCT_FOR_LOAN_TEST); - final Long loanProductId = loanProductResponse.getProduct().getId(); - final Long breachIdFromWCLP = getBreachIdFromWCLP(loanProductId); - final Long nearBreachIdFromWCLP = getNearBreachIdFromWCLP(loanProductId); + final PostWorkingCapitalLoansRequest request = workingCapitalLoanRequestFactory.defaultWorkingCapitalLoansRequest(clientId) + .productId(productId) // + .delinquencyGraceDays(graceDays); - checkCreateWCLoanAccountBreachNearBreachData(breachIdFromWCLP, nearBreachIdFromWCLP); + final CallFailedRuntimeException exception = fail( + () -> fineractClient.workingCapitalLoans().submitWorkingCapitalLoanApplication(request)); + assertThat(exception.getStatus()).as("HTTP status").isEqualTo(expectedStatus); } - @Then("Verify working capital loan account has been created with none breach nor near breach data") - public void checkCreateWCLoanAccountNoneBreachNearBreachData() { - final PostWorkingCapitalLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); - Long loanId = loanResponse.getLoanId(); + @Then("Creating a working capital loan with invalid delinquencyStartType {string} will result with status code {int}") + public void createLoanWithInvalidStartType(String startType, int expectedStatus) { + final Long clientId = extractClientId(); + final Long productId = testContext().get(TestContextKey.WORKING_CAPITAL_LOAN_PRODUCT_FOR_LOAN_TEST); - final GetWorkingCapitalLoansLoanIdResponse loanAccountResponse = retrieveLoanDetails(loanId); - assertThat(loanAccountResponse.getBreach()).isNull(); - assertThat(loanAccountResponse.getNearBreach()).isNull(); + final PostWorkingCapitalLoansRequest request = workingCapitalLoanRequestFactory.defaultWorkingCapitalLoansRequest(clientId) + .productId(productId) // + .delinquencyStartType(startType); + + final CallFailedRuntimeException exception = fail( + () -> fineractClient.workingCapitalLoans().submitWorkingCapitalLoanApplication(request)); + assertThat(exception.getStatus()).as("HTTP status").isEqualTo(expectedStatus); } - @Then("Admin failed to create working capital loan while breach override disallowed with breach override and the following data:") - public void createLoanWithBreachOverrideDisallowedWithBreachFailure(final DataTable table) { - final List> data = table.asLists(); - final List loanData = data.get(1); - final Long overrideBreachId = createBreachOverrideAndGetId(); + @Then("Creating a working capital loan with breachId {long} on {string} will result with status code {int}") + public void createLoanWithInvalidBreachId(final long breachId, final String submittedOnDate, final int expectedStatus) { + final Long clientId = extractClientId(); + final Long loanProductId = resolveLoanProductId(DefaultWorkingCapitalLoanProduct.WCLP.name()); - final PostWorkingCapitalLoansRequest loansRequest = createWorkingCapitalLoanAccountWithBreachNearBreachRequest(loanData, - overrideBreachId, null); - String message = ErrorMessageHelper.overrideDisallowedByProductFailure(); - verifyCreateWorkingCapitalLoanAccountFailure(loansRequest, 400, message); + final PostWorkingCapitalLoansRequest request = workingCapitalLoanRequestFactory.defaultWorkingCapitalLoansRequest(clientId) + .productId(loanProductId) // + .submittedOnDate(submittedOnDate) // + .expectedDisbursementDate(submittedOnDate) // + .principalAmount(new BigDecimal("100")) // + .totalPayment(new BigDecimal("100")) // + .periodPaymentRate(new BigDecimal("1")) // + .discount(BigDecimal.ZERO) // + .breachId(breachId); + + final CallFailedRuntimeException exception = fail( + () -> fineractClient.workingCapitalLoans().submitWorkingCapitalLoanApplication(request)); + assertThat(exception.getStatus()).as("HTTP status").isEqualTo(expectedStatus); } - @Then("Admin failed to create working capital loan while breach override disallowed with breach override and default following data:") - public void createLoanWithBreachOverrideDisallowedWithBreachDefaultFailure(final DataTable table) { - final List loanData = table.asLists().get(1); - final String loanProduct = loanData.get(0); - final String submittedOnDate = loanData.get(1); + @Then("Creating a working capital loan with breach override allowed {string} on {string} will result with status code {int}") + public void createLoanWithBreachOverrideAllowed(final String breachOverrideAllowed, final String submittedOnDate, + final int expectedStatus) { + final Long clientId = extractClientId(); + final boolean overrideAllowed = Boolean.parseBoolean(breachOverrideAllowed); - final Long overrideBreachId = createBreachOverrideAndGetId(); - final PostWorkingCapitalLoansRequest loansRequest = createWorkingCapitalLoanAccountDefaultRequest(loanProduct, submittedOnDate) + final Long productBreachId = createBreachAndGetId(); + final Long overrideBreachId = createBreachAndGetId(); + final Long productId = createWorkingCapitalProductForBreachOverride(overrideAllowed, productBreachId); + + final PostWorkingCapitalLoansRequest request = workingCapitalLoanRequestFactory.defaultWorkingCapitalLoansRequest(clientId) + .productId(productId) // + .submittedOnDate(submittedOnDate) // + .expectedDisbursementDate(submittedOnDate) // + .principalAmount(new BigDecimal("100")) // + .totalPayment(new BigDecimal("100")) // + .periodPaymentRate(new BigDecimal("1")) // + .discount(null) // .breachId(overrideBreachId); - String message = ErrorMessageHelper.overrideDisallowedByProductFailure(); - verifyCreateWorkingCapitalLoanAccountFailure(loansRequest, 400, message); - } - @Then("Admin failed to create working capital loan while breach override disallowed with breach and near breach override and the following data:") - public void createLoanWithBreachOverrideDisallowedWithBreachAndNearBreachFailure(final DataTable table) { - final List> data = table.asLists(); - final List loanData = data.get(1); - final Long overrideBreachId = createBreachOverrideAndGetId(); - final Long overrideNearBreachId = createNearBreachOverrideAndGetId(); + if (expectedStatus == 200) { + final PostWorkingCapitalLoansResponse response = ok( + () -> fineractClient.workingCapitalLoans().submitWorkingCapitalLoanApplication(request)); + assertThat(response).isNotNull(); + assertThat(response.getLoanId()).isNotNull(); + return; + } - final PostWorkingCapitalLoansRequest loansRequest = createWorkingCapitalLoanAccountWithBreachNearBreachRequest(loanData, - overrideBreachId, overrideNearBreachId); - String message = ErrorMessageHelper.overrideDisallowedByProductFailure(); - verifyCreateWorkingCapitalLoanAccountFailure(loansRequest, 400, message); + final CallFailedRuntimeException exception = fail( + () -> fineractClient.workingCapitalLoans().submitWorkingCapitalLoanApplication(request)); + assertThat(exception.getStatus()).as("HTTP status").isEqualTo(expectedStatus); } - @Then("Admin failed to create working capital loan while breach override disallowed with breach and near breach override and default following data:") - public void createLoanWithBreachOverrideDisallowedWithBreachAndNearBreachDefaultFailure(final DataTable table) { - final List> data = table.asLists(); - final List loanData = data.get(1); - final String loanProduct = loanData.get(0); - final String submittedOnDate = loanData.get(1); - - final Long overrideBreachId = createBreachOverrideAndGetId(); - final Long overrideNearBreachId = createNearBreachOverrideAndGetId(); - - final PostWorkingCapitalLoansRequest loansRequest = createWorkingCapitalLoanAccountDefaultRequest(loanProduct, submittedOnDate) - .breachId(overrideBreachId).nearBreachId(overrideNearBreachId); + @Then("Admin creates working capital loan with with breach on {string} date") + public void createLoanWithBreachOverrideAllowedWithBreachData(String submittedOnDate) { + final Long breachId = createBreachAndGetId(); - String message = ErrorMessageHelper.overrideDisallowedByProductFailure(); - verifyCreateWorkingCapitalLoanAccountFailure(loansRequest, 400, message); + final PostWorkingCapitalLoansRequest loansRequest = createWorkingCapitalLoanAccountDefaultRequest(submittedOnDate) + .breachId(breachId); + createWorkingCapitalLoanAccount(loansRequest); } - @Then("Admin failed to create WC loan account on {string} with breach {int} {string} frequency lower then near breach {int} {string} frequency") - public void createLoanWithBreachLowerThenNearBreachFailure(String submittedOnDate, int breachFrequency, String breachFrequencyType, - int nearBreachFrequency, String nearBreachFrequencyType) { - final Long breachId = createBreachAndGetId(breachFrequency, breachFrequencyType); - final Long nearBreachId = createNearBreachAndGetId(nearBreachFrequency, nearBreachFrequencyType); + public void checkCreateWCLoanAccountBreachData(Long breachId) { + final PostWorkingCapitalLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); + Long loanId = loanResponse.getLoanId(); - final PostWorkingCapitalLoansRequest loansRequest = createWorkingCapitalLoanAccountDefaultRequest(submittedOnDate) - .breachId(breachId).nearBreachId(nearBreachId); - String message = ErrorMessageHelper.nearBreachMustBeLowerThenBreachFailure(); - verifyCreateWorkingCapitalLoanAccountFailure(loansRequest, 400, message); + final GetWorkingCapitalLoansLoanIdResponse loanAccountResponse = retrieveLoanDetails(loanId); + assert loanAccountResponse.getBreach() != null; + assertThat(loanAccountResponse.getBreach().getId()).isEqualTo(breachId); + assertThat(loanAccountResponse.getNearBreach()).isNull(); } - @Then("Admin failed to create WC loan account on {string} without breach, but with near breach") - public void createLoanWithoutBreachButWithNearBreachFailure(String submittedOnDate) { - final Long nearBreachId = createNearBreachAndGetId(); + public void checkCreateWCLoanAccountBreachNearBreachData(Long breachId, Long nearBreachId) { + final PostWorkingCapitalLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); + Long loanId = loanResponse.getLoanId(); - final PostWorkingCapitalLoansRequest loansRequest = createWorkingCapitalLoanAccountDefaultRequest(submittedOnDate) - .nearBreachId(nearBreachId); - String message = ErrorMessageHelper.nearBreachCannotEnableWithoutBreachFailure(); - verifyCreateWorkingCapitalLoanAccountFailure(loansRequest, 400, message); + final GetWorkingCapitalLoansLoanIdResponse loanAccountResponse = retrieveLoanDetails(loanId); + assert loanAccountResponse.getBreach() != null; + assert loanAccountResponse.getNearBreach() != null; + assertThat(loanAccountResponse.getBreach().getId()).isEqualTo(breachId); + assertThat(loanAccountResponse.getNearBreach().getId()).isEqualTo(nearBreachId); } public PostWorkingCapitalLoansRequest createWorkingCapitalLoanAccountDefaultRequest(String submittedOnDate) { @@ -2097,15 +2200,11 @@ private String extractWcScheduleCellValue(final String headerName, final Project return switch (headerName) { case "paymentNo" -> period.getPaymentNo() == null ? null : period.getPaymentNo().toString(); case "paymentDate" -> period.getPaymentDate() == null ? null : FORMATTER.format(period.getPaymentDate()); - case "count" -> period.getCount() == null ? null : period.getCount().toString(); - case "paymentsLeft" -> period.getPaymentsLeft() == null ? null : period.getPaymentsLeft().toString(); case "expectedPaymentAmount" -> asText(period.getExpectedPaymentAmount()); - case "forecastPaymentAmount" -> asText(period.getForecastPaymentAmount()); case "discountFactor" -> asText(period.getDiscountFactor()); case "npvValue" -> asText(period.getNpvValue()); case "balance" -> asText(period.getBalance()); case "expectedAmortizationAmount" -> asText(period.getExpectedAmortizationAmount()); - case "netAmortizationAmount" -> asText(period.getNetAmortizationAmount()); case "actualPaymentAmount" -> asText(period.getActualPaymentAmount()); case "actualAmortizationAmount" -> asText(period.getActualAmortizationAmount()); case "incomeModification" -> asText(period.getIncomeModification()); @@ -2508,4 +2607,68 @@ private void assertTable(Class tClass, List header, List fineractClient.workingCapitalLoans().updateWorkingCapitalLoanRateById(loanId, rateChangeRequest)); + + assertThat(exception.getStatus()).as(errorMessage).isEqualTo(400); + assertThat(exception.getDeveloperMessage()).contains(errorMessage); + } + + public void checkWorkingCapitalPeriodPaymentRate(Long loanId, String periodPaymentRate) { + final GetWorkingCapitalLoansLoanIdResponse loanDetailsResponse = retrieveLoanDetails(loanId); + assert loanDetailsResponse.getPeriodPaymentRate() != null; + assertThat(loanDetailsResponse.getPeriodPaymentRate().compareTo(new BigDecimal(periodPaymentRate))).isZero(); + } + + public void checkPeriodPaymentRateChangeHistory(List> data, + List rateChanges, List header, String resourceId) { + checkPeriodPaymentRatesTabRows(data, rateChanges, header, resourceId); + assertThat(rateChanges.size()) + .as(ErrorMessageHelper.nrOfLinesWrongInTransactionsTab(resourceId, rateChanges.size(), data.size() - 1)) + .isEqualTo(data.size() - 1); + } + + public void checkPeriodPaymentRatesTabRows(List> data, List rateChanges, + List header, String resourceId) { + for (int i = 1; i < data.size(); i++) { + List expectedValues = data.get(i); + String transactionDateExpected = expectedValues.get(0); + List> actualValuesList = rateChanges.stream()// + .filter(rate -> transactionDateExpected.equals(FORMATTER.format(rate.getEffectiveDate())))// + .map(rate -> fetchValuesOfRateChangesHistory(header, rate))// + .collect(Collectors.toList());// + boolean containsExpectedValues = actualValuesList.stream()// + .anyMatch(actualValues -> actualValues.equals(expectedValues));// + assertThat(containsExpectedValues) + .as(ErrorMessageHelper.wrongValueInLineInTransactionsTab(resourceId, i, actualValuesList, expectedValues)).isTrue(); + } + } + + private List fetchValuesOfRateChangesHistory(List header, + WorkingCapitalLoanPeriodPaymentRateChangeData rateChangeData) { + List actualValues = new ArrayList<>(); + for (String headerName : header) { + switch (headerName) { + case "Effective Date" -> actualValues + .add(rateChangeData.getEffectiveDate() == null ? null : FORMATTER.format(rateChangeData.getEffectiveDate())); + case "Previous Rate" -> actualValues.add(rateChangeData.getPreviousRate() == null ? null + : new Utils.DoubleFormatter(rateChangeData.getPreviousRate().doubleValue()).format()); + case "New Rate" -> actualValues.add(rateChangeData.getNewRate() == null ? null + : new Utils.DoubleFormatter(rateChangeData.getNewRate().doubleValue()).format()); + case "Reversed" -> + actualValues.add(rateChangeData.getReversed() == null ? null : String.valueOf(rateChangeData.getReversed())); + default -> throw new IllegalStateException(String.format("Header name %s cannot be found", headerName)); + } + } + return actualValues; + } + } diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java index 9db44f7aea0..a51acea119a 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java @@ -332,6 +332,7 @@ public abstract class TestContextKey { public static final String DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE_WCLP_BREACH_NEAR_BREACH = "workingCapitalLoanProductCreateResponseWCLPBreachNearBreach"; public static final String DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE_WCLP_BREACH_DISALLOW_OVERRIDES = "workingCapitalLoanProductCreateResponseWCLPBreachDisallowOverrides"; public static final String DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE_WCLP_BREACH_NEAR_BREACH_DISALLOW_OVERRIDES = "workingCapitalLoanProductCreateResponseWCLPBreachNearBreachDisallowOverrides"; + public static final String DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE_WCLP_PERIOD_PAYMENT_RATE = "workingCapitalLoanProductCreateResponseWCLPPeriodPaymentRate"; public static final String WC_LOAN_IDS = "wcLoanIds"; public static final String DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_REQUEST_FOR_UPDATE_WCLP = "workingCapitalLoanProductCreateRequestForUpdateWCLP"; public static final String DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE_FOR_UPDATE_WCLP = "workingCapitalLoanProductCreateResponseForUpdateWCLP"; @@ -359,4 +360,5 @@ public abstract class TestContextKey { public static final String WORKING_CAPITAL_NEAR_BREACH_ID_FOR_UPDATE = "workingCapitalNearBreachIdForUpdate"; public static final String WORKING_CAPITAL_NEAR_BREACH_CREATE_REQUEST_FOR_UPDATE = "workingCapitalNearBreachCreateRequestForUpdate"; public static final String WC_LOAN_ACTION_TEMPLATE_RESPONSE = "wcLoanActionTemplateResponse"; + public static final String WORKING_CAPITAL_LOAN_RATE_CHANGE_ID = "wcLoanRateChangeId"; } diff --git a/fineract-e2e-tests-runner/src/test/java/org/apache/fineract/test/initializer/global/WorkingCapitalInitializerStep.java b/fineract-e2e-tests-runner/src/test/java/org/apache/fineract/test/initializer/global/WorkingCapitalInitializerStep.java index 955557875bf..e928d088732 100644 --- a/fineract-e2e-tests-runner/src/test/java/org/apache/fineract/test/initializer/global/WorkingCapitalInitializerStep.java +++ b/fineract-e2e-tests-runner/src/test/java/org/apache/fineract/test/initializer/global/WorkingCapitalInitializerStep.java @@ -95,10 +95,16 @@ public void initialize() throws Exception { .name(DefaultWorkingCapitalLoanProduct.WCLP_BREACH_DISALLOW_ATTRIBUTES_OVERRIDE.getName()))), () -> TestContext.INSTANCE.set( TestContextKey.DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE_WCLP_BREACH_NEAR_BREACH_DISALLOW_OVERRIDES, + createWorkingCapitalLoanProductIdempotent( + workingCapitalRequestFactory.defaultWorkingCapitalLoanProductBreachNearBreachRequest() + .allowAttributeOverrides(allowAttributeOverridesDisabled) + .name(DefaultWorkingCapitalLoanProduct.WCLP_BREACH_NEAR_BREACH_DISALLOW_ATTRIBUTES_OVERRIDE + .getName()))), + () -> TestContext.INSTANCE.set(TestContextKey.DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE_WCLP_PERIOD_PAYMENT_RATE, createWorkingCapitalLoanProductIdempotent(workingCapitalRequestFactory - .defaultWorkingCapitalLoanProductBreachNearBreachRequest() - .allowAttributeOverrides(allowAttributeOverridesDisabled) - .name(DefaultWorkingCapitalLoanProduct.WCLP_BREACH_NEAR_BREACH_DISALLOW_ATTRIBUTES_OVERRIDE.getName())))); + .defaultWorkingCapitalLoanProductAllowAttributesOverrideRequest().minPeriodPaymentRate(new BigDecimal(1)) + .maxPeriodPaymentRate(new BigDecimal(95)).periodPaymentRate(new BigDecimal(10)) + .name(DefaultWorkingCapitalLoanProduct.WCLP_PERIOD_PAYMENT_RATE.getName())))); ParallelExecutionHelper.runInParallel(items); } diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalAmortizationSchedule.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalAmortizationSchedule.feature index c845bd18471..a9d89d7823c 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalAmortizationSchedule.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalAmortizationSchedule.feature @@ -8,211 +8,676 @@ Feature: WorkingCapitalAmortizationSchedule And Admin creates a working capital loan with the following data: | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | | WCLP | 01 January 2026 | 01 January 2026 | 100 | 100 | 1 | 0 | - When Admin generates a projected amortization schedule with originationFeeAmount 1000.0, netDisbursementAmount 9000.0, totalPaymentValue 100000.0, periodPaymentRate 0.18, npvDayCount 360, expectedDisbursementDate "2019-01-01" + When Admin generates a projected amortization schedule with discountFeeAmount 1000.0, netDisbursementAmount 9000.0, totalPaymentValue 100000.0, periodPaymentRate 0.18, npvDayCount 360, expectedDisbursementDate "2019-01-01" And Admin retrieves the projected amortization schedule Then The retrieved amortization schedule has the following summary fields: - | originationFeeAmount | netDisbursementAmount | totalPaymentValue | periodPaymentRate | npvDayCount | expectedPaymentAmount | loanTerm | - | 1000.00 | 9000.00 | 100000.00 | 0.18 | 360 | 50.00 | 200 | + | discountFeeAmount | netDisbursementAmount | totalPaymentValue | periodPaymentRate | npvDayCount | expectedPaymentAmount | originalPaymentNumber | + | 1000.00 | 9000.00 | 100000.00 | 0.18 | 360 | 50.00 | 200 | And The retrieved amortization schedule has payments with the following details: - | paymentNo | date | paymentsLeft | expectedPaymentAmount | forecastPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | netAmortizationAmount | actualPaymentAmount | actualAmortizationAmount | incomeModification | deferredBalance | - | 0 | 2019-01-01 | 0 | -9000.00 | | 1 | -9000.00 | 9000.00 | | | | | | 1000.00 | - | 1 | 2019-01-02 | 1 | 50.00 | 50.00 | | 49.95 | 8959.61 | 9.61 | 0.00 | | | -9.61 | 1000.00 | - | 2 | 2019-01-03 | 2 | 50.00 | 50.00 | | 49.89 | 8919.18 | 9.57 | 0.00 | | | -9.57 | 1000.00 | - | 3 | 2019-01-04 | 3 | 50.00 | 50.00 | | 49.84 | 8878.70 | 9.52 | 0.00 | | | -9.52 | 1000.00 | - | 4 | 2019-01-05 | 4 | 50.00 | 50.00 | | 49.79 | 8838.18 | 9.48 | 0.00 | | | -9.48 | 1000.00 | - | 5 | 2019-01-06 | 5 | 50.00 | 50.00 | | 49.73 | 8797.62 | 9.44 | 0.00 | | | -9.44 | 1000.00 | - | 6 | 2019-01-07 | 6 | 50.00 | 50.00 | | 49.68 | 8757.01 | 9.39 | 0.00 | | | -9.39 | 1000.00 | - | 7 | 2019-01-08 | 7 | 50.00 | 50.00 | | 49.63 | 8716.36 | 9.35 | 0.00 | | | -9.35 | 1000.00 | - | 8 | 2019-01-09 | 8 | 50.00 | 50.00 | | 49.57 | 8675.67 | 9.31 | 0.00 | | | -9.31 | 1000.00 | - | 9 | 2019-01-10 | 9 | 50.00 | 50.00 | | 49.52 | 8634.94 | 9.26 | 0.00 | | | -9.26 | 1000.00 | - | 10 | 2019-01-11 | 10 | 50.00 | 50.00 | | 49.47 | 8594.16 | 9.22 | 0.00 | | | -9.22 | 1000.00 | - | 11 | 2019-01-12 | 11 | 50.00 | 50.00 | | 49.42 | 8553.33 | 9.18 | 0.00 | | | -9.18 | 1000.00 | - | 12 | 2019-01-13 | 12 | 50.00 | 50.00 | | 49.36 | 8512.47 | 9.13 | 0.00 | | | -9.13 | 1000.00 | - | 13 | 2019-01-14 | 13 | 50.00 | 50.00 | | 49.31 | 8471.56 | 9.09 | 0.00 | | | -9.09 | 1000.00 | - | 14 | 2019-01-15 | 14 | 50.00 | 50.00 | | 49.26 | 8430.60 | 9.05 | 0.00 | | | -9.05 | 1000.00 | - | 15 | 2019-01-16 | 15 | 50.00 | 50.00 | | 49.21 | 8389.61 | 9.00 | 0.00 | | | -9.00 | 1000.00 | - | 16 | 2019-01-17 | 16 | 50.00 | 50.00 | | 49.15 | 8348.56 | 8.96 | 0.00 | | | -8.96 | 1000.00 | - | 17 | 2019-01-18 | 17 | 50.00 | 50.00 | | 49.10 | 8307.48 | 8.91 | 0.00 | | | -8.91 | 1000.00 | - | 18 | 2019-01-19 | 18 | 50.00 | 50.00 | | 49.05 | 8266.35 | 8.87 | 0.00 | | | -8.87 | 1000.00 | - | 19 | 2019-01-20 | 19 | 50.00 | 50.00 | | 49.00 | 8225.18 | 8.83 | 0.00 | | | -8.83 | 1000.00 | - | 20 | 2019-01-21 | 20 | 50.00 | 50.00 | | 48.94 | 8183.96 | 8.78 | 0.00 | | | -8.78 | 1000.00 | - | 21 | 2019-01-22 | 21 | 50.00 | 50.00 | | 48.89 | 8142.70 | 8.74 | 0.00 | | | -8.74 | 1000.00 | - | 22 | 2019-01-23 | 22 | 50.00 | 50.00 | | 48.84 | 8101.39 | 8.69 | 0.00 | | | -8.69 | 1000.00 | - | 23 | 2019-01-24 | 23 | 50.00 | 50.00 | | 48.79 | 8060.04 | 8.65 | 0.00 | | | -8.65 | 1000.00 | - | 24 | 2019-01-25 | 24 | 50.00 | 50.00 | | 48.74 | 8018.65 | 8.61 | 0.00 | | | -8.61 | 1000.00 | - | 25 | 2019-01-26 | 25 | 50.00 | 50.00 | | 48.68 | 7977.21 | 8.56 | 0.00 | | | -8.56 | 1000.00 | - | 26 | 2019-01-27 | 26 | 50.00 | 50.00 | | 48.63 | 7935.73 | 8.52 | 0.00 | | | -8.52 | 1000.00 | - | 27 | 2019-01-28 | 27 | 50.00 | 50.00 | | 48.58 | 7894.21 | 8.47 | 0.00 | | | -8.47 | 1000.00 | - | 28 | 2019-01-29 | 28 | 50.00 | 50.00 | | 48.53 | 7852.63 | 8.43 | 0.00 | | | -8.43 | 1000.00 | - | 29 | 2019-01-30 | 29 | 50.00 | 50.00 | | 48.48 | 7811.02 | 8.39 | 0.00 | | | -8.39 | 1000.00 | - | 30 | 2019-01-31 | 30 | 50.00 | 50.00 | | 48.42 | 7769.36 | 8.34 | 0.00 | | | -8.34 | 1000.00 | - | 31 | 2019-02-01 | 31 | 50.00 | 50.00 | | 48.37 | 7727.66 | 8.30 | 0.00 | | | -8.30 | 1000.00 | - | 32 | 2019-02-02 | 32 | 50.00 | 50.00 | | 48.32 | 7685.91 | 8.25 | 0.00 | | | -8.25 | 1000.00 | - | 33 | 2019-02-03 | 33 | 50.00 | 50.00 | | 48.27 | 7644.12 | 8.21 | 0.00 | | | -8.21 | 1000.00 | - | 34 | 2019-02-04 | 34 | 50.00 | 50.00 | | 48.22 | 7602.28 | 8.16 | 0.00 | | | -8.16 | 1000.00 | - | 35 | 2019-02-05 | 35 | 50.00 | 50.00 | | 48.17 | 7560.40 | 8.12 | 0.00 | | | -8.12 | 1000.00 | - | 36 | 2019-02-06 | 36 | 50.00 | 50.00 | | 48.12 | 7518.47 | 8.07 | 0.00 | | | -8.07 | 1000.00 | - | 37 | 2019-02-07 | 37 | 50.00 | 50.00 | | 48.06 | 7476.50 | 8.03 | 0.00 | | | -8.03 | 1000.00 | - | 38 | 2019-02-08 | 38 | 50.00 | 50.00 | | 48.01 | 7434.48 | 7.98 | 0.00 | | | -7.98 | 1000.00 | - | 39 | 2019-02-09 | 39 | 50.00 | 50.00 | | 47.96 | 7392.42 | 7.94 | 0.00 | | | -7.94 | 1000.00 | - | 40 | 2019-02-10 | 40 | 50.00 | 50.00 | | 47.91 | 7350.31 | 7.89 | 0.00 | | | -7.89 | 1000.00 | - | 41 | 2019-02-11 | 41 | 50.00 | 50.00 | | 47.86 | 7308.16 | 7.85 | 0.00 | | | -7.85 | 1000.00 | - | 42 | 2019-02-12 | 42 | 50.00 | 50.00 | | 47.81 | 7265.97 | 7.80 | 0.00 | | | -7.80 | 1000.00 | - | 43 | 2019-02-13 | 43 | 50.00 | 50.00 | | 47.76 | 7223.72 | 7.76 | 0.00 | | | -7.76 | 1000.00 | - | 44 | 2019-02-14 | 44 | 50.00 | 50.00 | | 47.71 | 7181.44 | 7.71 | 0.00 | | | -7.71 | 1000.00 | - | 45 | 2019-02-15 | 45 | 50.00 | 50.00 | | 47.66 | 7139.11 | 7.67 | 0.00 | | | -7.67 | 1000.00 | - | 46 | 2019-02-16 | 46 | 50.00 | 50.00 | | 47.60 | 7096.73 | 7.62 | 0.00 | | | -7.62 | 1000.00 | - | 47 | 2019-02-17 | 47 | 50.00 | 50.00 | | 47.55 | 7054.31 | 7.58 | 0.00 | | | -7.58 | 1000.00 | - | 48 | 2019-02-18 | 48 | 50.00 | 50.00 | | 47.50 | 7011.84 | 7.53 | 0.00 | | | -7.53 | 1000.00 | - | 49 | 2019-02-19 | 49 | 50.00 | 50.00 | | 47.45 | 6969.33 | 7.49 | 0.00 | | | -7.49 | 1000.00 | - | 50 | 2019-02-20 | 50 | 50.00 | 50.00 | | 47.40 | 6926.77 | 7.44 | 0.00 | | | -7.44 | 1000.00 | - | 51 | 2019-02-21 | 51 | 50.00 | 50.00 | | 47.35 | 6884.17 | 7.40 | 0.00 | | | -7.40 | 1000.00 | - | 52 | 2019-02-22 | 52 | 50.00 | 50.00 | | 47.30 | 6841.52 | 7.35 | 0.00 | | | -7.35 | 1000.00 | - | 53 | 2019-02-23 | 53 | 50.00 | 50.00 | | 47.25 | 6798.82 | 7.31 | 0.00 | | | -7.31 | 1000.00 | - | 54 | 2019-02-24 | 54 | 50.00 | 50.00 | | 47.20 | 6756.08 | 7.26 | 0.00 | | | -7.26 | 1000.00 | - | 55 | 2019-02-25 | 55 | 50.00 | 50.00 | | 47.15 | 6713.30 | 7.21 | 0.00 | | | -7.21 | 1000.00 | - | 56 | 2019-02-26 | 56 | 50.00 | 50.00 | | 47.10 | 6670.47 | 7.17 | 0.00 | | | -7.17 | 1000.00 | - | 57 | 2019-02-27 | 57 | 50.00 | 50.00 | | 47.05 | 6627.59 | 7.12 | 0.00 | | | -7.12 | 1000.00 | - | 58 | 2019-02-28 | 58 | 50.00 | 50.00 | | 47.00 | 6584.67 | 7.08 | 0.00 | | | -7.08 | 1000.00 | - | 59 | 2019-03-01 | 59 | 50.00 | 50.00 | | 46.95 | 6541.70 | 7.03 | 0.00 | | | -7.03 | 1000.00 | - | 60 | 2019-03-02 | 60 | 50.00 | 50.00 | | 46.90 | 6498.68 | 6.99 | 0.00 | | | -6.99 | 1000.00 | - | 61 | 2019-03-03 | 61 | 50.00 | 50.00 | | 46.85 | 6455.62 | 6.94 | 0.00 | | | -6.94 | 1000.00 | - | 62 | 2019-03-04 | 62 | 50.00 | 50.00 | | 46.80 | 6412.51 | 6.89 | 0.00 | | | -6.89 | 1000.00 | - | 63 | 2019-03-05 | 63 | 50.00 | 50.00 | | 46.75 | 6369.36 | 6.85 | 0.00 | | | -6.85 | 1000.00 | - | 64 | 2019-03-06 | 64 | 50.00 | 50.00 | | 46.70 | 6326.16 | 6.80 | 0.00 | | | -6.80 | 1000.00 | - | 65 | 2019-03-07 | 65 | 50.00 | 50.00 | | 46.65 | 6282.92 | 6.76 | 0.00 | | | -6.76 | 1000.00 | - | 66 | 2019-03-08 | 66 | 50.00 | 50.00 | | 46.60 | 6239.63 | 6.71 | 0.00 | | | -6.71 | 1000.00 | - | 67 | 2019-03-09 | 67 | 50.00 | 50.00 | | 46.55 | 6196.29 | 6.66 | 0.00 | | | -6.66 | 1000.00 | - | 68 | 2019-03-10 | 68 | 50.00 | 50.00 | | 46.50 | 6152.91 | 6.62 | 0.00 | | | -6.62 | 1000.00 | - | 69 | 2019-03-11 | 69 | 50.00 | 50.00 | | 46.45 | 6109.48 | 6.57 | 0.00 | | | -6.57 | 1000.00 | - | 70 | 2019-03-12 | 70 | 50.00 | 50.00 | | 46.40 | 6066.00 | 6.52 | 0.00 | | | -6.52 | 1000.00 | - | 71 | 2019-03-13 | 71 | 50.00 | 50.00 | | 46.35 | 6022.48 | 6.48 | 0.00 | | | -6.48 | 1000.00 | - | 72 | 2019-03-14 | 72 | 50.00 | 50.00 | | 46.30 | 5978.91 | 6.43 | 0.00 | | | -6.43 | 1000.00 | - | 73 | 2019-03-15 | 73 | 50.00 | 50.00 | | 46.25 | 5935.29 | 6.38 | 0.00 | | | -6.38 | 1000.00 | - | 74 | 2019-03-16 | 74 | 50.00 | 50.00 | | 46.20 | 5891.63 | 6.34 | 0.00 | | | -6.34 | 1000.00 | - | 75 | 2019-03-17 | 75 | 50.00 | 50.00 | | 46.15 | 5847.92 | 6.29 | 0.00 | | | -6.29 | 1000.00 | - | 76 | 2019-03-18 | 76 | 50.00 | 50.00 | | 46.10 | 5804.17 | 6.24 | 0.00 | | | -6.24 | 1000.00 | - | 77 | 2019-03-19 | 77 | 50.00 | 50.00 | | 46.06 | 5760.36 | 6.20 | 0.00 | | | -6.20 | 1000.00 | - | 78 | 2019-03-20 | 78 | 50.00 | 50.00 | | 46.01 | 5716.52 | 6.15 | 0.00 | | | -6.15 | 1000.00 | - | 79 | 2019-03-21 | 79 | 50.00 | 50.00 | | 45.96 | 5672.62 | 6.10 | 0.00 | | | -6.10 | 1000.00 | - | 80 | 2019-03-22 | 80 | 50.00 | 50.00 | | 45.91 | 5628.68 | 6.06 | 0.00 | | | -6.06 | 1000.00 | - | 81 | 2019-03-23 | 81 | 50.00 | 50.00 | | 45.86 | 5584.69 | 6.01 | 0.00 | | | -6.01 | 1000.00 | - | 82 | 2019-03-24 | 82 | 50.00 | 50.00 | | 45.81 | 5540.65 | 5.96 | 0.00 | | | -5.96 | 1000.00 | - | 83 | 2019-03-25 | 83 | 50.00 | 50.00 | | 45.76 | 5496.57 | 5.92 | 0.00 | | | -5.92 | 1000.00 | - | 84 | 2019-03-26 | 84 | 50.00 | 50.00 | | 45.71 | 5452.44 | 5.87 | 0.00 | | | -5.87 | 1000.00 | - | 85 | 2019-03-27 | 85 | 50.00 | 50.00 | | 45.66 | 5408.26 | 5.82 | 0.00 | | | -5.82 | 1000.00 | - | 86 | 2019-03-28 | 86 | 50.00 | 50.00 | | 45.62 | 5364.03 | 5.78 | 0.00 | | | -5.78 | 1000.00 | - | 87 | 2019-03-29 | 87 | 50.00 | 50.00 | | 45.57 | 5319.76 | 5.73 | 0.00 | | | -5.73 | 1000.00 | - | 88 | 2019-03-30 | 88 | 50.00 | 50.00 | | 45.52 | 5275.44 | 5.68 | 0.00 | | | -5.68 | 1000.00 | - | 89 | 2019-03-31 | 89 | 50.00 | 50.00 | | 45.47 | 5231.08 | 5.63 | 0.00 | | | -5.63 | 1000.00 | - | 90 | 2019-04-01 | 90 | 50.00 | 50.00 | | 45.42 | 5186.66 | 5.59 | 0.00 | | | -5.59 | 1000.00 | - | 91 | 2019-04-02 | 91 | 50.00 | 50.00 | | 45.37 | 5142.20 | 5.54 | 0.00 | | | -5.54 | 1000.00 | - | 92 | 2019-04-03 | 92 | 50.00 | 50.00 | | 45.32 | 5097.69 | 5.49 | 0.00 | | | -5.49 | 1000.00 | - | 93 | 2019-04-04 | 93 | 50.00 | 50.00 | | 45.28 | 5053.13 | 5.44 | 0.00 | | | -5.44 | 1000.00 | - | 94 | 2019-04-05 | 94 | 50.00 | 50.00 | | 45.23 | 5008.53 | 5.40 | 0.00 | | | -5.40 | 1000.00 | - | 95 | 2019-04-06 | 95 | 50.00 | 50.00 | | 45.18 | 4963.88 | 5.35 | 0.00 | | | -5.35 | 1000.00 | - | 96 | 2019-04-07 | 96 | 50.00 | 50.00 | | 45.13 | 4919.18 | 5.30 | 0.00 | | | -5.30 | 1000.00 | - | 97 | 2019-04-08 | 97 | 50.00 | 50.00 | | 45.08 | 4874.43 | 5.25 | 0.00 | | | -5.25 | 1000.00 | - | 98 | 2019-04-09 | 98 | 50.00 | 50.00 | | 45.03 | 4829.64 | 5.20 | 0.00 | | | -5.20 | 1000.00 | - | 99 | 2019-04-10 | 99 | 50.00 | 50.00 | | 44.99 | 4784.79 | 5.16 | 0.00 | | | -5.16 | 1000.00 | - | 100 | 2019-04-11 | 100 | 50.00 | 50.00 | | 44.94 | 4739.90 | 5.11 | 0.00 | | | -5.11 | 1000.00 | - | 101 | 2019-04-12 | 101 | 50.00 | 50.00 | | 44.89 | 4694.96 | 5.06 | 0.00 | | | -5.06 | 1000.00 | - | 102 | 2019-04-13 | 102 | 50.00 | 50.00 | | 44.84 | 4649.98 | 5.01 | 0.00 | | | -5.01 | 1000.00 | - | 103 | 2019-04-14 | 103 | 50.00 | 50.00 | | 44.80 | 4604.94 | 4.97 | 0.00 | | | -4.97 | 1000.00 | - | 104 | 2019-04-15 | 104 | 50.00 | 50.00 | | 44.75 | 4559.86 | 4.92 | 0.00 | | | -4.92 | 1000.00 | - | 105 | 2019-04-16 | 105 | 50.00 | 50.00 | | 44.70 | 4514.73 | 4.87 | 0.00 | | | -4.87 | 1000.00 | - | 106 | 2019-04-17 | 106 | 50.00 | 50.00 | | 44.65 | 4469.55 | 4.82 | 0.00 | | | -4.82 | 1000.00 | - | 107 | 2019-04-18 | 107 | 50.00 | 50.00 | | 44.60 | 4424.32 | 4.77 | 0.00 | | | -4.77 | 1000.00 | - | 108 | 2019-04-19 | 108 | 50.00 | 50.00 | | 44.56 | 4379.05 | 4.72 | 0.00 | | | -4.72 | 1000.00 | - | 109 | 2019-04-20 | 109 | 50.00 | 50.00 | | 44.51 | 4333.72 | 4.68 | 0.00 | | | -4.68 | 1000.00 | - | 110 | 2019-04-21 | 110 | 50.00 | 50.00 | | 44.46 | 4288.35 | 4.63 | 0.00 | | | -4.63 | 1000.00 | - | 111 | 2019-04-22 | 111 | 50.00 | 50.00 | | 44.41 | 4242.93 | 4.58 | 0.00 | | | -4.58 | 1000.00 | - | 112 | 2019-04-23 | 112 | 50.00 | 50.00 | | 44.37 | 4197.46 | 4.53 | 0.00 | | | -4.53 | 1000.00 | - | 113 | 2019-04-24 | 113 | 50.00 | 50.00 | | 44.32 | 4151.94 | 4.48 | 0.00 | | | -4.48 | 1000.00 | - | 114 | 2019-04-25 | 114 | 50.00 | 50.00 | | 44.27 | 4106.38 | 4.43 | 0.00 | | | -4.43 | 1000.00 | - | 115 | 2019-04-26 | 115 | 50.00 | 50.00 | | 44.22 | 4060.76 | 4.38 | 0.00 | | | -4.38 | 1000.00 | - | 116 | 2019-04-27 | 116 | 50.00 | 50.00 | | 44.18 | 4015.10 | 4.34 | 0.00 | | | -4.34 | 1000.00 | - | 117 | 2019-04-28 | 117 | 50.00 | 50.00 | | 44.13 | 3969.38 | 4.29 | 0.00 | | | -4.29 | 1000.00 | - | 118 | 2019-04-29 | 118 | 50.00 | 50.00 | | 44.08 | 3923.62 | 4.24 | 0.00 | | | -4.24 | 1000.00 | - | 119 | 2019-04-30 | 119 | 50.00 | 50.00 | | 44.04 | 3877.81 | 4.19 | 0.00 | | | -4.19 | 1000.00 | - | 120 | 2019-05-01 | 120 | 50.00 | 50.00 | | 43.99 | 3831.95 | 4.14 | 0.00 | | | -4.14 | 1000.00 | - | 121 | 2019-05-02 | 121 | 50.00 | 50.00 | | 43.94 | 3786.04 | 4.09 | 0.00 | | | -4.09 | 1000.00 | - | 122 | 2019-05-03 | 122 | 50.00 | 50.00 | | 43.90 | 3740.09 | 4.04 | 0.00 | | | -4.04 | 1000.00 | - | 123 | 2019-05-04 | 123 | 50.00 | 50.00 | | 43.85 | 3694.08 | 3.99 | 0.00 | | | -3.99 | 1000.00 | - | 124 | 2019-05-05 | 124 | 50.00 | 50.00 | | 43.80 | 3648.03 | 3.94 | 0.00 | | | -3.94 | 1000.00 | - | 125 | 2019-05-06 | 125 | 50.00 | 50.00 | | 43.76 | 3601.92 | 3.90 | 0.00 | | | -3.90 | 1000.00 | - | 126 | 2019-05-07 | 126 | 50.00 | 50.00 | | 43.71 | 3555.77 | 3.85 | 0.00 | | | -3.85 | 1000.00 | - | 127 | 2019-05-08 | 127 | 50.00 | 50.00 | | 43.66 | 3509.56 | 3.80 | 0.00 | | | -3.80 | 1000.00 | - | 128 | 2019-05-09 | 128 | 50.00 | 50.00 | | 43.62 | 3463.31 | 3.75 | 0.00 | | | -3.75 | 1000.00 | - | 129 | 2019-05-10 | 129 | 50.00 | 50.00 | | 43.57 | 3417.01 | 3.70 | 0.00 | | | -3.70 | 1000.00 | - | 130 | 2019-05-11 | 130 | 50.00 | 50.00 | | 43.52 | 3370.66 | 3.65 | 0.00 | | | -3.65 | 1000.00 | - | 131 | 2019-05-12 | 131 | 50.00 | 50.00 | | 43.48 | 3324.26 | 3.60 | 0.00 | | | -3.60 | 1000.00 | - | 132 | 2019-05-13 | 132 | 50.00 | 50.00 | | 43.43 | 3277.81 | 3.55 | 0.00 | | | -3.55 | 1000.00 | - | 133 | 2019-05-14 | 133 | 50.00 | 50.00 | | 43.38 | 3231.31 | 3.50 | 0.00 | | | -3.50 | 1000.00 | - | 134 | 2019-05-15 | 134 | 50.00 | 50.00 | | 43.34 | 3184.76 | 3.45 | 0.00 | | | -3.45 | 1000.00 | - | 135 | 2019-05-16 | 135 | 50.00 | 50.00 | | 43.29 | 3138.16 | 3.40 | 0.00 | | | -3.40 | 1000.00 | - | 136 | 2019-05-17 | 136 | 50.00 | 50.00 | | 43.24 | 3091.51 | 3.35 | 0.00 | | | -3.35 | 1000.00 | - | 137 | 2019-05-18 | 137 | 50.00 | 50.00 | | 43.20 | 3044.81 | 3.30 | 0.00 | | | -3.30 | 1000.00 | - | 138 | 2019-05-19 | 138 | 50.00 | 50.00 | | 43.15 | 2998.06 | 3.25 | 0.00 | | | -3.25 | 1000.00 | - | 139 | 2019-05-20 | 139 | 50.00 | 50.00 | | 43.11 | 2951.26 | 3.20 | 0.00 | | | -3.20 | 1000.00 | - | 140 | 2019-05-21 | 140 | 50.00 | 50.00 | | 43.06 | 2904.42 | 3.15 | 0.00 | | | -3.15 | 1000.00 | - | 141 | 2019-05-22 | 141 | 50.00 | 50.00 | | 43.01 | 2857.52 | 3.10 | 0.00 | | | -3.10 | 1000.00 | - | 142 | 2019-05-23 | 142 | 50.00 | 50.00 | | 42.97 | 2810.57 | 3.05 | 0.00 | | | -3.05 | 1000.00 | - | 143 | 2019-05-24 | 143 | 50.00 | 50.00 | | 42.92 | 2763.57 | 3.00 | 0.00 | | | -3.00 | 1000.00 | - | 144 | 2019-05-25 | 144 | 50.00 | 50.00 | | 42.88 | 2716.52 | 2.95 | 0.00 | | | -2.95 | 1000.00 | - | 145 | 2019-05-26 | 145 | 50.00 | 50.00 | | 42.83 | 2669.42 | 2.90 | 0.00 | | | -2.90 | 1000.00 | - | 146 | 2019-05-27 | 146 | 50.00 | 50.00 | | 42.79 | 2622.27 | 2.85 | 0.00 | | | -2.85 | 1000.00 | - | 147 | 2019-05-28 | 147 | 50.00 | 50.00 | | 42.74 | 2575.07 | 2.80 | 0.00 | | | -2.80 | 1000.00 | - | 148 | 2019-05-29 | 148 | 50.00 | 50.00 | | 42.69 | 2527.82 | 2.75 | 0.00 | | | -2.75 | 1000.00 | - | 149 | 2019-05-30 | 149 | 50.00 | 50.00 | | 42.65 | 2480.52 | 2.70 | 0.00 | | | -2.70 | 1000.00 | - | 150 | 2019-05-31 | 150 | 50.00 | 50.00 | | 42.60 | 2433.17 | 2.65 | 0.00 | | | -2.65 | 1000.00 | - | 151 | 2019-06-01 | 151 | 50.00 | 50.00 | | 42.56 | 2385.77 | 2.60 | 0.00 | | | -2.60 | 1000.00 | - | 152 | 2019-06-02 | 152 | 50.00 | 50.00 | | 42.51 | 2338.31 | 2.55 | 0.00 | | | -2.55 | 1000.00 | - | 153 | 2019-06-03 | 153 | 50.00 | 50.00 | | 42.47 | 2290.81 | 2.50 | 0.00 | | | -2.50 | 1000.00 | - | 154 | 2019-06-04 | 154 | 50.00 | 50.00 | | 42.42 | 2243.26 | 2.45 | 0.00 | | | -2.45 | 1000.00 | - | 155 | 2019-06-05 | 155 | 50.00 | 50.00 | | 42.38 | 2195.65 | 2.40 | 0.00 | | | -2.40 | 1000.00 | - | 156 | 2019-06-06 | 156 | 50.00 | 50.00 | | 42.33 | 2148.00 | 2.34 | 0.00 | | | -2.34 | 1000.00 | - | 157 | 2019-06-07 | 157 | 50.00 | 50.00 | | 42.29 | 2100.29 | 2.29 | 0.00 | | | -2.29 | 1000.00 | - | 158 | 2019-06-08 | 158 | 50.00 | 50.00 | | 42.24 | 2052.53 | 2.24 | 0.00 | | | -2.24 | 1000.00 | - | 159 | 2019-06-09 | 159 | 50.00 | 50.00 | | 42.20 | 2004.73 | 2.19 | 0.00 | | | -2.19 | 1000.00 | - | 160 | 2019-06-10 | 160 | 50.00 | 50.00 | | 42.15 | 1956.87 | 2.14 | 0.00 | | | -2.14 | 1000.00 | - | 161 | 2019-06-11 | 161 | 50.00 | 50.00 | | 42.11 | 1908.96 | 2.09 | 0.00 | | | -2.09 | 1000.00 | - | 162 | 2019-06-12 | 162 | 50.00 | 50.00 | | 42.06 | 1860.99 | 2.04 | 0.00 | | | -2.04 | 1000.00 | - | 163 | 2019-06-13 | 163 | 50.00 | 50.00 | | 42.02 | 1812.98 | 1.99 | 0.00 | | | -1.99 | 1000.00 | - | 164 | 2019-06-14 | 164 | 50.00 | 50.00 | | 41.97 | 1764.92 | 1.94 | 0.00 | | | -1.94 | 1000.00 | - | 165 | 2019-06-15 | 165 | 50.00 | 50.00 | | 41.93 | 1716.80 | 1.88 | 0.00 | | | -1.88 | 1000.00 | - | 166 | 2019-06-16 | 166 | 50.00 | 50.00 | | 41.88 | 1668.64 | 1.83 | 0.00 | | | -1.83 | 1000.00 | - | 167 | 2019-06-17 | 167 | 50.00 | 50.00 | | 41.84 | 1620.42 | 1.78 | 0.00 | | | -1.78 | 1000.00 | - | 168 | 2019-06-18 | 168 | 50.00 | 50.00 | | 41.79 | 1572.15 | 1.73 | 0.00 | | | -1.73 | 1000.00 | - | 169 | 2019-06-19 | 169 | 50.00 | 50.00 | | 41.75 | 1523.83 | 1.68 | 0.00 | | | -1.68 | 1000.00 | - | 170 | 2019-06-20 | 170 | 50.00 | 50.00 | | 41.70 | 1475.45 | 1.63 | 0.00 | | | -1.63 | 1000.00 | - | 171 | 2019-06-21 | 171 | 50.00 | 50.00 | | 41.66 | 1427.03 | 1.58 | 0.00 | | | -1.58 | 1000.00 | - | 172 | 2019-06-22 | 172 | 50.00 | 50.00 | | 41.61 | 1378.55 | 1.52 | 0.00 | | | -1.52 | 1000.00 | - | 173 | 2019-06-23 | 173 | 50.00 | 50.00 | | 41.57 | 1330.02 | 1.47 | 0.00 | | | -1.47 | 1000.00 | - | 174 | 2019-06-24 | 174 | 50.00 | 50.00 | | 41.53 | 1281.45 | 1.42 | 0.00 | | | -1.42 | 1000.00 | - | 175 | 2019-06-25 | 175 | 50.00 | 50.00 | | 41.48 | 1232.81 | 1.37 | 0.00 | | | -1.37 | 1000.00 | - | 176 | 2019-06-26 | 176 | 50.00 | 50.00 | | 41.44 | 1184.13 | 1.32 | 0.00 | | | -1.32 | 1000.00 | - | 177 | 2019-06-27 | 177 | 50.00 | 50.00 | | 41.39 | 1135.39 | 1.26 | 0.00 | | | -1.26 | 1000.00 | - | 178 | 2019-06-28 | 178 | 50.00 | 50.00 | | 41.35 | 1086.61 | 1.21 | 0.00 | | | -1.21 | 1000.00 | - | 179 | 2019-06-29 | 179 | 50.00 | 50.00 | | 41.31 | 1037.77 | 1.16 | 0.00 | | | -1.16 | 1000.00 | - | 180 | 2019-06-30 | 180 | 50.00 | 50.00 | | 41.26 | 988.88 | 1.11 | 0.00 | | | -1.11 | 1000.00 | - | 181 | 2019-07-01 | 181 | 50.00 | 50.00 | | 41.22 | 939.93 | 1.06 | 0.00 | | | -1.06 | 1000.00 | - | 182 | 2019-07-02 | 182 | 50.00 | 50.00 | | 41.17 | 890.93 | 1.00 | 0.00 | | | -1.00 | 1000.00 | - | 183 | 2019-07-03 | 183 | 50.00 | 50.00 | | 41.13 | 841.89 | 0.95 | 0.00 | | | -0.95 | 1000.00 | - | 184 | 2019-07-04 | 184 | 50.00 | 50.00 | | 41.09 | 792.79 | 0.90 | 0.00 | | | -0.90 | 1000.00 | - | 185 | 2019-07-05 | 185 | 50.00 | 50.00 | | 41.04 | 743.63 | 0.85 | 0.00 | | | -0.85 | 1000.00 | - | 186 | 2019-07-06 | 186 | 50.00 | 50.00 | | 41.00 | 694.43 | 0.79 | 0.00 | | | -0.79 | 1000.00 | - | 187 | 2019-07-07 | 187 | 50.00 | 50.00 | | 40.95 | 645.17 | 0.74 | 0.00 | | | -0.74 | 1000.00 | - | 188 | 2019-07-08 | 188 | 50.00 | 50.00 | | 40.91 | 595.86 | 0.69 | 0.00 | | | -0.69 | 1000.00 | - | 189 | 2019-07-09 | 189 | 50.00 | 50.00 | | 40.87 | 546.49 | 0.64 | 0.00 | | | -0.64 | 1000.00 | - | 190 | 2019-07-10 | 190 | 50.00 | 50.00 | | 40.82 | 497.08 | 0.58 | 0.00 | | | -0.58 | 1000.00 | - | 191 | 2019-07-11 | 191 | 50.00 | 50.00 | | 40.78 | 447.61 | 0.53 | 0.00 | | | -0.53 | 1000.00 | - | 192 | 2019-07-12 | 192 | 50.00 | 50.00 | | 40.74 | 398.08 | 0.48 | 0.00 | | | -0.48 | 1000.00 | - | 193 | 2019-07-13 | 193 | 50.00 | 50.00 | | 40.69 | 348.51 | 0.43 | 0.00 | | | -0.43 | 1000.00 | - | 194 | 2019-07-14 | 194 | 50.00 | 50.00 | | 40.65 | 298.88 | 0.37 | 0.00 | | | -0.37 | 1000.00 | - | 195 | 2019-07-15 | 195 | 50.00 | 50.00 | | 40.61 | 249.20 | 0.32 | 0.00 | | | -0.32 | 1000.00 | - | 196 | 2019-07-16 | 196 | 50.00 | 50.00 | | 40.56 | 199.47 | 0.27 | 0.00 | | | -0.27 | 1000.00 | - | 197 | 2019-07-17 | 197 | 50.00 | 50.00 | | 40.52 | 149.68 | 0.21 | 0.00 | | | -0.21 | 1000.00 | - | 198 | 2019-07-18 | 198 | 50.00 | 50.00 | | 40.48 | 99.84 | 0.16 | 0.00 | | | -0.16 | 1000.00 | - | 199 | 2019-07-19 | 199 | 50.00 | 50.00 | | 40.43 | 49.95 | 0.11 | 0.00 | | | -0.11 | 1000.00 | - | 200 | 2019-07-20 | 200 | 50.00 | 50.00 | | 40.39 | 0.00 | 0.05 | 0.00 | | | -0.05 | 1000.00 | + | paymentNo | date | expectedPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | actualPaymentAmount | actualAmortizationAmount | incomeModification | deferredBalance | + | 0 | 2019-01-01 | -9000.00 | 1 | -9000.00 | 9000.00 | | | | | 1000.00 | + | 1 | 2019-01-02 | 50.00 | | 49.95 | 8959.61 | 9.61 | | | | 1000.00 | + | 2 | 2019-01-03 | 50.00 | | 49.89 | 8919.18 | 9.57 | | | | 1000.00 | + | 3 | 2019-01-04 | 50.00 | | 49.84 | 8878.70 | 9.52 | | | | 1000.00 | + | 4 | 2019-01-05 | 50.00 | | 49.79 | 8838.18 | 9.48 | | | | 1000.00 | + | 5 | 2019-01-06 | 50.00 | | 49.73 | 8797.62 | 9.44 | | | | 1000.00 | + | 6 | 2019-01-07 | 50.00 | | 49.68 | 8757.01 | 9.39 | | | | 1000.00 | + | 7 | 2019-01-08 | 50.00 | | 49.63 | 8716.36 | 9.35 | | | | 1000.00 | + | 8 | 2019-01-09 | 50.00 | | 49.57 | 8675.67 | 9.31 | | | | 1000.00 | + | 9 | 2019-01-10 | 50.00 | | 49.52 | 8634.94 | 9.26 | | | | 1000.00 | + | 10 | 2019-01-11 | 50.00 | | 49.47 | 8594.16 | 9.22 | | | | 1000.00 | + | 11 | 2019-01-12 | 50.00 | | 49.42 | 8553.33 | 9.18 | | | | 1000.00 | + | 12 | 2019-01-13 | 50.00 | | 49.36 | 8512.47 | 9.13 | | | | 1000.00 | + | 13 | 2019-01-14 | 50.00 | | 49.31 | 8471.56 | 9.09 | | | | 1000.00 | + | 14 | 2019-01-15 | 50.00 | | 49.26 | 8430.60 | 9.05 | | | | 1000.00 | + | 15 | 2019-01-16 | 50.00 | | 49.21 | 8389.61 | 9.00 | | | | 1000.00 | + | 16 | 2019-01-17 | 50.00 | | 49.15 | 8348.56 | 8.96 | | | | 1000.00 | + | 17 | 2019-01-18 | 50.00 | | 49.10 | 8307.48 | 8.91 | | | | 1000.00 | + | 18 | 2019-01-19 | 50.00 | | 49.05 | 8266.35 | 8.87 | | | | 1000.00 | + | 19 | 2019-01-20 | 50.00 | | 49.00 | 8225.18 | 8.83 | | | | 1000.00 | + | 20 | 2019-01-21 | 50.00 | | 48.94 | 8183.96 | 8.78 | | | | 1000.00 | + | 21 | 2019-01-22 | 50.00 | | 48.89 | 8142.70 | 8.74 | | | | 1000.00 | + | 22 | 2019-01-23 | 50.00 | | 48.84 | 8101.39 | 8.69 | | | | 1000.00 | + | 23 | 2019-01-24 | 50.00 | | 48.79 | 8060.04 | 8.65 | | | | 1000.00 | + | 24 | 2019-01-25 | 50.00 | | 48.74 | 8018.65 | 8.61 | | | | 1000.00 | + | 25 | 2019-01-26 | 50.00 | | 48.68 | 7977.21 | 8.56 | | | | 1000.00 | + | 26 | 2019-01-27 | 50.00 | | 48.63 | 7935.73 | 8.52 | | | | 1000.00 | + | 27 | 2019-01-28 | 50.00 | | 48.58 | 7894.21 | 8.47 | | | | 1000.00 | + | 28 | 2019-01-29 | 50.00 | | 48.53 | 7852.63 | 8.43 | | | | 1000.00 | + | 29 | 2019-01-30 | 50.00 | | 48.48 | 7811.02 | 8.39 | | | | 1000.00 | + | 30 | 2019-01-31 | 50.00 | | 48.42 | 7769.36 | 8.34 | | | | 1000.00 | + | 31 | 2019-02-01 | 50.00 | | 48.37 | 7727.66 | 8.30 | | | | 1000.00 | + | 32 | 2019-02-02 | 50.00 | | 48.32 | 7685.91 | 8.25 | | | | 1000.00 | + | 33 | 2019-02-03 | 50.00 | | 48.27 | 7644.12 | 8.21 | | | | 1000.00 | + | 34 | 2019-02-04 | 50.00 | | 48.22 | 7602.28 | 8.16 | | | | 1000.00 | + | 35 | 2019-02-05 | 50.00 | | 48.17 | 7560.40 | 8.12 | | | | 1000.00 | + | 36 | 2019-02-06 | 50.00 | | 48.12 | 7518.47 | 8.07 | | | | 1000.00 | + | 37 | 2019-02-07 | 50.00 | | 48.06 | 7476.50 | 8.03 | | | | 1000.00 | + | 38 | 2019-02-08 | 50.00 | | 48.01 | 7434.48 | 7.98 | | | | 1000.00 | + | 39 | 2019-02-09 | 50.00 | | 47.96 | 7392.42 | 7.94 | | | | 1000.00 | + | 40 | 2019-02-10 | 50.00 | | 47.91 | 7350.31 | 7.89 | | | | 1000.00 | + | 41 | 2019-02-11 | 50.00 | | 47.86 | 7308.16 | 7.85 | | | | 1000.00 | + | 42 | 2019-02-12 | 50.00 | | 47.81 | 7265.97 | 7.80 | | | | 1000.00 | + | 43 | 2019-02-13 | 50.00 | | 47.76 | 7223.72 | 7.76 | | | | 1000.00 | + | 44 | 2019-02-14 | 50.00 | | 47.71 | 7181.44 | 7.71 | | | | 1000.00 | + | 45 | 2019-02-15 | 50.00 | | 47.66 | 7139.11 | 7.67 | | | | 1000.00 | + | 46 | 2019-02-16 | 50.00 | | 47.60 | 7096.73 | 7.62 | | | | 1000.00 | + | 47 | 2019-02-17 | 50.00 | | 47.55 | 7054.31 | 7.58 | | | | 1000.00 | + | 48 | 2019-02-18 | 50.00 | | 47.50 | 7011.84 | 7.53 | | | | 1000.00 | + | 49 | 2019-02-19 | 50.00 | | 47.45 | 6969.33 | 7.49 | | | | 1000.00 | + | 50 | 2019-02-20 | 50.00 | | 47.40 | 6926.77 | 7.44 | | | | 1000.00 | + | 51 | 2019-02-21 | 50.00 | | 47.35 | 6884.17 | 7.40 | | | | 1000.00 | + | 52 | 2019-02-22 | 50.00 | | 47.30 | 6841.52 | 7.35 | | | | 1000.00 | + | 53 | 2019-02-23 | 50.00 | | 47.25 | 6798.82 | 7.31 | | | | 1000.00 | + | 54 | 2019-02-24 | 50.00 | | 47.20 | 6756.08 | 7.26 | | | | 1000.00 | + | 55 | 2019-02-25 | 50.00 | | 47.15 | 6713.30 | 7.21 | | | | 1000.00 | + | 56 | 2019-02-26 | 50.00 | | 47.10 | 6670.47 | 7.17 | | | | 1000.00 | + | 57 | 2019-02-27 | 50.00 | | 47.05 | 6627.59 | 7.12 | | | | 1000.00 | + | 58 | 2019-02-28 | 50.00 | | 47.00 | 6584.67 | 7.08 | | | | 1000.00 | + | 59 | 2019-03-01 | 50.00 | | 46.95 | 6541.70 | 7.03 | | | | 1000.00 | + | 60 | 2019-03-02 | 50.00 | | 46.90 | 6498.68 | 6.99 | | | | 1000.00 | + | 61 | 2019-03-03 | 50.00 | | 46.85 | 6455.62 | 6.94 | | | | 1000.00 | + | 62 | 2019-03-04 | 50.00 | | 46.80 | 6412.51 | 6.89 | | | | 1000.00 | + | 63 | 2019-03-05 | 50.00 | | 46.75 | 6369.36 | 6.85 | | | | 1000.00 | + | 64 | 2019-03-06 | 50.00 | | 46.70 | 6326.16 | 6.80 | | | | 1000.00 | + | 65 | 2019-03-07 | 50.00 | | 46.65 | 6282.92 | 6.76 | | | | 1000.00 | + | 66 | 2019-03-08 | 50.00 | | 46.60 | 6239.63 | 6.71 | | | | 1000.00 | + | 67 | 2019-03-09 | 50.00 | | 46.55 | 6196.29 | 6.66 | | | | 1000.00 | + | 68 | 2019-03-10 | 50.00 | | 46.50 | 6152.91 | 6.62 | | | | 1000.00 | + | 69 | 2019-03-11 | 50.00 | | 46.45 | 6109.48 | 6.57 | | | | 1000.00 | + | 70 | 2019-03-12 | 50.00 | | 46.40 | 6066.00 | 6.52 | | | | 1000.00 | + | 71 | 2019-03-13 | 50.00 | | 46.35 | 6022.48 | 6.48 | | | | 1000.00 | + | 72 | 2019-03-14 | 50.00 | | 46.30 | 5978.91 | 6.43 | | | | 1000.00 | + | 73 | 2019-03-15 | 50.00 | | 46.25 | 5935.29 | 6.38 | | | | 1000.00 | + | 74 | 2019-03-16 | 50.00 | | 46.20 | 5891.63 | 6.34 | | | | 1000.00 | + | 75 | 2019-03-17 | 50.00 | | 46.15 | 5847.92 | 6.29 | | | | 1000.00 | + | 76 | 2019-03-18 | 50.00 | | 46.10 | 5804.17 | 6.24 | | | | 1000.00 | + | 77 | 2019-03-19 | 50.00 | | 46.06 | 5760.36 | 6.20 | | | | 1000.00 | + | 78 | 2019-03-20 | 50.00 | | 46.01 | 5716.52 | 6.15 | | | | 1000.00 | + | 79 | 2019-03-21 | 50.00 | | 45.96 | 5672.62 | 6.10 | | | | 1000.00 | + | 80 | 2019-03-22 | 50.00 | | 45.91 | 5628.68 | 6.06 | | | | 1000.00 | + | 81 | 2019-03-23 | 50.00 | | 45.86 | 5584.69 | 6.01 | | | | 1000.00 | + | 82 | 2019-03-24 | 50.00 | | 45.81 | 5540.65 | 5.96 | | | | 1000.00 | + | 83 | 2019-03-25 | 50.00 | | 45.76 | 5496.57 | 5.92 | | | | 1000.00 | + | 84 | 2019-03-26 | 50.00 | | 45.71 | 5452.44 | 5.87 | | | | 1000.00 | + | 85 | 2019-03-27 | 50.00 | | 45.66 | 5408.26 | 5.82 | | | | 1000.00 | + | 86 | 2019-03-28 | 50.00 | | 45.62 | 5364.03 | 5.78 | | | | 1000.00 | + | 87 | 2019-03-29 | 50.00 | | 45.57 | 5319.76 | 5.73 | | | | 1000.00 | + | 88 | 2019-03-30 | 50.00 | | 45.52 | 5275.44 | 5.68 | | | | 1000.00 | + | 89 | 2019-03-31 | 50.00 | | 45.47 | 5231.08 | 5.63 | | | | 1000.00 | + | 90 | 2019-04-01 | 50.00 | | 45.42 | 5186.66 | 5.59 | | | | 1000.00 | + | 91 | 2019-04-02 | 50.00 | | 45.37 | 5142.20 | 5.54 | | | | 1000.00 | + | 92 | 2019-04-03 | 50.00 | | 45.32 | 5097.69 | 5.49 | | | | 1000.00 | + | 93 | 2019-04-04 | 50.00 | | 45.28 | 5053.13 | 5.44 | | | | 1000.00 | + | 94 | 2019-04-05 | 50.00 | | 45.23 | 5008.53 | 5.40 | | | | 1000.00 | + | 95 | 2019-04-06 | 50.00 | | 45.18 | 4963.88 | 5.35 | | | | 1000.00 | + | 96 | 2019-04-07 | 50.00 | | 45.13 | 4919.18 | 5.30 | | | | 1000.00 | + | 97 | 2019-04-08 | 50.00 | | 45.08 | 4874.43 | 5.25 | | | | 1000.00 | + | 98 | 2019-04-09 | 50.00 | | 45.03 | 4829.64 | 5.20 | | | | 1000.00 | + | 99 | 2019-04-10 | 50.00 | | 44.99 | 4784.79 | 5.16 | | | | 1000.00 | + | 100 | 2019-04-11 | 50.00 | | 44.94 | 4739.90 | 5.11 | | | | 1000.00 | + | 101 | 2019-04-12 | 50.00 | | 44.89 | 4694.96 | 5.06 | | | | 1000.00 | + | 102 | 2019-04-13 | 50.00 | | 44.84 | 4649.98 | 5.01 | | | | 1000.00 | + | 103 | 2019-04-14 | 50.00 | | 44.80 | 4604.94 | 4.97 | | | | 1000.00 | + | 104 | 2019-04-15 | 50.00 | | 44.75 | 4559.86 | 4.92 | | | | 1000.00 | + | 105 | 2019-04-16 | 50.00 | | 44.70 | 4514.73 | 4.87 | | | | 1000.00 | + | 106 | 2019-04-17 | 50.00 | | 44.65 | 4469.55 | 4.82 | | | | 1000.00 | + | 107 | 2019-04-18 | 50.00 | | 44.60 | 4424.32 | 4.77 | | | | 1000.00 | + | 108 | 2019-04-19 | 50.00 | | 44.56 | 4379.05 | 4.72 | | | | 1000.00 | + | 109 | 2019-04-20 | 50.00 | | 44.51 | 4333.72 | 4.68 | | | | 1000.00 | + | 110 | 2019-04-21 | 50.00 | | 44.46 | 4288.35 | 4.63 | | | | 1000.00 | + | 111 | 2019-04-22 | 50.00 | | 44.41 | 4242.93 | 4.58 | | | | 1000.00 | + | 112 | 2019-04-23 | 50.00 | | 44.37 | 4197.46 | 4.53 | | | | 1000.00 | + | 113 | 2019-04-24 | 50.00 | | 44.32 | 4151.94 | 4.48 | | | | 1000.00 | + | 114 | 2019-04-25 | 50.00 | | 44.27 | 4106.38 | 4.43 | | | | 1000.00 | + | 115 | 2019-04-26 | 50.00 | | 44.22 | 4060.76 | 4.38 | | | | 1000.00 | + | 116 | 2019-04-27 | 50.00 | | 44.18 | 4015.10 | 4.34 | | | | 1000.00 | + | 117 | 2019-04-28 | 50.00 | | 44.13 | 3969.38 | 4.29 | | | | 1000.00 | + | 118 | 2019-04-29 | 50.00 | | 44.08 | 3923.62 | 4.24 | | | | 1000.00 | + | 119 | 2019-04-30 | 50.00 | | 44.04 | 3877.81 | 4.19 | | | | 1000.00 | + | 120 | 2019-05-01 | 50.00 | | 43.99 | 3831.95 | 4.14 | | | | 1000.00 | + | 121 | 2019-05-02 | 50.00 | | 43.94 | 3786.04 | 4.09 | | | | 1000.00 | + | 122 | 2019-05-03 | 50.00 | | 43.90 | 3740.09 | 4.04 | | | | 1000.00 | + | 123 | 2019-05-04 | 50.00 | | 43.85 | 3694.08 | 3.99 | | | | 1000.00 | + | 124 | 2019-05-05 | 50.00 | | 43.80 | 3648.03 | 3.94 | | | | 1000.00 | + | 125 | 2019-05-06 | 50.00 | | 43.76 | 3601.92 | 3.90 | | | | 1000.00 | + | 126 | 2019-05-07 | 50.00 | | 43.71 | 3555.77 | 3.85 | | | | 1000.00 | + | 127 | 2019-05-08 | 50.00 | | 43.66 | 3509.56 | 3.80 | | | | 1000.00 | + | 128 | 2019-05-09 | 50.00 | | 43.62 | 3463.31 | 3.75 | | | | 1000.00 | + | 129 | 2019-05-10 | 50.00 | | 43.57 | 3417.01 | 3.70 | | | | 1000.00 | + | 130 | 2019-05-11 | 50.00 | | 43.52 | 3370.66 | 3.65 | | | | 1000.00 | + | 131 | 2019-05-12 | 50.00 | | 43.48 | 3324.26 | 3.60 | | | | 1000.00 | + | 132 | 2019-05-13 | 50.00 | | 43.43 | 3277.81 | 3.55 | | | | 1000.00 | + | 133 | 2019-05-14 | 50.00 | | 43.38 | 3231.31 | 3.50 | | | | 1000.00 | + | 134 | 2019-05-15 | 50.00 | | 43.34 | 3184.76 | 3.45 | | | | 1000.00 | + | 135 | 2019-05-16 | 50.00 | | 43.29 | 3138.16 | 3.40 | | | | 1000.00 | + | 136 | 2019-05-17 | 50.00 | | 43.24 | 3091.51 | 3.35 | | | | 1000.00 | + | 137 | 2019-05-18 | 50.00 | | 43.20 | 3044.81 | 3.30 | | | | 1000.00 | + | 138 | 2019-05-19 | 50.00 | | 43.15 | 2998.06 | 3.25 | | | | 1000.00 | + | 139 | 2019-05-20 | 50.00 | | 43.11 | 2951.26 | 3.20 | | | | 1000.00 | + | 140 | 2019-05-21 | 50.00 | | 43.06 | 2904.42 | 3.15 | | | | 1000.00 | + | 141 | 2019-05-22 | 50.00 | | 43.01 | 2857.52 | 3.10 | | | | 1000.00 | + | 142 | 2019-05-23 | 50.00 | | 42.97 | 2810.57 | 3.05 | | | | 1000.00 | + | 143 | 2019-05-24 | 50.00 | | 42.92 | 2763.57 | 3.00 | | | | 1000.00 | + | 144 | 2019-05-25 | 50.00 | | 42.88 | 2716.52 | 2.95 | | | | 1000.00 | + | 145 | 2019-05-26 | 50.00 | | 42.83 | 2669.42 | 2.90 | | | | 1000.00 | + | 146 | 2019-05-27 | 50.00 | | 42.79 | 2622.27 | 2.85 | | | | 1000.00 | + | 147 | 2019-05-28 | 50.00 | | 42.74 | 2575.07 | 2.80 | | | | 1000.00 | + | 148 | 2019-05-29 | 50.00 | | 42.69 | 2527.82 | 2.75 | | | | 1000.00 | + | 149 | 2019-05-30 | 50.00 | | 42.65 | 2480.52 | 2.70 | | | | 1000.00 | + | 150 | 2019-05-31 | 50.00 | | 42.60 | 2433.17 | 2.65 | | | | 1000.00 | + | 151 | 2019-06-01 | 50.00 | | 42.56 | 2385.77 | 2.60 | | | | 1000.00 | + | 152 | 2019-06-02 | 50.00 | | 42.51 | 2338.31 | 2.55 | | | | 1000.00 | + | 153 | 2019-06-03 | 50.00 | | 42.47 | 2290.81 | 2.50 | | | | 1000.00 | + | 154 | 2019-06-04 | 50.00 | | 42.42 | 2243.26 | 2.45 | | | | 1000.00 | + | 155 | 2019-06-05 | 50.00 | | 42.38 | 2195.65 | 2.40 | | | | 1000.00 | + | 156 | 2019-06-06 | 50.00 | | 42.33 | 2148.00 | 2.34 | | | | 1000.00 | + | 157 | 2019-06-07 | 50.00 | | 42.29 | 2100.29 | 2.29 | | | | 1000.00 | + | 158 | 2019-06-08 | 50.00 | | 42.24 | 2052.53 | 2.24 | | | | 1000.00 | + | 159 | 2019-06-09 | 50.00 | | 42.20 | 2004.73 | 2.19 | | | | 1000.00 | + | 160 | 2019-06-10 | 50.00 | | 42.15 | 1956.87 | 2.14 | | | | 1000.00 | + | 161 | 2019-06-11 | 50.00 | | 42.11 | 1908.96 | 2.09 | | | | 1000.00 | + | 162 | 2019-06-12 | 50.00 | | 42.06 | 1860.99 | 2.04 | | | | 1000.00 | + | 163 | 2019-06-13 | 50.00 | | 42.02 | 1812.98 | 1.99 | | | | 1000.00 | + | 164 | 2019-06-14 | 50.00 | | 41.97 | 1764.92 | 1.94 | | | | 1000.00 | + | 165 | 2019-06-15 | 50.00 | | 41.93 | 1716.80 | 1.88 | | | | 1000.00 | + | 166 | 2019-06-16 | 50.00 | | 41.88 | 1668.64 | 1.83 | | | | 1000.00 | + | 167 | 2019-06-17 | 50.00 | | 41.84 | 1620.42 | 1.78 | | | | 1000.00 | + | 168 | 2019-06-18 | 50.00 | | 41.79 | 1572.15 | 1.73 | | | | 1000.00 | + | 169 | 2019-06-19 | 50.00 | | 41.75 | 1523.83 | 1.68 | | | | 1000.00 | + | 170 | 2019-06-20 | 50.00 | | 41.70 | 1475.45 | 1.63 | | | | 1000.00 | + | 171 | 2019-06-21 | 50.00 | | 41.66 | 1427.03 | 1.58 | | | | 1000.00 | + | 172 | 2019-06-22 | 50.00 | | 41.61 | 1378.55 | 1.52 | | | | 1000.00 | + | 173 | 2019-06-23 | 50.00 | | 41.57 | 1330.02 | 1.47 | | | | 1000.00 | + | 174 | 2019-06-24 | 50.00 | | 41.53 | 1281.45 | 1.42 | | | | 1000.00 | + | 175 | 2019-06-25 | 50.00 | | 41.48 | 1232.81 | 1.37 | | | | 1000.00 | + | 176 | 2019-06-26 | 50.00 | | 41.44 | 1184.13 | 1.32 | | | | 1000.00 | + | 177 | 2019-06-27 | 50.00 | | 41.39 | 1135.39 | 1.26 | | | | 1000.00 | + | 178 | 2019-06-28 | 50.00 | | 41.35 | 1086.61 | 1.21 | | | | 1000.00 | + | 179 | 2019-06-29 | 50.00 | | 41.31 | 1037.77 | 1.16 | | | | 1000.00 | + | 180 | 2019-06-30 | 50.00 | | 41.26 | 988.88 | 1.11 | | | | 1000.00 | + | 181 | 2019-07-01 | 50.00 | | 41.22 | 939.93 | 1.06 | | | | 1000.00 | + | 182 | 2019-07-02 | 50.00 | | 41.17 | 890.93 | 1.00 | | | | 1000.00 | + | 183 | 2019-07-03 | 50.00 | | 41.13 | 841.89 | 0.95 | | | | 1000.00 | + | 184 | 2019-07-04 | 50.00 | | 41.09 | 792.79 | 0.90 | | | | 1000.00 | + | 185 | 2019-07-05 | 50.00 | | 41.04 | 743.63 | 0.85 | | | | 1000.00 | + | 186 | 2019-07-06 | 50.00 | | 41.00 | 694.43 | 0.79 | | | | 1000.00 | + | 187 | 2019-07-07 | 50.00 | | 40.95 | 645.17 | 0.74 | | | | 1000.00 | + | 188 | 2019-07-08 | 50.00 | | 40.91 | 595.86 | 0.69 | | | | 1000.00 | + | 189 | 2019-07-09 | 50.00 | | 40.87 | 546.49 | 0.64 | | | | 1000.00 | + | 190 | 2019-07-10 | 50.00 | | 40.82 | 497.08 | 0.58 | | | | 1000.00 | + | 191 | 2019-07-11 | 50.00 | | 40.78 | 447.61 | 0.53 | | | | 1000.00 | + | 192 | 2019-07-12 | 50.00 | | 40.74 | 398.08 | 0.48 | | | | 1000.00 | + | 193 | 2019-07-13 | 50.00 | | 40.69 | 348.51 | 0.43 | | | | 1000.00 | + | 194 | 2019-07-14 | 50.00 | | 40.65 | 298.88 | 0.37 | | | | 1000.00 | + | 195 | 2019-07-15 | 50.00 | | 40.61 | 249.20 | 0.32 | | | | 1000.00 | + | 196 | 2019-07-16 | 50.00 | | 40.56 | 199.47 | 0.27 | | | | 1000.00 | + | 197 | 2019-07-17 | 50.00 | | 40.52 | 149.68 | 0.21 | | | | 1000.00 | + | 198 | 2019-07-18 | 50.00 | | 40.48 | 99.84 | 0.16 | | | | 1000.00 | + | 199 | 2019-07-19 | 50.00 | | 40.43 | 49.95 | 0.11 | | | | 1000.00 | + | 200 | 2019-07-20 | 50.00 | | 40.39 | 0.00 | 0.05 | | | | 1000.00 | + + @TestRailId:C78826 + Scenario: Generate and retrieve a projected amortization schedule with 200 payments with update period payment rate in a middle of loan lifecycle - UC2 + When Admin sets the business date to "01 January 2026" + And Admin creates a client with random data + And Admin creates a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP | 01 January 2026 | 01 January 2026 | 100 | 100 | 18 | 0 | + Then Admin successfully approves the working capital loan on "01 January 2026" with "100" amount and expected disbursement date on "01 January 2026" + Then Admin successfully disburse the Working Capital loan on "01 January 2026" with "100" EUR transaction amount + Then Working Capital loan status will be "ACTIVE" + + When Admin generates a projected amortization schedule with discountFeeAmount 1000.0, netDisbursementAmount 9000.0, totalPaymentValue 1000.0, periodPaymentRate 18, npvDayCount 360, expectedDisbursementDate "2019-01-01" + And Admin retrieves the projected amortization schedule + Then The retrieved amortization schedule has the following summary fields: + | discountFeeAmount | netDisbursementAmount | totalPaymentValue | periodPaymentRate | npvDayCount | expectedPaymentAmount | originalPaymentNumber | + | 1000.00 | 9000.00 | 1000.00 | 18 | 360 | 50.00 | 200 | + And The retrieved amortization schedule has payments with the following details: + | paymentNo | date | expectedPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | actualPaymentAmount | actualAmortizationAmount | incomeModification | deferredBalance | + | 0 | 2019-01-01 | -9000.00 | 1 | -9000.00 | 9000.00 | | | | | 1000.00 | + | 1 | 2019-01-02 | 50.00 | | 49.95 | 8959.61 | 9.61 | | | | 1000.00 | + | 2 | 2019-01-03 | 50.00 | | 49.89 | 8919.18 | 9.57 | | | | 1000.00 | + | 3 | 2019-01-04 | 50.00 | | 49.84 | 8878.70 | 9.52 | | | | 1000.00 | + | 4 | 2019-01-05 | 50.00 | | 49.79 | 8838.18 | 9.48 | | | | 1000.00 | + | 5 | 2019-01-06 | 50.00 | | 49.73 | 8797.62 | 9.44 | | | | 1000.00 | + | 6 | 2019-01-07 | 50.00 | | 49.68 | 8757.01 | 9.39 | | | | 1000.00 | + | 7 | 2019-01-08 | 50.00 | | 49.63 | 8716.36 | 9.35 | | | | 1000.00 | + | 8 | 2019-01-09 | 50.00 | | 49.57 | 8675.67 | 9.31 | | | | 1000.00 | + | 9 | 2019-01-10 | 50.00 | | 49.52 | 8634.94 | 9.26 | | | | 1000.00 | + | 10 | 2019-01-11 | 50.00 | | 49.47 | 8594.16 | 9.22 | | | | 1000.00 | + | 11 | 2019-01-12 | 50.00 | | 49.42 | 8553.33 | 9.18 | | | | 1000.00 | + | 12 | 2019-01-13 | 50.00 | | 49.36 | 8512.47 | 9.13 | | | | 1000.00 | + | 13 | 2019-01-14 | 50.00 | | 49.31 | 8471.56 | 9.09 | | | | 1000.00 | + | 14 | 2019-01-15 | 50.00 | | 49.26 | 8430.60 | 9.05 | | | | 1000.00 | + | 15 | 2019-01-16 | 50.00 | | 49.21 | 8389.61 | 9.00 | | | | 1000.00 | + | 16 | 2019-01-17 | 50.00 | | 49.15 | 8348.56 | 8.96 | | | | 1000.00 | + | 17 | 2019-01-18 | 50.00 | | 49.10 | 8307.48 | 8.91 | | | | 1000.00 | + | 18 | 2019-01-19 | 50.00 | | 49.05 | 8266.35 | 8.87 | | | | 1000.00 | + | 19 | 2019-01-20 | 50.00 | | 49.00 | 8225.18 | 8.83 | | | | 1000.00 | + | 20 | 2019-01-21 | 50.00 | | 48.94 | 8183.96 | 8.78 | | | | 1000.00 | + | 21 | 2019-01-22 | 50.00 | | 48.89 | 8142.70 | 8.74 | | | | 1000.00 | + | 22 | 2019-01-23 | 50.00 | | 48.84 | 8101.39 | 8.69 | | | | 1000.00 | + | 23 | 2019-01-24 | 50.00 | | 48.79 | 8060.04 | 8.65 | | | | 1000.00 | + | 24 | 2019-01-25 | 50.00 | | 48.74 | 8018.65 | 8.61 | | | | 1000.00 | + | 25 | 2019-01-26 | 50.00 | | 48.68 | 7977.21 | 8.56 | | | | 1000.00 | + | 26 | 2019-01-27 | 50.00 | | 48.63 | 7935.73 | 8.52 | | | | 1000.00 | + | 27 | 2019-01-28 | 50.00 | | 48.58 | 7894.21 | 8.47 | | | | 1000.00 | + | 28 | 2019-01-29 | 50.00 | | 48.53 | 7852.63 | 8.43 | | | | 1000.00 | + | 29 | 2019-01-30 | 50.00 | | 48.48 | 7811.02 | 8.39 | | | | 1000.00 | + | 30 | 2019-01-31 | 50.00 | | 48.42 | 7769.36 | 8.34 | | | | 1000.00 | + | 31 | 2019-02-01 | 50.00 | | 48.37 | 7727.66 | 8.30 | | | | 1000.00 | + | 32 | 2019-02-02 | 50.00 | | 48.32 | 7685.91 | 8.25 | | | | 1000.00 | + | 33 | 2019-02-03 | 50.00 | | 48.27 | 7644.12 | 8.21 | | | | 1000.00 | + | 34 | 2019-02-04 | 50.00 | | 48.22 | 7602.28 | 8.16 | | | | 1000.00 | + | 35 | 2019-02-05 | 50.00 | | 48.17 | 7560.40 | 8.12 | | | | 1000.00 | + | 36 | 2019-02-06 | 50.00 | | 48.12 | 7518.47 | 8.07 | | | | 1000.00 | + | 37 | 2019-02-07 | 50.00 | | 48.06 | 7476.50 | 8.03 | | | | 1000.00 | + | 38 | 2019-02-08 | 50.00 | | 48.01 | 7434.48 | 7.98 | | | | 1000.00 | + | 39 | 2019-02-09 | 50.00 | | 47.96 | 7392.42 | 7.94 | | | | 1000.00 | + | 40 | 2019-02-10 | 50.00 | | 47.91 | 7350.31 | 7.89 | | | | 1000.00 | + | 41 | 2019-02-11 | 50.00 | | 47.86 | 7308.16 | 7.85 | | | | 1000.00 | + | 42 | 2019-02-12 | 50.00 | | 47.81 | 7265.97 | 7.80 | | | | 1000.00 | + | 43 | 2019-02-13 | 50.00 | | 47.76 | 7223.72 | 7.76 | | | | 1000.00 | + | 44 | 2019-02-14 | 50.00 | | 47.71 | 7181.44 | 7.71 | | | | 1000.00 | + | 45 | 2019-02-15 | 50.00 | | 47.66 | 7139.11 | 7.67 | | | | 1000.00 | + | 46 | 2019-02-16 | 50.00 | | 47.60 | 7096.73 | 7.62 | | | | 1000.00 | + | 47 | 2019-02-17 | 50.00 | | 47.55 | 7054.31 | 7.58 | | | | 1000.00 | + | 48 | 2019-02-18 | 50.00 | | 47.50 | 7011.84 | 7.53 | | | | 1000.00 | + | 49 | 2019-02-19 | 50.00 | | 47.45 | 6969.33 | 7.49 | | | | 1000.00 | + | 50 | 2019-02-20 | 50.00 | | 47.40 | 6926.77 | 7.44 | | | | 1000.00 | + | 51 | 2019-02-21 | 50.00 | | 47.35 | 6884.17 | 7.40 | | | | 1000.00 | + | 52 | 2019-02-22 | 50.00 | | 47.30 | 6841.52 | 7.35 | | | | 1000.00 | + | 53 | 2019-02-23 | 50.00 | | 47.25 | 6798.82 | 7.31 | | | | 1000.00 | + | 54 | 2019-02-24 | 50.00 | | 47.20 | 6756.08 | 7.26 | | | | 1000.00 | + | 55 | 2019-02-25 | 50.00 | | 47.15 | 6713.30 | 7.21 | | | | 1000.00 | + | 56 | 2019-02-26 | 50.00 | | 47.10 | 6670.47 | 7.17 | | | | 1000.00 | + | 57 | 2019-02-27 | 50.00 | | 47.05 | 6627.59 | 7.12 | | | | 1000.00 | + | 58 | 2019-02-28 | 50.00 | | 47.00 | 6584.67 | 7.08 | | | | 1000.00 | + | 59 | 2019-03-01 | 50.00 | | 46.95 | 6541.70 | 7.03 | | | | 1000.00 | + | 60 | 2019-03-02 | 50.00 | | 46.90 | 6498.68 | 6.99 | | | | 1000.00 | + | 61 | 2019-03-03 | 50.00 | | 46.85 | 6455.62 | 6.94 | | | | 1000.00 | + | 62 | 2019-03-04 | 50.00 | | 46.80 | 6412.51 | 6.89 | | | | 1000.00 | + | 63 | 2019-03-05 | 50.00 | | 46.75 | 6369.36 | 6.85 | | | | 1000.00 | + | 64 | 2019-03-06 | 50.00 | | 46.70 | 6326.16 | 6.80 | | | | 1000.00 | + | 65 | 2019-03-07 | 50.00 | | 46.65 | 6282.92 | 6.76 | | | | 1000.00 | + | 66 | 2019-03-08 | 50.00 | | 46.60 | 6239.63 | 6.71 | | | | 1000.00 | + | 67 | 2019-03-09 | 50.00 | | 46.55 | 6196.29 | 6.66 | | | | 1000.00 | + | 68 | 2019-03-10 | 50.00 | | 46.50 | 6152.91 | 6.62 | | | | 1000.00 | + | 69 | 2019-03-11 | 50.00 | | 46.45 | 6109.48 | 6.57 | | | | 1000.00 | + | 70 | 2019-03-12 | 50.00 | | 46.40 | 6066.00 | 6.52 | | | | 1000.00 | + | 71 | 2019-03-13 | 50.00 | | 46.35 | 6022.48 | 6.48 | | | | 1000.00 | + | 72 | 2019-03-14 | 50.00 | | 46.30 | 5978.91 | 6.43 | | | | 1000.00 | + | 73 | 2019-03-15 | 50.00 | | 46.25 | 5935.29 | 6.38 | | | | 1000.00 | + | 74 | 2019-03-16 | 50.00 | | 46.20 | 5891.63 | 6.34 | | | | 1000.00 | + | 75 | 2019-03-17 | 50.00 | | 46.15 | 5847.92 | 6.29 | | | | 1000.00 | + | 76 | 2019-03-18 | 50.00 | | 46.10 | 5804.17 | 6.24 | | | | 1000.00 | + | 77 | 2019-03-19 | 50.00 | | 46.06 | 5760.36 | 6.20 | | | | 1000.00 | + | 78 | 2019-03-20 | 50.00 | | 46.01 | 5716.52 | 6.15 | | | | 1000.00 | + | 79 | 2019-03-21 | 50.00 | | 45.96 | 5672.62 | 6.10 | | | | 1000.00 | + | 80 | 2019-03-22 | 50.00 | | 45.91 | 5628.68 | 6.06 | | | | 1000.00 | + | 81 | 2019-03-23 | 50.00 | | 45.86 | 5584.69 | 6.01 | | | | 1000.00 | + | 82 | 2019-03-24 | 50.00 | | 45.81 | 5540.65 | 5.96 | | | | 1000.00 | + | 83 | 2019-03-25 | 50.00 | | 45.76 | 5496.57 | 5.92 | | | | 1000.00 | + | 84 | 2019-03-26 | 50.00 | | 45.71 | 5452.44 | 5.87 | | | | 1000.00 | + | 85 | 2019-03-27 | 50.00 | | 45.66 | 5408.26 | 5.82 | | | | 1000.00 | + | 86 | 2019-03-28 | 50.00 | | 45.62 | 5364.03 | 5.78 | | | | 1000.00 | + | 87 | 2019-03-29 | 50.00 | | 45.57 | 5319.76 | 5.73 | | | | 1000.00 | + | 88 | 2019-03-30 | 50.00 | | 45.52 | 5275.44 | 5.68 | | | | 1000.00 | + | 89 | 2019-03-31 | 50.00 | | 45.47 | 5231.08 | 5.63 | | | | 1000.00 | + | 90 | 2019-04-01 | 50.00 | | 45.42 | 5186.66 | 5.59 | | | | 1000.00 | + | 91 | 2019-04-02 | 50.00 | | 45.37 | 5142.20 | 5.54 | | | | 1000.00 | + | 92 | 2019-04-03 | 50.00 | | 45.32 | 5097.69 | 5.49 | | | | 1000.00 | + | 93 | 2019-04-04 | 50.00 | | 45.28 | 5053.13 | 5.44 | | | | 1000.00 | + | 94 | 2019-04-05 | 50.00 | | 45.23 | 5008.53 | 5.40 | | | | 1000.00 | + | 95 | 2019-04-06 | 50.00 | | 45.18 | 4963.88 | 5.35 | | | | 1000.00 | + | 96 | 2019-04-07 | 50.00 | | 45.13 | 4919.18 | 5.30 | | | | 1000.00 | + | 97 | 2019-04-08 | 50.00 | | 45.08 | 4874.43 | 5.25 | | | | 1000.00 | + | 98 | 2019-04-09 | 50.00 | | 45.03 | 4829.64 | 5.20 | | | | 1000.00 | + | 99 | 2019-04-10 | 50.00 | | 44.99 | 4784.79 | 5.16 | | | | 1000.00 | + | 100 | 2019-04-11 | 50.00 | | 44.94 | 4739.90 | 5.11 | | | | 1000.00 | + | 101 | 2019-04-12 | 50.00 | | 44.89 | 4694.96 | 5.06 | | | | 1000.00 | + | 102 | 2019-04-13 | 50.00 | | 44.84 | 4649.98 | 5.01 | | | | 1000.00 | + | 103 | 2019-04-14 | 50.00 | | 44.80 | 4604.94 | 4.97 | | | | 1000.00 | + | 104 | 2019-04-15 | 50.00 | | 44.75 | 4559.86 | 4.92 | | | | 1000.00 | + | 105 | 2019-04-16 | 50.00 | | 44.70 | 4514.73 | 4.87 | | | | 1000.00 | + | 106 | 2019-04-17 | 50.00 | | 44.65 | 4469.55 | 4.82 | | | | 1000.00 | + | 107 | 2019-04-18 | 50.00 | | 44.60 | 4424.32 | 4.77 | | | | 1000.00 | + | 108 | 2019-04-19 | 50.00 | | 44.56 | 4379.05 | 4.72 | | | | 1000.00 | + | 109 | 2019-04-20 | 50.00 | | 44.51 | 4333.72 | 4.68 | | | | 1000.00 | + | 110 | 2019-04-21 | 50.00 | | 44.46 | 4288.35 | 4.63 | | | | 1000.00 | + | 111 | 2019-04-22 | 50.00 | | 44.41 | 4242.93 | 4.58 | | | | 1000.00 | + | 112 | 2019-04-23 | 50.00 | | 44.37 | 4197.46 | 4.53 | | | | 1000.00 | + | 113 | 2019-04-24 | 50.00 | | 44.32 | 4151.94 | 4.48 | | | | 1000.00 | + | 114 | 2019-04-25 | 50.00 | | 44.27 | 4106.38 | 4.43 | | | | 1000.00 | + | 115 | 2019-04-26 | 50.00 | | 44.22 | 4060.76 | 4.38 | | | | 1000.00 | + | 116 | 2019-04-27 | 50.00 | | 44.18 | 4015.10 | 4.34 | | | | 1000.00 | + | 117 | 2019-04-28 | 50.00 | | 44.13 | 3969.38 | 4.29 | | | | 1000.00 | + | 118 | 2019-04-29 | 50.00 | | 44.08 | 3923.62 | 4.24 | | | | 1000.00 | + | 119 | 2019-04-30 | 50.00 | | 44.04 | 3877.81 | 4.19 | | | | 1000.00 | + | 120 | 2019-05-01 | 50.00 | | 43.99 | 3831.95 | 4.14 | | | | 1000.00 | + | 121 | 2019-05-02 | 50.00 | | 43.94 | 3786.04 | 4.09 | | | | 1000.00 | + | 122 | 2019-05-03 | 50.00 | | 43.90 | 3740.09 | 4.04 | | | | 1000.00 | + | 123 | 2019-05-04 | 50.00 | | 43.85 | 3694.08 | 3.99 | | | | 1000.00 | + | 124 | 2019-05-05 | 50.00 | | 43.80 | 3648.03 | 3.94 | | | | 1000.00 | + | 125 | 2019-05-06 | 50.00 | | 43.76 | 3601.92 | 3.90 | | | | 1000.00 | + | 126 | 2019-05-07 | 50.00 | | 43.71 | 3555.77 | 3.85 | | | | 1000.00 | + | 127 | 2019-05-08 | 50.00 | | 43.66 | 3509.56 | 3.80 | | | | 1000.00 | + | 128 | 2019-05-09 | 50.00 | | 43.62 | 3463.31 | 3.75 | | | | 1000.00 | + | 129 | 2019-05-10 | 50.00 | | 43.57 | 3417.01 | 3.70 | | | | 1000.00 | + | 130 | 2019-05-11 | 50.00 | | 43.52 | 3370.66 | 3.65 | | | | 1000.00 | + | 131 | 2019-05-12 | 50.00 | | 43.48 | 3324.26 | 3.60 | | | | 1000.00 | + | 132 | 2019-05-13 | 50.00 | | 43.43 | 3277.81 | 3.55 | | | | 1000.00 | + | 133 | 2019-05-14 | 50.00 | | 43.38 | 3231.31 | 3.50 | | | | 1000.00 | + | 134 | 2019-05-15 | 50.00 | | 43.34 | 3184.76 | 3.45 | | | | 1000.00 | + | 135 | 2019-05-16 | 50.00 | | 43.29 | 3138.16 | 3.40 | | | | 1000.00 | + | 136 | 2019-05-17 | 50.00 | | 43.24 | 3091.51 | 3.35 | | | | 1000.00 | + | 137 | 2019-05-18 | 50.00 | | 43.20 | 3044.81 | 3.30 | | | | 1000.00 | + | 138 | 2019-05-19 | 50.00 | | 43.15 | 2998.06 | 3.25 | | | | 1000.00 | + | 139 | 2019-05-20 | 50.00 | | 43.11 | 2951.26 | 3.20 | | | | 1000.00 | + | 140 | 2019-05-21 | 50.00 | | 43.06 | 2904.42 | 3.15 | | | | 1000.00 | + | 141 | 2019-05-22 | 50.00 | | 43.01 | 2857.52 | 3.10 | | | | 1000.00 | + | 142 | 2019-05-23 | 50.00 | | 42.97 | 2810.57 | 3.05 | | | | 1000.00 | + | 143 | 2019-05-24 | 50.00 | | 42.92 | 2763.57 | 3.00 | | | | 1000.00 | + | 144 | 2019-05-25 | 50.00 | | 42.88 | 2716.52 | 2.95 | | | | 1000.00 | + | 145 | 2019-05-26 | 50.00 | | 42.83 | 2669.42 | 2.90 | | | | 1000.00 | + | 146 | 2019-05-27 | 50.00 | | 42.79 | 2622.27 | 2.85 | | | | 1000.00 | + | 147 | 2019-05-28 | 50.00 | | 42.74 | 2575.07 | 2.80 | | | | 1000.00 | + | 148 | 2019-05-29 | 50.00 | | 42.69 | 2527.82 | 2.75 | | | | 1000.00 | + | 149 | 2019-05-30 | 50.00 | | 42.65 | 2480.52 | 2.70 | | | | 1000.00 | + | 150 | 2019-05-31 | 50.00 | | 42.60 | 2433.17 | 2.65 | | | | 1000.00 | + | 151 | 2019-06-01 | 50.00 | | 42.56 | 2385.77 | 2.60 | | | | 1000.00 | + | 152 | 2019-06-02 | 50.00 | | 42.51 | 2338.31 | 2.55 | | | | 1000.00 | + | 153 | 2019-06-03 | 50.00 | | 42.47 | 2290.81 | 2.50 | | | | 1000.00 | + | 154 | 2019-06-04 | 50.00 | | 42.42 | 2243.26 | 2.45 | | | | 1000.00 | + | 155 | 2019-06-05 | 50.00 | | 42.38 | 2195.65 | 2.40 | | | | 1000.00 | + | 156 | 2019-06-06 | 50.00 | | 42.33 | 2148.00 | 2.34 | | | | 1000.00 | + | 157 | 2019-06-07 | 50.00 | | 42.29 | 2100.29 | 2.29 | | | | 1000.00 | + | 158 | 2019-06-08 | 50.00 | | 42.24 | 2052.53 | 2.24 | | | | 1000.00 | + | 159 | 2019-06-09 | 50.00 | | 42.20 | 2004.73 | 2.19 | | | | 1000.00 | + | 160 | 2019-06-10 | 50.00 | | 42.15 | 1956.87 | 2.14 | | | | 1000.00 | + | 161 | 2019-06-11 | 50.00 | | 42.11 | 1908.96 | 2.09 | | | | 1000.00 | + | 162 | 2019-06-12 | 50.00 | | 42.06 | 1860.99 | 2.04 | | | | 1000.00 | + | 163 | 2019-06-13 | 50.00 | | 42.02 | 1812.98 | 1.99 | | | | 1000.00 | + | 164 | 2019-06-14 | 50.00 | | 41.97 | 1764.92 | 1.94 | | | | 1000.00 | + | 165 | 2019-06-15 | 50.00 | | 41.93 | 1716.80 | 1.88 | | | | 1000.00 | + | 166 | 2019-06-16 | 50.00 | | 41.88 | 1668.64 | 1.83 | | | | 1000.00 | + | 167 | 2019-06-17 | 50.00 | | 41.84 | 1620.42 | 1.78 | | | | 1000.00 | + | 168 | 2019-06-18 | 50.00 | | 41.79 | 1572.15 | 1.73 | | | | 1000.00 | + | 169 | 2019-06-19 | 50.00 | | 41.75 | 1523.83 | 1.68 | | | | 1000.00 | + | 170 | 2019-06-20 | 50.00 | | 41.70 | 1475.45 | 1.63 | | | | 1000.00 | + | 171 | 2019-06-21 | 50.00 | | 41.66 | 1427.03 | 1.58 | | | | 1000.00 | + | 172 | 2019-06-22 | 50.00 | | 41.61 | 1378.55 | 1.52 | | | | 1000.00 | + | 173 | 2019-06-23 | 50.00 | | 41.57 | 1330.02 | 1.47 | | | | 1000.00 | + | 174 | 2019-06-24 | 50.00 | | 41.53 | 1281.45 | 1.42 | | | | 1000.00 | + | 175 | 2019-06-25 | 50.00 | | 41.48 | 1232.81 | 1.37 | | | | 1000.00 | + | 176 | 2019-06-26 | 50.00 | | 41.44 | 1184.13 | 1.32 | | | | 1000.00 | + | 177 | 2019-06-27 | 50.00 | | 41.39 | 1135.39 | 1.26 | | | | 1000.00 | + | 178 | 2019-06-28 | 50.00 | | 41.35 | 1086.61 | 1.21 | | | | 1000.00 | + | 179 | 2019-06-29 | 50.00 | | 41.31 | 1037.77 | 1.16 | | | | 1000.00 | + | 180 | 2019-06-30 | 50.00 | | 41.26 | 988.88 | 1.11 | | | | 1000.00 | + | 181 | 2019-07-01 | 50.00 | | 41.22 | 939.93 | 1.06 | | | | 1000.00 | + | 182 | 2019-07-02 | 50.00 | | 41.17 | 890.93 | 1.00 | | | | 1000.00 | + | 183 | 2019-07-03 | 50.00 | | 41.13 | 841.89 | 0.95 | | | | 1000.00 | + | 184 | 2019-07-04 | 50.00 | | 41.09 | 792.79 | 0.90 | | | | 1000.00 | + | 185 | 2019-07-05 | 50.00 | | 41.04 | 743.63 | 0.85 | | | | 1000.00 | + | 186 | 2019-07-06 | 50.00 | | 41.00 | 694.43 | 0.79 | | | | 1000.00 | + | 187 | 2019-07-07 | 50.00 | | 40.95 | 645.17 | 0.74 | | | | 1000.00 | + | 188 | 2019-07-08 | 50.00 | | 40.91 | 595.86 | 0.69 | | | | 1000.00 | + | 189 | 2019-07-09 | 50.00 | | 40.87 | 546.49 | 0.64 | | | | 1000.00 | + | 190 | 2019-07-10 | 50.00 | | 40.82 | 497.08 | 0.58 | | | | 1000.00 | + | 191 | 2019-07-11 | 50.00 | | 40.78 | 447.61 | 0.53 | | | | 1000.00 | + | 192 | 2019-07-12 | 50.00 | | 40.74 | 398.08 | 0.48 | | | | 1000.00 | + | 193 | 2019-07-13 | 50.00 | | 40.69 | 348.51 | 0.43 | | | | 1000.00 | + | 194 | 2019-07-14 | 50.00 | | 40.65 | 298.88 | 0.37 | | | | 1000.00 | + | 195 | 2019-07-15 | 50.00 | | 40.61 | 249.20 | 0.32 | | | | 1000.00 | + | 196 | 2019-07-16 | 50.00 | | 40.56 | 199.47 | 0.27 | | | | 1000.00 | + | 197 | 2019-07-17 | 50.00 | | 40.52 | 149.68 | 0.21 | | | | 1000.00 | + | 198 | 2019-07-18 | 50.00 | | 40.48 | 99.84 | 0.16 | | | | 1000.00 | + | 199 | 2019-07-19 | 50.00 | | 40.43 | 49.95 | 0.11 | | | | 1000.00 | + | 200 | 2019-07-20 | 50.00 | | 40.39 | 0.00 | 0.05 | | | | 1000.00 | + + When Admin sets the business date to "25 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin update Working Capital period payment rate with "17" value + And Admin retrieves the projected amortization schedule + Then The retrieved amortization schedule has the following summary fields: + | discountFeeAmount | netDisbursementAmount | totalPaymentValue | periodPaymentRate | npvDayCount | expectedPaymentAmount | originalPaymentNumber | + | 1000.00 | 9000.00 | 1000.00 | 18 | 360 | 50.00 | 200 | + And The retrieved amortization schedule has payments with the following details: + | paymentNo | date | expectedPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | actualPaymentAmount | actualAmortizationAmount | incomeModification | deferredBalance | + | 0 | 2019-01-01 | -9000.00 | | -9000.00 | 9000.00 | | | | | 1000.00 | + | 1 | 2019-01-02 | 50.00 | | 49.95 | 8959.61 | 9.61 | | | | 1000.00 | + | 2 | 2019-01-03 | 50.00 | | 49.89 | 8919.18 | 9.57 | | | | 1000.00 | + | 3 | 2019-01-04 | 50.00 | | 49.84 | 8878.70 | 9.52 | | | | 1000.00 | + | 4 | 2019-01-05 | 50.00 | | 49.79 | 8838.18 | 9.48 | | | | 1000.00 | + | 5 | 2019-01-06 | 50.00 | | 49.73 | 8797.62 | 9.44 | | | | 1000.00 | + | 6 | 2019-01-07 | 50.00 | | 49.68 | 8757.01 | 9.39 | | | | 1000.00 | + | 7 | 2019-01-08 | 50.00 | | 49.63 | 8716.36 | 9.35 | | | | 1000.00 | + | 8 | 2019-01-09 | 50.00 | | 49.57 | 8675.67 | 9.31 | | | | 1000.00 | + | 9 | 2019-01-10 | 50.00 | | 49.52 | 8634.94 | 9.26 | | | | 1000.00 | + | 10 | 2019-01-11 | 50.00 | | 49.47 | 8594.16 | 9.22 | | | | 1000.00 | + | 11 | 2019-01-12 | 50.00 | | 49.42 | 8553.33 | 9.18 | | | | 1000.00 | + | 12 | 2019-01-13 | 50.00 | | 49.36 | 8512.47 | 9.13 | | | | 1000.00 | + | 13 | 2019-01-14 | 50.00 | | 49.31 | 8471.56 | 9.09 | | | | 1000.00 | + | 14 | 2019-01-15 | 50.00 | | 49.26 | 8430.60 | 9.05 | | | | 1000.00 | + | 15 | 2019-01-16 | 50.00 | | 49.21 | 8389.61 | 9.00 | | | | 1000.00 | + | 16 | 2019-01-17 | 50.00 | | 49.15 | 8348.56 | 8.96 | | | | 1000.00 | + | 17 | 2019-01-18 | 50.00 | | 49.10 | 8307.48 | 8.91 | | | | 1000.00 | + | 18 | 2019-01-19 | 50.00 | | 49.05 | 8266.35 | 8.87 | | | | 1000.00 | + | 19 | 2019-01-20 | 50.00 | | 49.00 | 8225.18 | 8.83 | | | | 1000.00 | + | 20 | 2019-01-21 | 50.00 | | 48.94 | 8183.96 | 8.78 | | | | 1000.00 | + | 21 | 2019-01-22 | 50.00 | | 48.89 | 8142.70 | 8.74 | | | | 1000.00 | + | 22 | 2019-01-23 | 50.00 | | 48.84 | 8101.39 | 8.69 | | | | 1000.00 | + | 23 | 2019-01-24 | 50.00 | | 48.79 | 8060.04 | 8.65 | | | | 1000.00 | + | 24 | 2019-01-25 | 47.22 | | 44.87 | 7988.51 | 17.08 | | | | 1000.00 | + | 25 | 2019-01-26 | 47.22 | | 44.77 | 7958.30 | 17.02 | | | | 1000.00 | + | 26 | 2019-01-27 | 47.22 | | 44.68 | 7928.04 | 16.95 | | | | 1000.00 | + | 27 | 2019-01-28 | 47.22 | | 44.58 | 7897.70 | 16.89 | | | | 1000.00 | + | 28 | 2019-01-29 | 47.22 | | 44.49 | 7867.30 | 16.82 | | | | 1000.00 | + | 29 | 2019-01-30 | 47.22 | | 44.39 | 7836.84 | 16.76 | | | | 1000.00 | + | 30 | 2019-01-31 | 47.22 | | 44.30 | 7806.31 | 16.69 | | | | 1000.00 | + | 31 | 2019-02-01 | 47.22 | | 44.21 | 7775.72 | 16.63 | | | | 1000.00 | + | 32 | 2019-02-02 | 47.22 | | 44.11 | 7745.06 | 16.56 | | | | 1000.00 | + | 33 | 2019-02-03 | 47.22 | | 44.02 | 7714.34 | 16.50 | | | | 1000.00 | + | 34 | 2019-02-04 | 47.22 | | 43.92 | 7683.55 | 16.43 | | | | 1000.00 | + | 35 | 2019-02-05 | 47.22 | | 43.83 | 7652.69 | 16.37 | | | | 1000.00 | + | 36 | 2019-02-06 | 47.22 | | 43.74 | 7621.77 | 16.30 | | | | 1000.00 | + | 37 | 2019-02-07 | 47.22 | | 43.65 | 7590.79 | 16.23 | | | | 1000.00 | + | 38 | 2019-02-08 | 47.22 | | 43.55 | 7559.74 | 16.17 | | | | 1000.00 | + | 39 | 2019-02-09 | 47.22 | | 43.46 | 7528.62 | 16.10 | | | | 1000.00 | + | 40 | 2019-02-10 | 47.22 | | 43.37 | 7497.43 | 16.04 | | | | 1000.00 | + | 41 | 2019-02-11 | 47.22 | | 43.28 | 7466.18 | 15.97 | | | | 1000.00 | + | 42 | 2019-02-12 | 47.22 | | 43.18 | 7434.86 | 15.90 | | | | 1000.00 | + | 43 | 2019-02-13 | 47.22 | | 43.09 | 7403.48 | 15.84 | | | | 1000.00 | + | 44 | 2019-02-14 | 47.22 | | 43.00 | 7372.03 | 15.77 | | | | 1000.00 | + | 45 | 2019-02-15 | 47.22 | | 42.91 | 7340.51 | 15.70 | | | | 1000.00 | + | 46 | 2019-02-16 | 47.22 | | 42.82 | 7308.93 | 15.63 | | | | 1000.00 | + | 47 | 2019-02-17 | 47.22 | | 42.73 | 7277.27 | 15.57 | | | | 1000.00 | + | 48 | 2019-02-18 | 47.22 | | 42.64 | 7245.55 | 15.50 | | | | 1000.00 | + | 49 | 2019-02-19 | 47.22 | | 42.54 | 7213.77 | 15.43 | | | | 1000.00 | + | 50 | 2019-02-20 | 47.22 | | 42.45 | 7181.91 | 15.36 | | | | 1000.00 | + | 51 | 2019-02-21 | 47.22 | | 42.36 | 7149.99 | 15.30 | | | | 1000.00 | + | 52 | 2019-02-22 | 47.22 | | 42.27 | 7118.00 | 15.23 | | | | 1000.00 | + | 53 | 2019-02-23 | 47.22 | | 42.18 | 7085.94 | 15.16 | | | | 1000.00 | + | 54 | 2019-02-24 | 47.22 | | 42.09 | 7053.81 | 15.09 | | | | 1000.00 | + | 55 | 2019-02-25 | 47.22 | | 42.01 | 7021.62 | 15.02 | | | | 1000.00 | + | 56 | 2019-02-26 | 47.22 | | 41.92 | 6989.35 | 14.96 | | | | 1000.00 | + | 57 | 2019-02-27 | 47.22 | | 41.83 | 6957.02 | 14.89 | | | | 1000.00 | + | 58 | 2019-02-28 | 47.22 | | 41.74 | 6924.62 | 14.82 | | | | 1000.00 | + | 59 | 2019-03-01 | 47.22 | | 41.65 | 6892.15 | 14.75 | | | | 1000.00 | + | 60 | 2019-03-02 | 47.22 | | 41.56 | 6859.61 | 14.68 | | | | 1000.00 | + | 61 | 2019-03-03 | 47.22 | | 41.47 | 6827.00 | 14.61 | | | | 1000.00 | + | 62 | 2019-03-04 | 47.22 | | 41.38 | 6794.32 | 14.54 | | | | 1000.00 | + | 63 | 2019-03-05 | 47.22 | | 41.30 | 6761.57 | 14.47 | | | | 1000.00 | + | 64 | 2019-03-06 | 47.22 | | 41.21 | 6728.75 | 14.40 | | | | 1000.00 | + | 65 | 2019-03-07 | 47.22 | | 41.12 | 6695.86 | 14.33 | | | | 1000.00 | + | 66 | 2019-03-08 | 47.22 | | 41.03 | 6662.90 | 14.26 | | | | 1000.00 | + | 67 | 2019-03-09 | 47.22 | | 40.95 | 6629.88 | 14.19 | | | | 1000.00 | + | 68 | 2019-03-10 | 47.22 | | 40.86 | 6596.78 | 14.12 | | | | 1000.00 | + | 69 | 2019-03-11 | 47.22 | | 40.77 | 6563.61 | 14.05 | | | | 1000.00 | + | 70 | 2019-03-12 | 47.22 | | 40.69 | 6530.37 | 13.98 | | | | 1000.00 | + | 71 | 2019-03-13 | 47.22 | | 40.60 | 6497.06 | 13.91 | | | | 1000.00 | + | 72 | 2019-03-14 | 47.22 | | 40.51 | 6463.68 | 13.84 | | | | 1000.00 | + | 73 | 2019-03-15 | 47.22 | | 40.43 | 6430.22 | 13.77 | | | | 1000.00 | + | 74 | 2019-03-16 | 47.22 | | 40.34 | 6396.70 | 13.70 | | | | 1000.00 | + | 75 | 2019-03-17 | 47.22 | | 40.26 | 6363.10 | 13.62 | | | | 1000.00 | + | 76 | 2019-03-18 | 47.22 | | 40.17 | 6329.44 | 13.55 | | | | 1000.00 | + | 77 | 2019-03-19 | 47.22 | | 40.08 | 6295.70 | 13.48 | | | | 1000.00 | + | 78 | 2019-03-20 | 47.22 | | 40.00 | 6261.89 | 13.41 | | | | 1000.00 | + | 79 | 2019-03-21 | 47.22 | | 39.91 | 6228.00 | 13.34 | | | | 1000.00 | + | 80 | 2019-03-22 | 47.22 | | 39.83 | 6194.05 | 13.27 | | | | 1000.00 | + | 81 | 2019-03-23 | 47.22 | | 39.74 | 6160.02 | 13.19 | | | | 1000.00 | + | 82 | 2019-03-24 | 47.22 | | 39.66 | 6125.92 | 13.12 | | | | 1000.00 | + | 83 | 2019-03-25 | 47.22 | | 39.58 | 6091.75 | 13.05 | | | | 1000.00 | + | 84 | 2019-03-26 | 47.22 | | 39.49 | 6057.51 | 12.98 | | | | 1000.00 | + | 85 | 2019-03-27 | 47.22 | | 39.41 | 6023.19 | 12.90 | | | | 1000.00 | + | 86 | 2019-03-28 | 47.22 | | 39.32 | 5988.80 | 12.83 | | | | 1000.00 | + | 87 | 2019-03-29 | 47.22 | | 39.24 | 5954.33 | 12.76 | | | | 1000.00 | + | 88 | 2019-03-30 | 47.22 | | 39.16 | 5919.80 | 12.68 | | | | 1000.00 | + | 89 | 2019-03-31 | 47.22 | | 39.07 | 5885.18 | 12.61 | | | | 1000.00 | + | 90 | 2019-04-01 | 47.22 | | 38.99 | 5850.50 | 12.54 | | | | 1000.00 | + | 91 | 2019-04-02 | 47.22 | | 38.91 | 5815.74 | 12.46 | | | | 1000.00 | + | 92 | 2019-04-03 | 47.22 | | 38.83 | 5780.91 | 12.39 | | | | 1000.00 | + | 93 | 2019-04-04 | 47.22 | | 38.74 | 5746.00 | 12.31 | | | | 1000.00 | + | 94 | 2019-04-05 | 47.22 | | 38.66 | 5711.02 | 12.24 | | | | 1000.00 | + | 95 | 2019-04-06 | 47.22 | | 38.58 | 5675.96 | 12.16 | | | | 1000.00 | + | 96 | 2019-04-07 | 47.22 | | 38.50 | 5640.83 | 12.09 | | | | 1000.00 | + | 97 | 2019-04-08 | 47.22 | | 38.41 | 5605.63 | 12.01 | | | | 1000.00 | + | 98 | 2019-04-09 | 47.22 | | 38.33 | 5570.35 | 11.94 | | | | 1000.00 | + | 99 | 2019-04-10 | 47.22 | | 38.25 | 5534.99 | 11.86 | | | | 1000.00 | + | 100 | 2019-04-11 | 47.22 | | 38.17 | 5499.56 | 11.79 | | | | 1000.00 | + | 101 | 2019-04-12 | 47.22 | | 38.09 | 5464.05 | 11.71 | | | | 1000.00 | + | 102 | 2019-04-13 | 47.22 | | 38.01 | 5428.47 | 11.64 | | | | 1000.00 | + | 103 | 2019-04-14 | 47.22 | | 37.93 | 5392.82 | 11.56 | | | | 1000.00 | + | 104 | 2019-04-15 | 47.22 | | 37.85 | 5357.08 | 11.49 | | | | 1000.00 | + | 105 | 2019-04-16 | 47.22 | | 37.77 | 5321.27 | 11.41 | | | | 1000.00 | + | 106 | 2019-04-17 | 47.22 | | 37.69 | 5285.39 | 11.33 | | | | 1000.00 | + | 107 | 2019-04-18 | 47.22 | | 37.61 | 5249.42 | 11.26 | | | | 1000.00 | + | 108 | 2019-04-19 | 47.22 | | 37.53 | 5213.38 | 11.18 | | | | 1000.00 | + | 109 | 2019-04-20 | 47.22 | | 37.45 | 5177.27 | 11.10 | | | | 1000.00 | + | 110 | 2019-04-21 | 47.22 | | 37.37 | 5141.08 | 11.03 | | | | 1000.00 | + | 111 | 2019-04-22 | 47.22 | | 37.29 | 5104.81 | 10.95 | | | | 1000.00 | + | 112 | 2019-04-23 | 47.22 | | 37.21 | 5068.46 | 10.87 | | | | 1000.00 | + | 113 | 2019-04-24 | 47.22 | | 37.13 | 5032.03 | 10.80 | | | | 1000.00 | + | 114 | 2019-04-25 | 47.22 | | 37.05 | 4995.53 | 10.72 | | | | 1000.00 | + | 115 | 2019-04-26 | 47.22 | | 36.97 | 4958.95 | 10.64 | | | | 1000.00 | + | 116 | 2019-04-27 | 47.22 | | 36.89 | 4922.30 | 10.56 | | | | 1000.00 | + | 117 | 2019-04-28 | 47.22 | | 36.81 | 4885.56 | 10.48 | | | | 1000.00 | + | 118 | 2019-04-29 | 47.22 | | 36.74 | 4848.75 | 10.41 | | | | 1000.00 | + | 119 | 2019-04-30 | 47.22 | | 36.66 | 4811.85 | 10.33 | | | | 1000.00 | + | 120 | 2019-05-01 | 47.22 | | 36.58 | 4774.88 | 10.25 | | | | 1000.00 | + | 121 | 2019-05-02 | 47.22 | | 36.50 | 4737.83 | 10.17 | | | | 1000.00 | + | 122 | 2019-05-03 | 47.22 | | 36.42 | 4700.70 | 10.09 | | | | 1000.00 | + | 123 | 2019-05-04 | 47.22 | | 36.35 | 4663.50 | 10.01 | | | | 1000.00 | + | 124 | 2019-05-05 | 47.22 | | 36.27 | 4626.21 | 9.93 | | | | 1000.00 | + | 125 | 2019-05-06 | 47.22 | | 36.19 | 4588.84 | 9.85 | | | | 1000.00 | + | 126 | 2019-05-07 | 47.22 | | 36.12 | 4551.40 | 9.77 | | | | 1000.00 | + | 127 | 2019-05-08 | 47.22 | | 36.04 | 4513.87 | 9.69 | | | | 1000.00 | + | 128 | 2019-05-09 | 47.22 | | 35.96 | 4476.26 | 9.61 | | | | 1000.00 | + | 129 | 2019-05-10 | 47.22 | | 35.89 | 4438.58 | 9.53 | | | | 1000.00 | + | 130 | 2019-05-11 | 47.22 | | 35.81 | 4400.81 | 9.45 | | | | 1000.00 | + | 131 | 2019-05-12 | 47.22 | | 35.73 | 4362.97 | 9.37 | | | | 1000.00 | + | 132 | 2019-05-13 | 47.22 | | 35.66 | 4325.04 | 9.29 | | | | 1000.00 | + | 133 | 2019-05-14 | 47.22 | | 35.58 | 4287.03 | 9.21 | | | | 1000.00 | + | 134 | 2019-05-15 | 47.22 | | 35.51 | 4248.94 | 9.13 | | | | 1000.00 | + | 135 | 2019-05-16 | 47.22 | | 35.43 | 4210.77 | 9.05 | | | | 1000.00 | + | 136 | 2019-05-17 | 47.22 | | 35.36 | 4172.52 | 8.97 | | | | 1000.00 | + | 137 | 2019-05-18 | 47.22 | | 35.28 | 4134.19 | 8.89 | | | | 1000.00 | + | 138 | 2019-05-19 | 47.22 | | 35.21 | 4095.77 | 8.81 | | | | 1000.00 | + | 139 | 2019-05-20 | 47.22 | | 35.13 | 4057.28 | 8.72 | | | | 1000.00 | + | 140 | 2019-05-21 | 47.22 | | 35.06 | 4018.70 | 8.64 | | | | 1000.00 | + | 141 | 2019-05-22 | 47.22 | | 34.98 | 3980.04 | 8.56 | | | | 1000.00 | + | 142 | 2019-05-23 | 47.22 | | 34.91 | 3941.30 | 8.48 | | | | 1000.00 | + | 143 | 2019-05-24 | 47.22 | | 34.83 | 3902.47 | 8.39 | | | | 1000.00 | + | 144 | 2019-05-25 | 47.22 | | 34.76 | 3863.56 | 8.31 | | | | 1000.00 | + | 145 | 2019-05-26 | 47.22 | | 34.68 | 3824.57 | 8.23 | | | | 1000.00 | + | 146 | 2019-05-27 | 47.22 | | 34.61 | 3785.50 | 8.15 | | | | 1000.00 | + | 147 | 2019-05-28 | 47.22 | | 34.54 | 3746.34 | 8.06 | | | | 1000.00 | + | 148 | 2019-05-29 | 47.22 | | 34.46 | 3707.10 | 7.98 | | | | 1000.00 | + | 149 | 2019-05-30 | 47.22 | | 34.39 | 3667.78 | 7.90 | | | | 1000.00 | + | 150 | 2019-05-31 | 47.22 | | 34.32 | 3628.37 | 7.81 | | | | 1000.00 | + | 151 | 2019-06-01 | 47.22 | | 34.24 | 3588.88 | 7.73 | | | | 1000.00 | + | 152 | 2019-06-02 | 47.22 | | 34.17 | 3549.30 | 7.64 | | | | 1000.00 | + | 153 | 2019-06-03 | 47.22 | | 34.10 | 3509.64 | 7.56 | | | | 1000.00 | + | 154 | 2019-06-04 | 47.22 | | 34.03 | 3469.90 | 7.48 | | | | 1000.00 | + | 155 | 2019-06-05 | 47.22 | | 33.95 | 3430.07 | 7.39 | | | | 1000.00 | + | 156 | 2019-06-06 | 47.22 | | 33.88 | 3390.15 | 7.31 | | | | 1000.00 | + | 157 | 2019-06-07 | 47.22 | | 33.81 | 3350.15 | 7.22 | | | | 1000.00 | + | 158 | 2019-06-08 | 47.22 | | 33.74 | 3310.07 | 7.14 | | | | 1000.00 | + | 159 | 2019-06-09 | 47.22 | | 33.67 | 3269.90 | 7.05 | | | | 1000.00 | + | 160 | 2019-06-10 | 47.22 | | 33.60 | 3229.64 | 6.96 | | | | 1000.00 | + | 161 | 2019-06-11 | 47.22 | | 33.52 | 3189.30 | 6.88 | | | | 1000.00 | + | 162 | 2019-06-12 | 47.22 | | 33.45 | 3148.88 | 6.79 | | | | 1000.00 | + | 163 | 2019-06-13 | 47.22 | | 33.38 | 3108.36 | 6.71 | | | | 1000.00 | + | 164 | 2019-06-14 | 47.22 | | 33.31 | 3067.76 | 6.62 | | | | 1000.00 | + | 165 | 2019-06-15 | 47.22 | | 33.24 | 3027.08 | 6.53 | | | | 1000.00 | + | 166 | 2019-06-16 | 47.22 | | 33.17 | 2986.31 | 6.45 | | | | 1000.00 | + | 167 | 2019-06-17 | 47.22 | | 33.10 | 2945.45 | 6.36 | | | | 1000.00 | + | 168 | 2019-06-18 | 47.22 | | 33.03 | 2904.50 | 6.27 | | | | 1000.00 | + | 169 | 2019-06-19 | 47.22 | | 32.96 | 2863.47 | 6.19 | | | | 1000.00 | + | 170 | 2019-06-20 | 47.22 | | 32.89 | 2822.35 | 6.10 | | | | 1000.00 | + | 171 | 2019-06-21 | 47.22 | | 32.82 | 2781.14 | 6.01 | | | | 1000.00 | + | 172 | 2019-06-22 | 47.22 | | 32.75 | 2739.84 | 5.92 | | | | 1000.00 | + | 173 | 2019-06-23 | 47.22 | | 32.68 | 2698.46 | 5.84 | | | | 1000.00 | + | 174 | 2019-06-24 | 47.22 | | 32.61 | 2656.98 | 5.75 | | | | 1000.00 | + | 175 | 2019-06-25 | 47.22 | | 32.54 | 2615.42 | 5.66 | | | | 1000.00 | + | 176 | 2019-06-26 | 47.22 | | 32.47 | 2573.77 | 5.57 | | | | 1000.00 | + | 177 | 2019-06-27 | 47.22 | | 32.40 | 2532.04 | 5.48 | | | | 1000.00 | + | 178 | 2019-06-28 | 47.22 | | 32.33 | 2490.21 | 5.39 | | | | 1000.00 | + | 179 | 2019-06-29 | 47.22 | | 32.26 | 2448.29 | 5.30 | | | | 1000.00 | + | 180 | 2019-06-30 | 47.22 | | 32.20 | 2406.29 | 5.21 | | | | 1000.00 | + | 181 | 2019-07-01 | 47.22 | | 32.13 | 2364.19 | 5.13 | | | | 1000.00 | + | 182 | 2019-07-02 | 47.22 | | 32.06 | 2322.01 | 5.04 | | | | 1000.00 | + | 183 | 2019-07-03 | 47.22 | | 31.99 | 2279.73 | 4.95 | | | | 1000.00 | + | 184 | 2019-07-04 | 47.22 | | 31.92 | 2237.37 | 4.86 | | | | 1000.00 | + | 185 | 2019-07-05 | 47.22 | | 31.86 | 2194.91 | 4.77 | | | | 1000.00 | + | 186 | 2019-07-06 | 47.22 | | 31.79 | 2152.37 | 4.68 | | | | 1000.00 | + | 187 | 2019-07-07 | 47.22 | | 31.72 | 2109.73 | 4.58 | | | | 1000.00 | + | 188 | 2019-07-08 | 47.22 | | 31.65 | 2067.01 | 4.49 | | | | 1000.00 | + | 189 | 2019-07-09 | 47.22 | | 31.59 | 2024.19 | 4.40 | | | | 1000.00 | + | 190 | 2019-07-10 | 47.22 | | 31.52 | 1981.28 | 4.31 | | | | 1000.00 | + | 191 | 2019-07-11 | 47.22 | | 31.45 | 1938.28 | 4.22 | | | | 1000.00 | + | 192 | 2019-07-12 | 47.22 | | 31.38 | 1895.19 | 4.13 | | | | 1000.00 | + | 193 | 2019-07-13 | 47.22 | | 31.32 | 1852.01 | 4.04 | | | | 1000.00 | + | 194 | 2019-07-14 | 47.22 | | 31.25 | 1808.73 | 3.94 | | | | 1000.00 | + | 195 | 2019-07-15 | 47.22 | | 31.18 | 1765.36 | 3.85 | | | | 1000.00 | + | 196 | 2019-07-16 | 47.22 | | 31.12 | 1721.90 | 3.76 | | | | 1000.00 | + | 197 | 2019-07-17 | 47.22 | | 31.05 | 1678.35 | 3.67 | | | | 1000.00 | + | 198 | 2019-07-18 | 47.22 | | 30.99 | 1634.71 | 3.57 | | | | 1000.00 | + | 199 | 2019-07-19 | 47.22 | | 30.92 | 1590.97 | 3.48 | | | | 1000.00 | + | 200 | 2019-07-20 | 47.22 | | 30.85 | 1547.14 | 3.39 | | | | 1000.00 | + | 201 | 2019-07-21 | 47.22 | | 30.79 | 1503.21 | 3.30 | | | | 1000.00 | + | 202 | 2019-07-22 | 47.22 | | 30.72 | 1459.19 | 3.20 | | | | 1000.00 | + | 203 | 2019-07-23 | 47.22 | | 30.66 | 1415.08 | 3.11 | | | | 1000.00 | + | 204 | 2019-07-24 | 47.22 | | 30.59 | 1370.88 | 3.01 | | | | 1000.00 | + | 205 | 2019-07-25 | 47.22 | | 30.53 | 1326.58 | 2.92 | | | | 1000.00 | + | 206 | 2019-07-26 | 47.22 | | 30.46 | 1282.18 | 2.83 | | | | 1000.00 | + | 207 | 2019-07-27 | 47.22 | | 30.40 | 1237.69 | 2.73 | | | | 1000.00 | + | 208 | 2019-07-28 | 47.22 | | 30.33 | 1193.11 | 2.64 | | | | 1000.00 | + | 209 | 2019-07-29 | 47.22 | | 30.27 | 1148.43 | 2.54 | | | | 1000.00 | + | 210 | 2019-07-30 | 47.22 | | 30.20 | 1103.66 | 2.45 | | | | 1000.00 | + | 211 | 2019-07-31 | 47.22 | | 30.14 | 1058.79 | 2.35 | | | | 1000.00 | + | 212 | 2019-08-01 | 47.22 | | 30.08 | 1013.82 | 2.26 | | | | 1000.00 | + | 213 | 2019-08-02 | 47.22 | | 30.01 | 968.76 | 2.16 | | | | 1000.00 | + | 214 | 2019-08-03 | 47.22 | | 29.95 | 923.60 | 2.06 | | | | 1000.00 | + | 215 | 2019-08-04 | 47.22 | | 29.89 | 878.35 | 1.97 | | | | 1000.00 | + | 216 | 2019-08-05 | 47.22 | | 29.82 | 833.00 | 1.87 | | | | 1000.00 | + | 217 | 2019-08-06 | 47.22 | | 29.76 | 787.56 | 1.77 | | | | 1000.00 | + | 218 | 2019-08-07 | 47.22 | | 29.70 | 742.01 | 1.68 | | | | 1000.00 | + | 219 | 2019-08-08 | 47.22 | | 29.63 | 696.38 | 1.58 | | | | 1000.00 | + | 220 | 2019-08-09 | 47.22 | | 29.57 | 650.64 | 1.48 | | | | 1000.00 | + | 221 | 2019-08-10 | 47.22 | | 29.51 | 604.80 | 1.39 | | | | 1000.00 | + | 222 | 2019-08-11 | 47.22 | | 29.44 | 558.87 | 1.29 | | | | 1000.00 | + | 223 | 2019-08-12 | 47.22 | | 29.38 | 512.84 | 1.19 | | | | 1000.00 | + | 224 | 2019-08-13 | 47.22 | | 29.32 | 466.72 | 1.09 | | | | 1000.00 | + | 225 | 2019-08-14 | 47.22 | | 29.26 | 420.49 | 0.99 | | | | 1000.00 | + | 226 | 2019-08-15 | 47.22 | | 29.19 | 374.16 | 0.90 | | | | 1000.00 | + | 227 | 2019-08-16 | 47.22 | | 29.13 | 327.74 | 0.80 | | | | 1000.00 | + | 228 | 2019-08-17 | 47.22 | | 29.07 | 281.22 | 0.70 | | | | 1000.00 | + | 229 | 2019-08-18 | 47.22 | | 29.01 | 234.60 | 0.60 | | | | 1000.00 | + | 230 | 2019-08-19 | 47.22 | | 28.95 | 187.88 | 0.50 | | | | 1000.00 | + | 231 | 2019-08-20 | 47.22 | | 28.89 | 141.06 | 0.40 | | | | 1000.00 | + | 232 | 2019-08-21 | 47.22 | | 28.82 | 94.14 | 0.30 | | | | 1000.00 | + | 233 | 2019-08-22 | 47.22 | | 28.76 | 47.12 | 0.20 | | | | 1000.00 | + | 234 | 2019-08-23 | 47.22 | | 28.70 | 0.00 | 0.10 | | | | 1000.00 | diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanAccount.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanAccount.feature index 9bbd0171376..f66082be159 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanAccount.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanAccount.feature @@ -1239,3 +1239,61 @@ Feature: WorkingCapitalLoanAccount | near_breach_id | | 0 | | 9223372036854775807 | + + @TestRailId:C78813 + Scenario Outline: Period payment rate on create of WCL account failed with outranged from loan product level rate change value - UC1 + When Admin sets the business date to "01 January 2026" + And Admin creates a client with random data + And Admin failed to create Working Capital with period payment rate "" value and outcomes with error message with default following data: + | LoanProduct | submittedOnDate | + | WCLP_PERIOD_PAYMENT_RATE | 01 January 2026 | + + Examples: + | rate_change_value | rate_change_error_message | + | 0.5 | Failed data validation due to: must.be.greater.than.or.equal.to.min. | + | 99.5 | Failed data validation due to: must.be.less.than.or.equal.to.max. | + + @TestRailId:C78814 + Scenario Outline: Period payment rate on create of WCL account failed with invalid rate change value - UC2 + When Admin sets the business date to "01 January 2026" + And Admin creates a client with random data + And Admin failed to create Working Capital on "01 January 2026" with period payment rate "" value and outcomes with error message + + Examples: + | rate_change_value | rate_change_error_message | + | -1 | The parameter `periodPaymentRate` must be greater than or equal to 0. | + + @TestRailId:C78815 + Scenario Outline: Period payment rate on modify of WCL account failed with with outranged from loan product level rate change value - UC3 + When Admin sets the business date to "01 January 2026" + And Admin creates a client with random data + And Admin creates a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_PERIOD_PAYMENT_RATE | 01 January 2026 | 01 January 2026 | 100 | 100 | 12.5 | 15 | + Then Working capital loan creation was successful + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP_PERIOD_PAYMENT_RATE | 2026-01-01 | 2026-01-01 | Submitted and pending approval | 100.0 | 0.0 | 100.0 | 12.5 | 15.0 | + Then Admin failed to modify WC loan account with period payment rate "" value and outcomes with "" error message + + Examples: + | rate_change_value | rate_change_error_message | + | 0.5 | Failed data validation due to: must.be.greater.than.or.equal.to.min. | + | 99.5 | Failed data validation due to: must.be.less.than.or.equal.to.max. | + + @TestRailId:C78816 + Scenario Outline: Period payment rate on modify of WCL account failed with invalid rate change value - UC4 + When Admin sets the business date to "01 January 2026" + And Admin creates a client with random data + And Admin creates a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP | 01 January 2026 | 01 January 2026 | 100 | 100 | 12.5 | 15 | + Then Working capital loan creation was successful + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP | 2026-01-01 | 2026-01-01 | Submitted and pending approval | 100.0 | 0.0 | 100.0 | 12.5 | 15.0 | + Then Admin failed to modify WC loan account with period payment rate "" value and outcomes with "" error message + + Examples: + | rate_change_value | rate_change_error_message | + | -1 | The parameter `periodPaymentRate` must be greater than or equal to 0. | diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanRepayment.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanRepayment.feature index d36c7a47722..5acdf439ba4 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanRepayment.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanRepayment.feature @@ -26,11 +26,11 @@ Feature: Working Capital Loan Repayment | periodNumber | fromDate | toDate | expectedAmount | paidAmount | outstandingAmount | minPaymentCriteriaMet | delinquentAmount | delinquentDays | | 1 | 2026-01-01 | 2026-01-30 | 270.0 | 270.0 | 0.0 | true | 0.0 | 0 | Then Working Capital loan amortization schedule has 4 periods, with the following data for periods: - | paymentNo | paymentDate | count | paymentsLeft | expectedPaymentAmount | forecastPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | netAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | - | 0 | 01 January 2026 | 3 | 0 | -9000.00 | | | 1 | -9000.00 | 9000.00 | | | | | 0.00 | - | 1 | 02 January 2026 | 2 | 0 | 5000.00 | 5000.00 | 270.00 | 1 | 270.00 | 4658.91 | 0.00 | 0.00 | 35.58 | 35.58 | 0.00 | - | 2 | 03 January 2026 | 1 | 1 | 5000.00 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | 0.00 | | 0.00 | 0.00 | - | 3 | 04 January 2026 | 0 | 2 | | 4730.00 | | 0.8682178936723646887 | 4106.67 | | | 0.00 | | | 0.00 | + | paymentNo | paymentDate | expectedPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | + | 0 | 01 January 2026 | -9000.00 | | 1 | -9000.00 | 9000.00 | | | | 0.00 | + | 1 | 02 January 2026 | 5000.00 | 270.00 | 1 | 270.00 | 4658.91 | 0.00 | 35.58 | 35.58 | 0.00 | + | 2 | 03 January 2026 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | | | 0.00 | + | 3 | 04 January 2026 | | | 0.8682178936723646887 | 4106.67 | | | | | 0.00 | @TestRailId:C76618 Scenario: Verify working capital loan repayment - UC2: simple repayment by loan external ID @@ -57,11 +57,11 @@ Feature: Working Capital Loan Repayment | periodNumber | fromDate | toDate | expectedAmount | paidAmount | outstandingAmount | minPaymentCriteriaMet | delinquentAmount | delinquentDays | | 1 | 2026-01-01 | 2026-01-30 | 270.0 | 270.0 | 0.0 | true | 0.0 | 0 | Then Working Capital loan amortization schedule has 4 periods, with the following data for periods: - | paymentNo | paymentDate | count | paymentsLeft | expectedPaymentAmount | forecastPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | netAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | - | 0 | 01 January 2026 | 3 | 0 | -9000.00 | | | 1 | -9000.00 | 9000.00 | | | | | 0.00 | - | 1 | 02 January 2026 | 2 | 0 | 5000.00 | 5000.00 | 270.00 | 1 | 270.00 | 4658.91 | 0.00 | 0.00 | 35.58 | 35.58 | 0.00 | - | 2 | 03 January 2026 | 1 | 1 | 5000.00 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | 0.00 | | 0.00 | 0.00 | - | 3 | 04 January 2026 | 0 | 2 | | 4730.00 | | 0.8682178936723646887 | 4106.67 | | | 0.00 | | | 0.00 | + | paymentNo | paymentDate | expectedPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | + | 0 | 01 January 2026 | -9000.00 | | 1 | -9000.00 | 9000.00 | | | | 0.00 | + | 1 | 02 January 2026 | 5000.00 | 270.00 | 1 | 270.00 | 4658.91 | 0.00 | 35.58 | 35.58 | 0.00 | + | 2 | 03 January 2026 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | | | 0.00 | + | 3 | 04 January 2026 | | | 0.8682178936723646887 | 4106.67 | | | | | 0.00 | @TestRailId:C76619 Scenario: Verify working capital loan repayment - UC3: simple repayment with zero amount results an error (Negative) @@ -161,11 +161,11 @@ Feature: Working Capital Loan Repayment And Delinquency Tag History for Working Capital loan has lines: | periodNumber | addedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays | Then Working Capital loan amortization schedule has 4 periods, with the following data for periods: - | paymentNo | paymentDate | count | paymentsLeft | expectedPaymentAmount | forecastPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | netAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | - | 0 | 01 January 2026 | 3 | 0 | -9000.00 | | | 1 | -9000.00 | 9000.00 | | | | | 0.00 | - | 1 | 02 January 2026 | 2 | 0 | 5000.00 | 5000.00 | 270.00 | 1 | 270.00 | 4658.91 | 0.00 | 0.00 | 35.58 | 35.58 | 0.00 | - | 2 | 03 January 2026 | 1 | 1 | 5000.00 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | 0.00 | | 0.00 | 0.00 | - | 3 | 04 January 2026 | 0 | 2 | | 4730.00 | | 0.8682178936723646887 | 4106.67 | | | 0.00 | | | 0.00 | + | paymentNo | paymentDate | expectedPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | + | 0 | 01 January 2026 | -9000.00 | | 1 | -9000.00 | 9000.00 | | | | 0.00 | + | 1 | 02 January 2026 | 5000.00 | 270.00 | 1 | 270.00 | 4658.91 | 0.00 | 35.58 | 35.58 | 0.00 | + | 2 | 03 January 2026 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | | | 0.00 | + | 3 | 04 January 2026 | | | 0.8682178936723646887 | 4106.67 | | | | | 0.00 | @TestRailId:C76623 Scenario: Verify working capital loan repayment - UC7: full expectedAmount repaid after disbursement day @@ -208,11 +208,11 @@ Feature: Working Capital Loan Repayment And Delinquency Tag History for Working Capital loan has lines: | periodNumber | addedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays | Then Working Capital loan amortization schedule has 4 periods, with the following data for periods: - | paymentNo | paymentDate | count | paymentsLeft | expectedPaymentAmount | forecastPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | netAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | - | 0 | 01 January 2026 | 3 | 0 | -9000.00 | | | 1 | -9000.00 | 9000.00 | | | | | 0.00 | - | 1 | 02 January 2026 | 2 | 0 | 5000.00 | 5000.00 | 270.00 | 1 | 270.00 | 4658.91 | 0.00 | 0.00 | 35.58 | 35.58 | 0.00 | - | 2 | 03 January 2026 | 1 | 1 | 5000.00 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | 0.00 | | 0.00 | 0.00 | - | 3 | 04 January 2026 | 0 | 2 | | 4730.00 | | 0.8682178936723646887 | 4106.67 | | | 0.00 | | | 0.00 | + | paymentNo | paymentDate | expectedPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | + | 0 | 01 January 2026 | -9000.00 | | 1 | -9000.00 | 9000.00 | | | | 0.00 | + | 1 | 02 January 2026 | 5000.00 | 270.00 | 1 | 270.00 | 4658.91 | 0.00 | 35.58 | 35.58 | 0.00 | + | 2 | 03 January 2026 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | | | 0.00 | + | 3 | 04 January 2026 | | | 0.8682178936723646887 | 4106.67 | | | | | 0.00 | @TestRailId:C76624 Scenario: Verify working capital loan repayment - UC8: full expectedAmount repaid on last day of 1st period @@ -255,11 +255,11 @@ Feature: Working Capital Loan Repayment And Delinquency Tag History for Working Capital loan has lines: | periodNumber | addedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays | Then Working Capital loan amortization schedule has 4 periods, with the following data for periods: - | paymentNo | paymentDate | count | paymentsLeft | expectedPaymentAmount | forecastPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | netAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | - | 0 | 01 January 2026 | 3 | 0 | -9000.00 | | | 1 | -9000.00 | 9000.00 | | | | | 0.00 | - | 1 | 02 January 2026 | 2 | 0 | 5000.00 | 5000.00 | 270.00 | 1 | 270.00 | 4658.91 | 0.00 | 0.00 | 35.58 | 35.58 | 0.00 | - | 2 | 03 January 2026 | 1 | 1 | 5000.00 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | 0.00 | | 0.00 | 0.00 | - | 3 | 04 January 2026 | 0 | 2 | | 4730.00 | | 0.8682178936723646887 | 4106.67 | | | 0.00 | | | 0.00 | + | paymentNo | paymentDate | expectedPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | + | 0 | 01 January 2026 | -9000.00 | | 1 | -9000.00 | 9000.00 | | | | 0.00 | + | 1 | 02 January 2026 | 5000.00 | 270.00 | 1 | 270.00 | 4658.91 | 0.00 | 35.58 | 35.58 | 0.00 | + | 2 | 03 January 2026 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | | | 0.00 | + | 3 | 04 January 2026 | | | 0.8682178936723646887 | 4106.67 | | | | | 0.00 | @TestRailId:C76625 Scenario: Verify working capital loan repayment - UC9: full expectedAmount repaid on first day of 2nd period @@ -307,11 +307,11 @@ Feature: Working Capital Loan Repayment | periodNumber | addedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays | | 1 | 2026-01-31 | 2026-01-31 | D00 | 1 | 30 | Then Working Capital loan amortization schedule has 4 periods, with the following data for periods: - | paymentNo | paymentDate | count | paymentsLeft | expectedPaymentAmount | forecastPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | netAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | - | 0 | 01 January 2026 | 3 | 0 | -9000.00 | | | 1 | -9000.00 | 9000.00 | | | | | 0.00 | - | 1 | 02 January 2026 | 2 | 0 | 5000.00 | 5000.00 | 270.00 | 1 | 270.00 | 4658.91 | 0.00 | 0.00 | 35.58 | 35.58 | 0.00 | - | 2 | 03 January 2026 | 1 | 1 | 5000.00 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | 0.00 | | 0.00 | 0.00 | - | 3 | 04 January 2026 | 0 | 2 | | 4730.00 | | 0.8682178936723646887 | 4106.67 | | | 0.00 | | | 0.00 | + | paymentNo | paymentDate | expectedPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | + | 0 | 01 January 2026 | -9000.00 | | 1 | -9000.00 | 9000.00 | | | | 0.00 | + | 1 | 02 January 2026 | 5000.00 | 270.00 | 1 | 270.00 | 4658.91 | 0.00 | 35.58 | 35.58 | 0.00 | + | 2 | 03 January 2026 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | | | 0.00 | + | 3 | 04 January 2026 | | | 0.8682178936723646887 | 4106.67 | | | | | 0.00 | @TestRailId:C76626 Scenario: Verify working capital loan repayment - UC10: full expectedAmount repaid in 1st period with multiple payments on same day @@ -355,12 +355,12 @@ Feature: Working Capital Loan Repayment And Delinquency Tag History for Working Capital loan has lines: | periodNumber | addedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays | Then Working Capital loan amortization schedule has 5 periods, with the following data for periods: - | paymentNo | paymentDate | count | paymentsLeft | expectedPaymentAmount | forecastPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | netAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | - | 0 | 01 January 2026 | 4 | 0 | -9000.00 | | | 1 | -9000.00 | 9000.00 | | | | | 0.00 | - | 1 | 02 January 2026 | 3 | 0 | 5000.00 | 5000.00 | 170.00 | 1 | 170.00 | 4658.91 | 0.00 | 0.00 | 22.40 | 22.40 | 0.00 | - | 2 | 03 January 2026 | 2 | 0 | 5000.00 | 5000.00 | 100.00 | 1 | 100.00 | 0.00 | 0.00 | 0.00 | 13.18 | 13.18 | 0.00 | - | 3 | 04 January 2026 | 1 | 1 | | 5000.00 | | 0.9317821063 | 4658.91 | | | 0.00 | | | 0.00 | - | 4 | 05 January 2026 | 0 | 2 | | 4730.00 | | 0.8682178937 | 4106.67 | | | 0.00 | | | 0.00 | + | paymentNo | paymentDate | expectedPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | + | 0 | 01 January 2026 | -9000.00 | | 1 | -9000.00 | 9000.00 | | | | 0.00 | + | 1 | 02 January 2026 | 5000.00 | 170.00 | 1 | 170.00 | 4658.91 | 0.00 | 22.40 | 22.40 | 0.00 | + | 2 | 03 January 2026 | 5000.00 | 100.00 | 1 | 100.00 | 0.00 | 0.00 | 13.18 | 13.18 | 0.00 | + | 3 | 04 January 2026 | | | 0.9317821063 | 4658.91 | | | | | 0.00 | + | 4 | 05 January 2026 | | | 0.8682178937 | 4106.67 | | | | | 0.00 | @TestRailId:C76627 Scenario: Verify working capital loan repayment - UC11: full expectedAmount repaid in 1st period with multiple payments on different days @@ -410,12 +410,12 @@ Feature: Working Capital Loan Repayment And Delinquency Tag History for Working Capital loan has lines: | periodNumber | addedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays | Then Working Capital loan amortization schedule has 5 periods, with the following data for periods: - | paymentNo | paymentDate | count | paymentsLeft | expectedPaymentAmount | forecastPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | netAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | - | 0 | 01 January 2026 | 4 | 0 | -9000.00 | | | 1 | -9000.00 | 9000.00 | | | | | 0.00 | - | 1 | 02 January 2026 | 3 | 0 | 5000.00 | 5000.00 | 170.00 | 1 | 170.00 | 4658.91 | 0.00 | 0.00 | 22.40 | 22.40 | 0.00 | - | 2 | 03 January 2026 | 2 | 0 | 5000.00 | 5000.00 | 100.00 | 1 | 100.00 | 0.00 | 0.00 | 0.00 | 13.18 | 13.18 | 0.00 | - | 3 | 04 January 2026 | 1 | 1 | | 5000.00 | | 0.9317821063 | 4658.91 | | | 0.00 | | | 0.00 | - | 4 | 05 January 2026 | 0 | 2 | | 4730.00 | | 0.8682178937 | 4106.67 | | | 0.00 | | | 0.00 | + | paymentNo | paymentDate | expectedPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | + | 0 | 01 January 2026 | -9000.00 | | 1 | -9000.00 | 9000.00 | | | | 0.00 | + | 1 | 02 January 2026 | 5000.00 | 170.00 | 1 | 170.00 | 4658.91 | 0.00 | 22.40 | 22.40 | 0.00 | + | 2 | 03 January 2026 | 5000.00 | 100.00 | 1 | 100.00 | 0.00 | 0.00 | 13.18 | 13.18 | 0.00 | + | 3 | 04 January 2026 | | | 0.9317821063 | 4658.91 | | | | | 0.00 | + | 4 | 05 January 2026 | | | 0.8682178937 | 4106.67 | | | | | 0.00 | @TestRailId:C76628 Scenario: Verify working capital loan repayment - UC12: partial expectedAmount repaid in 1st period @@ -459,11 +459,11 @@ Feature: Working Capital Loan Repayment | periodNumber | addedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays | | 1 | 2026-01-31 | | D00 | 1 | 30 | Then Working Capital loan amortization schedule has 4 periods, with the following data for periods: - | paymentNo | paymentDate | count | paymentsLeft | expectedPaymentAmount | forecastPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | netAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | - | 0 | 01 January 2026 | 3 | 0 | -9000.00 | | | 1 | -9000.00 | 9000.00 | | | | | 0.00 | - | 1 | 02 January 2026 | 2 | 0 | 5000.00 | 5000.00 | 170.00 | 1 | 170.00 | 4658.91 | 0.00 | 0.00 | 22.40 | 22.40 | 0.00 | - | 2 | 03 January 2026 | 1 | 1 | 5000.00 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | 0.00 | | 0.00 | 0.00 | - | 3 | 04 January 2026 | 0 | 2 | | 4830.00 | | 0.8682178937 | 4193.49 | | | 0.00 | | | 0.00 | + | paymentNo | paymentDate | expectedPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | + | 0 | 01 January 2026 | -9000.00 | | 1 | -9000.00 | 9000.00 | | | | 0.00 | + | 1 | 02 January 2026 | 5000.00 | 170.00 | 1 | 170.00 | 4658.91 | 0.00 | 22.40 | 22.40 | 0.00 | + | 2 | 03 January 2026 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | | | 0.00 | + | 3 | 04 January 2026 | | | 0.8682178937 | 4193.49 | | | | | 0.00 | @TestRailId:C76629 Scenario: Verify working capital loan repayment - UC13: partial expectedAmount repaid in 2nd period @@ -511,11 +511,11 @@ Feature: Working Capital Loan Repayment | periodNumber | addedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays | | 1 | 2026-01-31 | | D00 | 1 | 30 | Then Working Capital loan amortization schedule has 4 periods, with the following data for periods: - | paymentNo | paymentDate | count | paymentsLeft | expectedPaymentAmount | forecastPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | netAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | - | 0 | 01 January 2026 | 3 | 0 | -9000.00 | | | 1 | -9000.00 | 9000.00 | | | | | 0.00 | - | 1 | 02 January 2026 | 2 | 0 | 5000.00 | 5000.00 | 170.00 | 1 | 170.00 | 4658.91 | 0.00 | 0.00 | 22.40 | 22.40 | 0.00 | - | 2 | 03 January 2026 | 1 | 1 | 5000.00 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | 0.00 | | 0.00 | 0.00 | - | 3 | 04 January 2026 | 0 | 2 | | 4830.00 | | 0.8682178937 | 4193.49 | | | 0.00 | | | 0.00 | + | paymentNo | paymentDate | expectedPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | + | 0 | 01 January 2026 | -9000.00 | | 1 | -9000.00 | 9000.00 | | | | 0.00 | + | 1 | 02 January 2026 | 5000.00 | 170.00 | 1 | 170.00 | 4658.91 | 0.00 | 22.40 | 22.40 | 0.00 | + | 2 | 03 January 2026 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | | | 0.00 | + | 3 | 04 January 2026 | | | 0.8682178937 | 4193.49 | | | | | 0.00 | @TestRailId:C76630 Scenario: Verify working capital loan repayment - UC14: expectedAmount overpaid in 1st period @@ -560,11 +560,11 @@ Feature: Working Capital Loan Repayment And Delinquency Tag History for Working Capital loan has lines: | periodNumber | addedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays | Then Working Capital loan amortization schedule has 4 periods, with the following data for periods: - | paymentNo | paymentDate | count | paymentsLeft | expectedPaymentAmount | forecastPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | netAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | - | 0 | 01 January 2026 | 3 | 0 | -9000.00 | | | 1 | -9000.00 | 9000.00 | | | | | 0.00 | - | 1 | 02 January 2026 | 2 | 0 | 5000.00 | 5000.00 | 370.00 | 1 | 370.00 | 4658.91 | 0.00 | 0.00 | 48.76 | 48.76 | 0.00 | - | 2 | 03 January 2026 | 1 | 1 | 5000.00 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | 0.00 | | 0.00 | 0.00 | - | 3 | 04 January 2026 | 0 | 2 | | 4630.00 | | 0.8682178937 | 4019.85 | | | 0.00 | | | 0.00 | + | paymentNo | paymentDate | expectedPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | + | 0 | 01 January 2026 | -9000.00 | | 1 | -9000.00 | 9000.00 | | | | 0.00 | + | 1 | 02 January 2026 | 5000.00 | 370.00 | 1 | 370.00 | 4658.91 | 0.00 | 48.76 | 48.76 | 0.00 | + | 2 | 03 January 2026 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | | | 0.00 | + | 3 | 04 January 2026 | | | 0.8682178937 | 4019.85 | | | | | 0.00 | @TestRailId:C76631 Scenario: Verify working capital loan repayment - UC15: expectedAmount overpaid in 2nd period @@ -612,11 +612,11 @@ Feature: Working Capital Loan Repayment | periodNumber | addedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays | | 1 | 2026-01-31 | 2026-02-10 | D00 | 1 | 30 | Then Working Capital loan amortization schedule has 4 periods, with the following data for periods: - | paymentNo | paymentDate | count | paymentsLeft | expectedPaymentAmount | forecastPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | netAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | - | 0 | 01 January 2026 | 3 | 0 | -9000.00 | | | 1 | -9000.00 | 9000.00 | | | | | 0.00 | - | 1 | 02 January 2026 | 2 | 0 | 5000.00 | 5000.00 | 370.00 | 1 | 370.00 | 4658.91 | 0.00 | 0.00 | 48.76 | 48.76 | 0.00 | - | 2 | 03 January 2026 | 1 | 1 | 5000.00 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | 0.00 | | 0.00 | 0.00 | - | 3 | 04 January 2026 | 0 | 2 | | 4630.00 | | 0.8682178937 | 4019.85 | | | 0.00 | | | 0.00 | + | paymentNo | paymentDate | expectedPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | + | 0 | 01 January 2026 | -9000.00 | | 1 | -9000.00 | 9000.00 | | | | 0.00 | + | 1 | 02 January 2026 | 5000.00 | 370.00 | 1 | 370.00 | 4658.91 | 0.00 | 48.76 | 48.76 | 0.00 | + | 2 | 03 January 2026 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | | | 0.00 | + | 3 | 04 January 2026 | | | 0.8682178937 | 4019.85 | | | | | 0.00 | @TestRailId:C76632 Scenario: Verify working capital loan repayment - UC16: expectedAmount overpaid in late period @@ -688,11 +688,11 @@ Feature: Working Capital Loan Repayment | 1 | 2026-03-02 | 2026-05-10 | D30 | 31 | 60 | | 1 | 2026-01-31 | 2026-05-10 | D00 | 1 | 30 | Then Working Capital loan amortization schedule has 4 periods, with the following data for periods: - | paymentNo | paymentDate | count | paymentsLeft | expectedPaymentAmount | forecastPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | netAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | - | 0 | 01 January 2026 | 3 | 0 | -9000.00 | | | 1 | -9000.00 | 9000.00 | | | | | 0.00 | - | 1 | 02 January 2026 | 2 | 0 | 5000.00 | 5000.00 | 1500.00 | 1 | 1500.00 | 4658.91 | 0.00 | 0.00 | 197.67 | 197.67 | 0.00 | - | 2 | 03 January 2026 | 1 | 1 | 5000.00 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | 0.00 | | 0.00 | 0.00 | - | 3 | 04 January 2026 | 0 | 2 | | 3500.00 | | 0.8682178937 | 3038.76 | | | 0.00 | | | 0.00 | + | paymentNo | paymentDate | expectedPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | + | 0 | 01 January 2026 | -9000.00 | | 1 | -9000.00 | 9000.00 | | | | 0.00 | + | 1 | 02 January 2026 | 5000.00 | 1500.00 | 1 | 1500.00 | 4658.91 | 0.00 | 197.67 | 197.67 | 0.00 | + | 2 | 03 January 2026 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | | | 0.00 | + | 3 | 04 January 2026 | | | 0.8682178937 | 3038.76 | | | | | 0.00 | @TestRailId:C76633 Scenario: Verify working capital loan repayment - UC17: simple repayment with payment details @@ -721,11 +721,11 @@ Feature: Working Capital Loan Repayment | periodNumber | fromDate | toDate | expectedAmount | paidAmount | outstandingAmount | minPaymentCriteriaMet | delinquentAmount | delinquentDays | | 1 | 2026-01-01 | 2026-01-30 | 270.0 | 270.0 | 0.0 | true | 0.0 | 0 | Then Working Capital loan amortization schedule has 4 periods, with the following data for periods: - | paymentNo | paymentDate | count | paymentsLeft | expectedPaymentAmount | forecastPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | netAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | - | 0 | 01 January 2026 | 3 | 0 | -9000.00 | | | 1 | -9000.00 | 9000.00 | | | | | 0.00 | - | 1 | 02 January 2026 | 2 | 0 | 5000.00 | 5000.00 | 270.00 | 1 | 270.00 | 4658.91 | 0.00 | 0.00 | 35.58 | 35.58 | 0.00 | - | 2 | 03 January 2026 | 1 | 1 | 5000.00 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | 0.00 | | 0.00 | 0.00 | - | 3 | 04 January 2026 | 0 | 2 | | 4730.00 | | 0.8682178936723646887 | 4106.67 | | | 0.00 | | | 0.00 | + | paymentNo | paymentDate | expectedPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | + | 0 | 01 January 2026 | -9000.00 | | 1 | -9000.00 | 9000.00 | | | | 0.00 | + | 1 | 02 January 2026 | 5000.00 | 270.00 | 1 | 270.00 | 4658.91 | 0.00 | 35.58 | 35.58 | 0.00 | + | 2 | 03 January 2026 | 5000.00 | | 0.9317821063276353179 | 4658.91 | 0.00 | 0.00 | | | 0.00 | + | 3 | 04 January 2026 | | | 0.8682178936723646887 | 4106.67 | | | | | 0.00 | @TestRailId:C76634 Scenario: Verify amortization schedule after repayment transaction - UC18 @@ -737,208 +737,208 @@ Feature: Working Capital Loan Repayment And Admin successfully approves the working capital loan on "01 January 2019" with "9000" amount and "1000" discount amount and expected disbursement date on "01 January 2019" And Admin successfully disburse the Working Capital loan on "01 January 2019" with "9000" EUR transaction amount Then Working Capital loan amortization schedule has 201 periods, with the following data for periods: - | paymentNo | paymentDate | count | paymentsLeft | expectedPaymentAmount | forecastPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | netAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | - | 0 | 01 January 2019 | 200 | 0 | -9000.00 | | | 1 | -9000.00 | 9000.00 | | | | | 1000.00 | - | 1 | 02 January 2019 | 199 | 1 | 50.00 | 50.00 | | 0.9989333245 | 49.95 | 8959.61 | 9.61 | 0.00 | | -9.61 | 1000.00 | - | 2 | 03 January 2019 | 198 | 2 | 50.00 | 50.00 | | 0.9978677868 | 49.89 | 8919.18 | 9.57 | 0.00 | | -9.57 | 1000.00 | - | 3 | 04 January 2019 | 197 | 3 | 50.00 | 50.00 | | 0.9968033857 | 49.84 | 8878.70 | 9.52 | 0.00 | | -9.52 | 1000.00 | - | 4 | 05 January 2019 | 196 | 4 | 50.00 | 50.00 | | 0.99574012 | 49.79 | 8838.18 | 9.48 | 0.00 | | -9.48 | 1000.00 | - | 5 | 06 January 2019 | 195 | 5 | 50.00 | 50.00 | | 0.9946779885 | 49.73 | 8797.62 | 9.44 | 0.00 | | -9.44 | 1000.00 | - | 6 | 07 January 2019 | 194 | 6 | 50.00 | 50.00 | | 0.9936169898 | 49.68 | 8757.01 | 9.39 | 0.00 | | -9.39 | 1000.00 | - | 7 | 08 January 2019 | 193 | 7 | 50.00 | 50.00 | | 0.992557123 | 49.63 | 8716.36 | 9.35 | 0.00 | | -9.35 | 1000.00 | - | 8 | 09 January 2019 | 192 | 8 | 50.00 | 50.00 | | 0.9914983866 | 49.57 | 8675.67 | 9.31 | 0.00 | | -9.31 | 1000.00 | - | 9 | 10 January 2019 | 191 | 9 | 50.00 | 50.00 | | 0.9904407796 | 49.52 | 8634.94 | 9.26 | 0.00 | | -9.26 | 1000.00 | - | 10 | 11 January 2019 | 190 | 10 | 50.00 | 50.00 | | 0.9893843007 | 49.47 | 8594.16 | 9.22 | 0.00 | | -9.22 | 1000.00 | - | 11 | 12 January 2019 | 189 | 11 | 50.00 | 50.00 | | 0.9883289487 | 49.42 | 8553.33 | 9.18 | 0.00 | | -9.18 | 1000.00 | - | 12 | 13 January 2019 | 188 | 12 | 50.00 | 50.00 | | 0.9872747225 | 49.36 | 8512.47 | 9.13 | 0.00 | | -9.13 | 1000.00 | - | 13 | 14 January 2019 | 187 | 13 | 50.00 | 50.00 | | 0.9862216208 | 49.31 | 8471.56 | 9.09 | 0.00 | | -9.09 | 1000.00 | - | 14 | 15 January 2019 | 186 | 14 | 50.00 | 50.00 | | 0.9851696423 | 49.26 | 8430.60 | 9.05 | 0.00 | | -9.05 | 1000.00 | - | 15 | 16 January 2019 | 185 | 15 | 50.00 | 50.00 | | 0.984118786 | 49.21 | 8389.61 | 9.00 | 0.00 | | -9.00 | 1000.00 | - | 16 | 17 January 2019 | 184 | 16 | 50.00 | 50.00 | | 0.9830690507 | 49.15 | 8348.56 | 8.96 | 0.00 | | -8.96 | 1000.00 | - | 17 | 18 January 2019 | 183 | 17 | 50.00 | 50.00 | | 0.982020435 | 49.10 | 8307.48 | 8.91 | 0.00 | | -8.91 | 1000.00 | - | 18 | 19 January 2019 | 182 | 18 | 50.00 | 50.00 | | 0.9809729379 | 49.05 | 8266.35 | 8.87 | 0.00 | | -8.87 | 1000.00 | - | 19 | 20 January 2019 | 181 | 19 | 50.00 | 50.00 | | 0.9799265581 | 49.00 | 8225.18 | 8.83 | 0.00 | | -8.83 | 1000.00 | - | 20 | 21 January 2019 | 180 | 20 | 50.00 | 50.00 | | 0.9788812945 | 48.94 | 8183.96 | 8.78 | 0.00 | | -8.78 | 1000.00 | - | 21 | 22 January 2019 | 179 | 21 | 50.00 | 50.00 | | 0.9778371458 | 48.89 | 8142.70 | 8.74 | 0.00 | | -8.74 | 1000.00 | - | 22 | 23 January 2019 | 178 | 22 | 50.00 | 50.00 | | 0.9767941109 | 48.84 | 8101.39 | 8.69 | 0.00 | | -8.69 | 1000.00 | - | 23 | 24 January 2019 | 177 | 23 | 50.00 | 50.00 | | 0.9757521886 | 48.79 | 8060.04 | 8.65 | 0.00 | | -8.65 | 1000.00 | - | 24 | 25 January 2019 | 176 | 24 | 50.00 | 50.00 | | 0.9747113777 | 48.74 | 8018.65 | 8.61 | 0.00 | | -8.61 | 1000.00 | - | 25 | 26 January 2019 | 175 | 25 | 50.00 | 50.00 | | 0.9736716769498249835 | 48.68 | 7977.21 | 8.56 | 0.00 | | -8.56 | 1000.00 | - | 26 | 27 January 2019 | 174 | 26 | 50.00 | 50.00 | | 0.9726330853 | 48.63 | 7935.73 | 8.52 | 0.00 | | -8.52 | 1000.00 | - | 27 | 28 January 2019 | 173 | 27 | 50.00 | 50.00 | | 0.9715956014 | 48.58 | 7894.21 | 8.47 | 0.00 | | -8.47 | 1000.00 | - | 28 | 29 January 2019 | 172 | 28 | 50.00 | 50.00 | | 0.9705592242 | 48.53 | 7852.63 | 8.43 | 0.00 | | -8.43 | 1000.00 | - | 29 | 30 January 2019 | 171 | 29 | 50.00 | 50.00 | | 0.9695239525 | 48.48 | 7811.02 | 8.39 | 0.00 | | -8.39 | 1000.00 | - | 30 | 31 January 2019 | 170 | 30 | 50.00 | 50.00 | | 0.968489785 | 48.42 | 7769.36 | 8.34 | 0.00 | | -8.34 | 1000.00 | - | 31 | 01 February 2019 | 169 | 31 | 50.00 | 50.00 | | 0.9674567207 | 48.37 | 7727.66 | 8.30 | 0.00 | | -8.30 | 1000.00 | - | 32 | 02 February 2019 | 168 | 32 | 50.00 | 50.00 | | 0.9664247584 | 48.32 | 7685.91 | 8.25 | 0.00 | | -8.25 | 1000.00 | - | 33 | 03 February 2019 | 167 | 33 | 50.00 | 50.00 | | 0.9653938968 | 48.27 | 7644.12 | 8.21 | 0.00 | | -8.21 | 1000.00 | - | 34 | 04 February 2019 | 166 | 34 | 50.00 | 50.00 | | 0.9643641348 | 48.22 | 7602.28 | 8.16 | 0.00 | | -8.16 | 1000.00 | - | 35 | 05 February 2019 | 165 | 35 | 50.00 | 50.00 | | 0.9633354712 | 48.17 | 7560.40 | 8.12 | 0.00 | | -8.12 | 1000.00 | - | 36 | 06 February 2019 | 164 | 36 | 50.00 | 50.00 | | 0.9623079049 | 48.12 | 7518.47 | 8.07 | 0.00 | | -8.07 | 1000.00 | - | 37 | 07 February 2019 | 163 | 37 | 50.00 | 50.00 | | 0.9612814347 | 48.06 | 7476.50 | 8.03 | 0.00 | | -8.03 | 1000.00 | - | 38 | 08 February 2019 | 162 | 38 | 50.00 | 50.00 | | 0.9602560593 | 48.01 | 7434.48 | 7.98 | 0.00 | | -7.98 | 1000.00 | - | 39 | 09 February 2019 | 161 | 39 | 50.00 | 50.00 | | 0.9592317777 | 47.96 | 7392.42 | 7.94 | 0.00 | | -7.94 | 1000.00 | - | 40 | 10 February 2019 | 160 | 40 | 50.00 | 50.00 | | 0.9582085887 | 47.91 | 7350.31 | 7.89 | 0.00 | | -7.89 | 1000.00 | - | 41 | 11 February 2019 | 159 | 41 | 50.00 | 50.00 | | 0.9571864911 | 47.86 | 7308.16 | 7.85 | 0.00 | | -7.85 | 1000.00 | - | 42 | 12 February 2019 | 158 | 42 | 50.00 | 50.00 | | 0.9561654838 | 47.81 | 7265.97 | 7.80 | 0.00 | | -7.80 | 1000.00 | - | 43 | 13 February 2019 | 157 | 43 | 50.00 | 50.00 | | 0.9551455655 | 47.76 | 7223.72 | 7.76 | 0.00 | | -7.76 | 1000.00 | - | 44 | 14 February 2019 | 156 | 44 | 50.00 | 50.00 | | 0.9541267351 | 47.71 | 7181.44 | 7.71 | 0.00 | | -7.71 | 1000.00 | - | 45 | 15 February 2019 | 155 | 45 | 50.00 | 50.00 | | 0.9531089916 | 47.66 | 7139.11 | 7.67 | 0.00 | | -7.67 | 1000.00 | - | 46 | 16 February 2019 | 154 | 46 | 50.00 | 50.00 | | 0.9520923336 | 47.60 | 7096.73 | 7.62 | 0.00 | | -7.62 | 1000.00 | - | 47 | 17 February 2019 | 153 | 47 | 50.00 | 50.00 | | 0.95107676 | 47.55 | 7054.31 | 7.58 | 0.00 | | -7.58 | 1000.00 | - | 48 | 18 February 2019 | 152 | 48 | 50.00 | 50.00 | | 0.9500622698 | 47.50 | 7011.84 | 7.53 | 0.00 | | -7.53 | 1000.00 | - | 49 | 19 February 2019 | 151 | 49 | 50.00 | 50.00 | | 0.9490488616 | 47.45 | 6969.33 | 7.49 | 0.00 | | -7.49 | 1000.00 | - | 50 | 20 February 2019 | 150 | 50 | 50.00 | 50.00 | | 0.9480365345 | 47.40 | 6926.77 | 7.44 | 0.00 | | -7.44 | 1000.00 | - | 51 | 21 February 2019 | 149 | 51 | 50.00 | 50.00 | | 0.9470252872 | 47.35 | 6884.17 | 7.40 | 0.00 | | -7.40 | 1000.00 | - | 52 | 22 February 2019 | 148 | 52 | 50.00 | 50.00 | | 0.9460151185 | 47.30 | 6841.52 | 7.35 | 0.00 | | -7.35 | 1000.00 | - | 53 | 23 February 2019 | 147 | 53 | 50.00 | 50.00 | | 0.9450060274 | 47.25 | 6798.82 | 7.31 | 0.00 | | -7.31 | 1000.00 | - | 54 | 24 February 2019 | 146 | 54 | 50.00 | 50.00 | | 0.9439980126 | 47.20 | 6756.08 | 7.26 | 0.00 | | -7.26 | 1000.00 | - | 55 | 25 February 2019 | 145 | 55 | 50.00 | 50.00 | | 0.9429910731 | 47.15 | 6713.30 | 7.21 | 0.00 | | -7.21 | 1000.00 | - | 56 | 26 February 2019 | 144 | 56 | 50.00 | 50.00 | | 0.9419852077 | 47.10 | 6670.47 | 7.17 | 0.00 | | -7.17 | 1000.00 | - | 57 | 27 February 2019 | 143 | 57 | 50.00 | 50.00 | | 0.9409804151 | 47.05 | 6627.59 | 7.12 | 0.00 | | -7.12 | 1000.00 | - | 58 | 28 February 2019 | 142 | 58 | 50.00 | 50.00 | | 0.9399766944 | 47.00 | 6584.67 | 7.08 | 0.00 | | -7.08 | 1000.00 | - | 59 | 01 March 2019 | 141 | 59 | 50.00 | 50.00 | | 0.9389740443 | 46.95 | 6541.70 | 7.03 | 0.00 | | -7.03 | 1000.00 | - | 60 | 02 March 2019 | 140 | 60 | 50.00 | 50.00 | | 0.9379724637 | 46.90 | 6498.68 | 6.99 | 0.00 | | -6.99 | 1000.00 | - | 61 | 03 March 2019 | 139 | 61 | 50.00 | 50.00 | | 0.9369719515 | 46.85 | 6455.62 | 6.94 | 0.00 | | -6.94 | 1000.00 | - | 62 | 04 March 2019 | 138 | 62 | 50.00 | 50.00 | | 0.9359725065 | 46.80 | 6412.51 | 6.89 | 0.00 | | -6.89 | 1000.00 | - | 63 | 05 March 2019 | 137 | 63 | 50.00 | 50.00 | | 0.9349741276 | 46.75 | 6369.36 | 6.85 | 0.00 | | -6.85 | 1000.00 | - | 64 | 06 March 2019 | 136 | 64 | 50.00 | 50.00 | | 0.9339768136 | 46.70 | 6326.16 | 6.80 | 0.00 | | -6.80 | 1000.00 | - | 65 | 07 March 2019 | 135 | 65 | 50.00 | 50.00 | | 0.9329805635 | 46.65 | 6282.92 | 6.76 | 0.00 | | -6.76 | 1000.00 | - | 66 | 08 March 2019 | 134 | 66 | 50.00 | 50.00 | | 0.931985376 | 46.60 | 6239.63 | 6.71 | 0.00 | | -6.71 | 1000.00 | - | 67 | 09 March 2019 | 133 | 67 | 50.00 | 50.00 | | 0.93099125 | 46.55 | 6196.29 | 6.66 | 0.00 | | -6.66 | 1000.00 | - | 68 | 10 March 2019 | 132 | 68 | 50.00 | 50.00 | | 0.9299981845 | 46.50 | 6152.91 | 6.62 | 0.00 | | -6.62 | 1000.00 | - | 69 | 11 March 2019 | 131 | 69 | 50.00 | 50.00 | | 0.9290061782 | 46.45 | 6109.48 | 6.57 | 0.00 | | -6.57 | 1000.00 | - | 70 | 12 March 2019 | 130 | 70 | 50.00 | 50.00 | | 0.9280152301 | 46.40 | 6066.00 | 6.52 | 0.00 | | -6.52 | 1000.00 | - | 71 | 13 March 2019 | 129 | 71 | 50.00 | 50.00 | | 0.927025339 | 46.35 | 6022.48 | 6.48 | 0.00 | | -6.48 | 1000.00 | - | 72 | 14 March 2019 | 128 | 72 | 50.00 | 50.00 | | 0.9260365038 | 46.30 | 5978.91 | 6.43 | 0.00 | | -6.43 | 1000.00 | - | 73 | 15 March 2019 | 127 | 73 | 50.00 | 50.00 | | 0.9250487234 | 46.25 | 5935.29 | 6.38 | 0.00 | | -6.38 | 1000.00 | - | 74 | 16 March 2019 | 126 | 74 | 50.00 | 50.00 | | 0.9240619966 | 46.20 | 5891.63 | 6.34 | 0.00 | | -6.34 | 1000.00 | - | 75 | 17 March 2019 | 125 | 75 | 50.00 | 50.00 | | 0.9230763224 | 46.15 | 5847.92 | 6.29 | 0.00 | | -6.29 | 1000.00 | - | 76 | 18 March 2019 | 124 | 76 | 50.00 | 50.00 | | 0.9220916995 | 46.10 | 5804.17 | 6.24 | 0.00 | | -6.24 | 1000.00 | - | 77 | 19 March 2019 | 123 | 77 | 50.00 | 50.00 | | 0.9211081269 | 46.06 | 5760.36 | 6.20 | 0.00 | | -6.20 | 1000.00 | - | 78 | 20 March 2019 | 122 | 78 | 50.00 | 50.00 | | 0.9201256034 | 46.01 | 5716.52 | 6.15 | 0.00 | | -6.15 | 1000.00 | - | 79 | 21 March 2019 | 121 | 79 | 50.00 | 50.00 | | 0.919144128 | 45.96 | 5672.62 | 6.10 | 0.00 | | -6.10 | 1000.00 | - | 80 | 22 March 2019 | 120 | 80 | 50.00 | 50.00 | | 0.9181636995 | 45.91 | 5628.68 | 6.06 | 0.00 | | -6.06 | 1000.00 | - | 81 | 23 March 2019 | 119 | 81 | 50.00 | 50.00 | | 0.9171843168 | 45.86 | 5584.69 | 6.01 | 0.00 | | -6.01 | 1000.00 | - | 82 | 24 March 2019 | 118 | 82 | 50.00 | 50.00 | | 0.9162059788 | 45.81 | 5540.65 | 5.96 | 0.00 | | -5.96 | 1000.00 | - | 83 | 25 March 2019 | 117 | 83 | 50.00 | 50.00 | | 0.9152286843 | 45.76 | 5496.57 | 5.92 | 0.00 | | -5.92 | 1000.00 | - | 84 | 26 March 2019 | 116 | 84 | 50.00 | 50.00 | | 0.9142524323 | 45.71 | 5452.44 | 5.87 | 0.00 | | -5.87 | 1000.00 | - | 85 | 27 March 2019 | 115 | 85 | 50.00 | 50.00 | | 0.9132772217 | 45.66 | 5408.26 | 5.82 | 0.00 | | -5.82 | 1000.00 | - | 86 | 28 March 2019 | 114 | 86 | 50.00 | 50.00 | | 0.9123030513 | 45.62 | 5364.03 | 5.78 | 0.00 | | -5.78 | 1000.00 | - | 87 | 29 March 2019 | 113 | 87 | 50.00 | 50.00 | | 0.91132992 | 45.57 | 5319.76 | 5.73 | 0.00 | | -5.73 | 1000.00 | - | 88 | 30 March 2019 | 112 | 88 | 50.00 | 50.00 | | 0.9103578267 | 45.52 | 5275.44 | 5.68 | 0.00 | | -5.68 | 1000.00 | - | 89 | 31 March 2019 | 111 | 89 | 50.00 | 50.00 | | 0.9093867703 | 45.47 | 5231.08 | 5.63 | 0.00 | | -5.63 | 1000.00 | - | 90 | 01 April 2019 | 110 | 90 | 50.00 | 50.00 | | 0.9084167498 | 45.42 | 5186.66 | 5.59 | 0.00 | | -5.59 | 1000.00 | - | 91 | 02 April 2019 | 109 | 91 | 50.00 | 50.00 | | 0.9074477639 | 45.37 | 5142.20 | 5.54 | 0.00 | | -5.54 | 1000.00 | - | 92 | 03 April 2019 | 108 | 92 | 50.00 | 50.00 | | 0.9064798116 | 45.32 | 5097.69 | 5.49 | 0.00 | | -5.49 | 1000.00 | - | 93 | 04 April 2019 | 107 | 93 | 50.00 | 50.00 | | 0.9055128918 | 45.28 | 5053.13 | 5.44 | 0.00 | | -5.44 | 1000.00 | - | 94 | 05 April 2019 | 106 | 94 | 50.00 | 50.00 | | 0.9045470035 | 45.23 | 5008.53 | 5.40 | 0.00 | | -5.40 | 1000.00 | - | 95 | 06 April 2019 | 105 | 95 | 50.00 | 50.00 | | 0.9035821453 | 45.18 | 4963.88 | 5.35 | 0.00 | | -5.35 | 1000.00 | - | 96 | 07 April 2019 | 104 | 96 | 50.00 | 50.00 | | 0.9026183164 | 45.13 | 4919.18 | 5.30 | 0.00 | | -5.30 | 1000.00 | - | 97 | 08 April 2019 | 103 | 97 | 50.00 | 50.00 | | 0.9016555156 | 45.08 | 4874.43 | 5.25 | 0.00 | | -5.25 | 1000.00 | - | 98 | 09 April 2019 | 102 | 98 | 50.00 | 50.00 | | 0.9006937418 | 45.03 | 4829.64 | 5.20 | 0.00 | | -5.20 | 1000.00 | - | 99 | 10 April 2019 | 101 | 99 | 50.00 | 50.00 | | 0.8997329939 | 44.99 | 4784.79 | 5.16 | 0.00 | | -5.16 | 1000.00 | - | 100 | 11 April 2019 | 100 | 100 | 50.00 | 50.00 | | 0.8987732707 | 44.94 | 4739.90 | 5.11 | 0.00 | | -5.11 | 1000.00 | - | 101 | 12 April 2019 | 99 | 101 | 50.00 | 50.00 | | 0.8978145713 | 44.89 | 4694.96 | 5.06 | 0.00 | | -5.06 | 1000.00 | - | 102 | 13 April 2019 | 98 | 102 | 50.00 | 50.00 | | 0.8968568945 | 44.84 | 4649.98 | 5.01 | 0.00 | | -5.01 | 1000.00 | - | 103 | 14 April 2019 | 97 | 103 | 50.00 | 50.00 | | 0.8959002393 | 44.80 | 4604.94 | 4.97 | 0.00 | | -4.97 | 1000.00 | - | 104 | 15 April 2019 | 96 | 104 | 50.00 | 50.00 | | 0.8949446045 | 44.75 | 4559.86 | 4.92 | 0.00 | | -4.92 | 1000.00 | - | 105 | 16 April 2019 | 95 | 105 | 50.00 | 50.00 | | 0.893989989 | 44.70 | 4514.73 | 4.87 | 0.00 | | -4.87 | 1000.00 | - | 106 | 17 April 2019 | 94 | 106 | 50.00 | 50.00 | | 0.8930363918 | 44.65 | 4469.55 | 4.82 | 0.00 | | -4.82 | 1000.00 | - | 107 | 18 April 2019 | 93 | 107 | 50.00 | 50.00 | | 0.8920838118 | 44.60 | 4424.32 | 4.77 | 0.00 | | -4.77 | 1000.00 | - | 108 | 19 April 2019 | 92 | 108 | 50.00 | 50.00 | | 0.8911322479 | 44.56 | 4379.05 | 4.72 | 0.00 | | -4.72 | 1000.00 | - | 109 | 20 April 2019 | 91 | 109 | 50.00 | 50.00 | | 0.890181699 | 44.51 | 4333.72 | 4.68 | 0.00 | | -4.68 | 1000.00 | - | 110 | 21 April 2019 | 90 | 110 | 50.00 | 50.00 | | 0.889232164 | 44.46 | 4288.35 | 4.63 | 0.00 | | -4.63 | 1000.00 | - | 111 | 22 April 2019 | 89 | 111 | 50.00 | 50.00 | | 0.8882836418 | 44.41 | 4242.93 | 4.58 | 0.00 | | -4.58 | 1000.00 | - | 112 | 23 April 2019 | 88 | 112 | 50.00 | 50.00 | | 0.8873361314 | 44.37 | 4197.46 | 4.53 | 0.00 | | -4.53 | 1000.00 | - | 113 | 24 April 2019 | 87 | 113 | 50.00 | 50.00 | | 0.8863896318 | 44.32 | 4151.94 | 4.48 | 0.00 | | -4.48 | 1000.00 | - | 114 | 25 April 2019 | 86 | 114 | 50.00 | 50.00 | | 0.8854441417 | 44.27 | 4106.38 | 4.43 | 0.00 | | -4.43 | 1000.00 | - | 115 | 26 April 2019 | 85 | 115 | 50.00 | 50.00 | | 0.8844996601 | 44.22 | 4060.76 | 4.38 | 0.00 | | -4.38 | 1000.00 | - | 116 | 27 April 2019 | 84 | 116 | 50.00 | 50.00 | | 0.883556186 | 44.18 | 4015.10 | 4.34 | 0.00 | | -4.34 | 1000.00 | - | 117 | 28 April 2019 | 83 | 117 | 50.00 | 50.00 | | 0.8826137183 | 44.13 | 3969.38 | 4.29 | 0.00 | | -4.29 | 1000.00 | - | 118 | 29 April 2019 | 82 | 118 | 50.00 | 50.00 | | 0.8816722559 | 44.08 | 3923.62 | 4.24 | 0.00 | | -4.24 | 1000.00 | - | 119 | 30 April 2019 | 81 | 119 | 50.00 | 50.00 | | 0.8807317977 | 44.04 | 3877.81 | 4.19 | 0.00 | | -4.19 | 1000.00 | - | 120 | 01 May 2019 | 80 | 120 | 50.00 | 50.00 | | 0.8797923427 | 43.99 | 3831.95 | 4.14 | 0.00 | | -4.14 | 1000.00 | - | 121 | 02 May 2019 | 79 | 121 | 50.00 | 50.00 | | 0.8788538898 | 43.94 | 3786.04 | 4.09 | 0.00 | | -4.09 | 1000.00 | - | 122 | 03 May 2019 | 78 | 122 | 50.00 | 50.00 | | 0.8779164379 | 43.90 | 3740.09 | 4.04 | 0.00 | | -4.04 | 1000.00 | - | 123 | 04 May 2019 | 77 | 123 | 50.00 | 50.00 | | 0.876979986 | 43.85 | 3694.08 | 3.99 | 0.00 | | -3.99 | 1000.00 | - | 124 | 05 May 2019 | 76 | 124 | 50.00 | 50.00 | | 0.8760445329 | 43.80 | 3648.03 | 3.94 | 0.00 | | -3.94 | 1000.00 | - | 125 | 06 May 2019 | 75 | 125 | 50.00 | 50.00 | | 0.8751100777 | 43.76 | 3601.92 | 3.90 | 0.00 | | -3.90 | 1000.00 | - | 126 | 07 May 2019 | 74 | 126 | 50.00 | 50.00 | | 0.8741766193 | 43.71 | 3555.77 | 3.85 | 0.00 | | -3.85 | 1000.00 | - | 127 | 08 May 2019 | 73 | 127 | 50.00 | 50.00 | | 0.8732441565 | 43.66 | 3509.56 | 3.80 | 0.00 | | -3.80 | 1000.00 | - | 128 | 09 May 2019 | 72 | 128 | 50.00 | 50.00 | | 0.8723126884 | 43.62 | 3463.31 | 3.75 | 0.00 | | -3.75 | 1000.00 | - | 129 | 10 May 2019 | 71 | 129 | 50.00 | 50.00 | | 0.8713822138 | 43.57 | 3417.01 | 3.70 | 0.00 | | -3.70 | 1000.00 | - | 130 | 11 May 2019 | 70 | 130 | 50.00 | 50.00 | | 0.8704527318 | 43.52 | 3370.66 | 3.65 | 0.00 | | -3.65 | 1000.00 | - | 131 | 12 May 2019 | 69 | 131 | 50.00 | 50.00 | | 0.8695242412 | 43.48 | 3324.26 | 3.60 | 0.00 | | -3.60 | 1000.00 | - | 132 | 13 May 2019 | 68 | 132 | 50.00 | 50.00 | | 0.868596741 | 43.43 | 3277.81 | 3.55 | 0.00 | | -3.55 | 1000.00 | - | 133 | 14 May 2019 | 67 | 133 | 50.00 | 50.00 | | 0.8676702302 | 43.38 | 3231.31 | 3.50 | 0.00 | | -3.50 | 1000.00 | - | 134 | 15 May 2019 | 66 | 134 | 50.00 | 50.00 | | 0.8667447076 | 43.34 | 3184.76 | 3.45 | 0.00 | | -3.45 | 1000.00 | - | 135 | 16 May 2019 | 65 | 135 | 50.00 | 50.00 | | 0.8658201723 | 43.29 | 3138.16 | 3.40 | 0.00 | | -3.40 | 1000.00 | - | 136 | 17 May 2019 | 64 | 136 | 50.00 | 50.00 | | 0.8648966231 | 43.24 | 3091.51 | 3.35 | 0.00 | | -3.35 | 1000.00 | - | 137 | 18 May 2019 | 63 | 137 | 50.00 | 50.00 | | 0.8639740591 | 43.20 | 3044.81 | 3.30 | 0.00 | | -3.30 | 1000.00 | - | 138 | 19 May 2019 | 62 | 138 | 50.00 | 50.00 | | 0.8630524792 | 43.15 | 2998.06 | 3.25 | 0.00 | | -3.25 | 1000.00 | - | 139 | 20 May 2019 | 61 | 139 | 50.00 | 50.00 | | 0.8621318823 | 43.11 | 2951.26 | 3.20 | 0.00 | | -3.20 | 1000.00 | - | 140 | 21 May 2019 | 60 | 140 | 50.00 | 50.00 | | 0.8612122673 | 43.06 | 2904.42 | 3.15 | 0.00 | | -3.15 | 1000.00 | - | 141 | 22 May 2019 | 59 | 141 | 50.00 | 50.00 | | 0.8602936333 | 43.01 | 2857.52 | 3.10 | 0.00 | | -3.10 | 1000.00 | - | 142 | 23 May 2019 | 58 | 142 | 50.00 | 50.00 | | 0.8593759792 | 42.97 | 2810.57 | 3.05 | 0.00 | | -3.05 | 1000.00 | - | 143 | 24 May 2019 | 57 | 143 | 50.00 | 50.00 | | 0.8584593039 | 42.92 | 2763.57 | 3.00 | 0.00 | | -3.00 | 1000.00 | - | 144 | 25 May 2019 | 56 | 144 | 50.00 | 50.00 | | 0.8575436064 | 42.88 | 2716.52 | 2.95 | 0.00 | | -2.95 | 1000.00 | - | 145 | 26 May 2019 | 55 | 145 | 50.00 | 50.00 | | 0.8566288857 | 42.83 | 2669.42 | 2.90 | 0.00 | | -2.90 | 1000.00 | - | 146 | 27 May 2019 | 54 | 146 | 50.00 | 50.00 | | 0.8557151407 | 42.79 | 2622.27 | 2.85 | 0.00 | | -2.85 | 1000.00 | - | 147 | 28 May 2019 | 53 | 147 | 50.00 | 50.00 | | 0.8548023703 | 42.74 | 2575.07 | 2.80 | 0.00 | | -2.80 | 1000.00 | - | 148 | 29 May 2019 | 52 | 148 | 50.00 | 50.00 | | 0.8538905736 | 42.69 | 2527.82 | 2.75 | 0.00 | | -2.75 | 1000.00 | - | 149 | 30 May 2019 | 51 | 149 | 50.00 | 50.00 | | 0.8529797495 | 42.65 | 2480.52 | 2.70 | 0.00 | | -2.70 | 1000.00 | - | 150 | 31 May 2019 | 50 | 150 | 50.00 | 50.00 | | 0.8520698969 | 42.60 | 2433.17 | 2.65 | 0.00 | | -2.65 | 1000.00 | - | 151 | 01 June 2019 | 49 | 151 | 50.00 | 50.00 | | 0.8511610148 | 42.56 | 2385.77 | 2.60 | 0.00 | | -2.60 | 1000.00 | - | 152 | 02 June 2019 | 48 | 152 | 50.00 | 50.00 | | 0.8502531022 | 42.51 | 2338.31 | 2.55 | 0.00 | | -2.55 | 1000.00 | - | 153 | 03 June 2019 | 47 | 153 | 50.00 | 50.00 | | 0.8493461581 | 42.47 | 2290.81 | 2.50 | 0.00 | | -2.50 | 1000.00 | - | 154 | 04 June 2019 | 46 | 154 | 50.00 | 50.00 | | 0.8484401814 | 42.42 | 2243.26 | 2.45 | 0.00 | | -2.45 | 1000.00 | - | 155 | 05 June 2019 | 45 | 155 | 50.00 | 50.00 | | 0.8475351711 | 42.38 | 2195.65 | 2.40 | 0.00 | | -2.40 | 1000.00 | - | 156 | 06 June 2019 | 44 | 156 | 50.00 | 50.00 | | 0.8466311261 | 42.33 | 2148.00 | 2.34 | 0.00 | | -2.34 | 1000.00 | - | 157 | 07 June 2019 | 43 | 157 | 50.00 | 50.00 | | 0.8457280454 | 42.29 | 2100.29 | 2.29 | 0.00 | | -2.29 | 1000.00 | - | 158 | 08 June 2019 | 42 | 158 | 50.00 | 50.00 | | 0.844825928 | 42.24 | 2052.53 | 2.24 | 0.00 | | -2.24 | 1000.00 | - | 159 | 09 June 2019 | 41 | 159 | 50.00 | 50.00 | | 0.8439247729 | 42.20 | 2004.73 | 2.19 | 0.00 | | -2.19 | 1000.00 | - | 160 | 10 June 2019 | 40 | 160 | 50.00 | 50.00 | | 0.8430245791 | 42.15 | 1956.87 | 2.14 | 0.00 | | -2.14 | 1000.00 | - | 161 | 11 June 2019 | 39 | 161 | 50.00 | 50.00 | | 0.8421253454 | 42.11 | 1908.96 | 2.09 | 0.00 | | -2.09 | 1000.00 | - | 162 | 12 June 2019 | 38 | 162 | 50.00 | 50.00 | | 0.841227071 | 42.06 | 1860.99 | 2.04 | 0.00 | | -2.04 | 1000.00 | - | 163 | 13 June 2019 | 37 | 163 | 50.00 | 50.00 | | 0.8403297547 | 42.02 | 1812.98 | 1.99 | 0.00 | | -1.99 | 1000.00 | - | 164 | 14 June 2019 | 36 | 164 | 50.00 | 50.00 | | 0.8394333956 | 41.97 | 1764.92 | 1.94 | 0.00 | | -1.94 | 1000.00 | - | 165 | 15 June 2019 | 35 | 165 | 50.00 | 50.00 | | 0.8385379925 | 41.93 | 1716.80 | 1.88 | 0.00 | | -1.88 | 1000.00 | - | 166 | 16 June 2019 | 34 | 166 | 50.00 | 50.00 | | 0.8376435446 | 41.88 | 1668.64 | 1.83 | 0.00 | | -1.83 | 1000.00 | - | 167 | 17 June 2019 | 33 | 167 | 50.00 | 50.00 | | 0.8367500508 | 41.84 | 1620.42 | 1.78 | 0.00 | | -1.78 | 1000.00 | - | 168 | 18 June 2019 | 32 | 168 | 50.00 | 50.00 | | 0.83585751 | 41.79 | 1572.15 | 1.73 | 0.00 | | -1.73 | 1000.00 | - | 169 | 19 June 2019 | 31 | 169 | 50.00 | 50.00 | | 0.8349659213 | 41.75 | 1523.83 | 1.68 | 0.00 | | -1.68 | 1000.00 | - | 170 | 20 June 2019 | 30 | 170 | 50.00 | 50.00 | | 0.8340752837 | 41.70 | 1475.45 | 1.63 | 0.00 | | -1.63 | 1000.00 | - | 171 | 21 June 2019 | 29 | 171 | 50.00 | 50.00 | | 0.833185596 | 41.66 | 1427.03 | 1.58 | 0.00 | | -1.58 | 1000.00 | - | 172 | 22 June 2019 | 28 | 172 | 50.00 | 50.00 | | 0.8322968574 | 41.61 | 1378.55 | 1.52 | 0.00 | | -1.52 | 1000.00 | - | 173 | 23 June 2019 | 27 | 173 | 50.00 | 50.00 | | 0.8314090667 | 41.57 | 1330.02 | 1.47 | 0.00 | | -1.47 | 1000.00 | - | 174 | 24 June 2019 | 26 | 174 | 50.00 | 50.00 | | 0.8305222231 | 41.53 | 1281.45 | 1.42 | 0.00 | | -1.42 | 1000.00 | - | 175 | 25 June 2019 | 25 | 175 | 50.00 | 50.00 | | 0.8296363254 | 41.48 | 1232.81 | 1.37 | 0.00 | | -1.37 | 1000.00 | - | 176 | 26 June 2019 | 24 | 176 | 50.00 | 50.00 | | 0.8287513727 | 41.44 | 1184.13 | 1.32 | 0.00 | | -1.32 | 1000.00 | - | 177 | 27 June 2019 | 23 | 177 | 50.00 | 50.00 | | 0.8278673639 | 41.39 | 1135.39 | 1.26 | 0.00 | | -1.26 | 1000.00 | - | 178 | 28 June 2019 | 22 | 178 | 50.00 | 50.00 | | 0.8269842981 | 41.35 | 1086.61 | 1.21 | 0.00 | | -1.21 | 1000.00 | - | 179 | 29 June 2019 | 21 | 179 | 50.00 | 50.00 | | 0.8261021742 | 41.31 | 1037.77 | 1.16 | 0.00 | | -1.16 | 1000.00 | - | 180 | 30 June 2019 | 20 | 180 | 50.00 | 50.00 | | 0.8252209913 | 41.26 | 988.88 | 1.11 | 0.00 | | -1.11 | 1000.00 | - | 181 | 01 July 2019 | 19 | 181 | 50.00 | 50.00 | | 0.8243407483 | 41.22 | 939.93 | 1.06 | 0.00 | | -1.06 | 1000.00 | - | 182 | 02 July 2019 | 18 | 182 | 50.00 | 50.00 | | 0.8234614442 | 41.17 | 890.93 | 1.00 | 0.00 | | -1.00 | 1000.00 | - | 183 | 03 July 2019 | 17 | 183 | 50.00 | 50.00 | | 0.8225830781 | 41.13 | 841.89 | 0.95 | 0.00 | | -0.95 | 1000.00 | - | 184 | 04 July 2019 | 16 | 184 | 50.00 | 50.00 | | 0.8217056489 | 41.09 | 792.79 | 0.90 | 0.00 | | -0.90 | 1000.00 | - | 185 | 05 July 2019 | 15 | 185 | 50.00 | 50.00 | | 0.8208291556 | 41.04 | 743.63 | 0.85 | 0.00 | | -0.85 | 1000.00 | - | 186 | 06 July 2019 | 14 | 186 | 50.00 | 50.00 | | 0.8199535973 | 41.00 | 694.43 | 0.79 | 0.00 | | -0.79 | 1000.00 | - | 187 | 07 July 2019 | 13 | 187 | 50.00 | 50.00 | | 0.8190789729 | 40.95 | 645.17 | 0.74 | 0.00 | | -0.74 | 1000.00 | - | 188 | 08 July 2019 | 12 | 188 | 50.00 | 50.00 | | 0.8182052815 | 40.91 | 595.86 | 0.69 | 0.00 | | -0.69 | 1000.00 | - | 189 | 09 July 2019 | 11 | 189 | 50.00 | 50.00 | | 0.8173325219 | 40.87 | 546.49 | 0.64 | 0.00 | | -0.64 | 1000.00 | - | 190 | 10 July 2019 | 10 | 190 | 50.00 | 50.00 | | 0.8164606934 | 40.82 | 497.08 | 0.58 | 0.00 | | -0.58 | 1000.00 | - | 191 | 11 July 2019 | 9 | 191 | 50.00 | 50.00 | | 0.8155897948 | 40.78 | 447.61 | 0.53 | 0.00 | | -0.53 | 1000.00 | - | 192 | 12 July 2019 | 8 | 192 | 50.00 | 50.00 | | 0.8147198252 | 40.74 | 398.08 | 0.48 | 0.00 | | -0.48 | 1000.00 | - | 193 | 13 July 2019 | 7 | 193 | 50.00 | 50.00 | | 0.8138507835 | 40.69 | 348.51 | 0.43 | 0.00 | | -0.43 | 1000.00 | - | 194 | 14 July 2019 | 6 | 194 | 50.00 | 50.00 | | 0.8129826688 | 40.65 | 298.88 | 0.37 | 0.00 | | -0.37 | 1000.00 | - | 195 | 15 July 2019 | 5 | 195 | 50.00 | 50.00 | | 0.8121154802 | 40.61 | 249.20 | 0.32 | 0.00 | | -0.32 | 1000.00 | - | 196 | 16 July 2019 | 4 | 196 | 50.00 | 50.00 | | 0.8112492165 | 40.56 | 199.47 | 0.27 | 0.00 | | -0.27 | 1000.00 | - | 197 | 17 July 2019 | 3 | 197 | 50.00 | 50.00 | | 0.8103838768 | 40.52 | 149.68 | 0.21 | 0.00 | | -0.21 | 1000.00 | - | 198 | 18 July 2019 | 2 | 198 | 50.00 | 50.00 | | 0.8095194602 | 40.48 | 99.84 | 0.16 | 0.00 | | -0.16 | 1000.00 | - | 199 | 19 July 2019 | 1 | 199 | 50.00 | 50.00 | | 0.8086559657 | 40.43 | 49.95 | 0.11 | 0.00 | | -0.11 | 1000.00 | - | 200 | 20 July 2019 | 0 | 200 | 50.00 | 50.00 | | 0.8077933922 | 40.39 | 0.00 | 0.05 | 0.00 | | -0.05 | 1000.00 | + | paymentNo | paymentDate | expectedPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | + | 0 | 01 January 2019 | -9000.00 | | 1 | -9000.00 | 9000.00 | | | | 1000.00 | + | 1 | 02 January 2019 | 50.00 | | 0.9989333245 | 49.95 | 8959.61 | 9.61 | | | 1000.00 | + | 2 | 03 January 2019 | 50.00 | | 0.9978677868 | 49.89 | 8919.18 | 9.57 | | | 1000.00 | + | 3 | 04 January 2019 | 50.00 | | 0.9968033857 | 49.84 | 8878.70 | 9.52 | | | 1000.00 | + | 4 | 05 January 2019 | 50.00 | | 0.99574012 | 49.79 | 8838.18 | 9.48 | | | 1000.00 | + | 5 | 06 January 2019 | 50.00 | | 0.9946779885 | 49.73 | 8797.62 | 9.44 | | | 1000.00 | + | 6 | 07 January 2019 | 50.00 | | 0.9936169898 | 49.68 | 8757.01 | 9.39 | | | 1000.00 | + | 7 | 08 January 2019 | 50.00 | | 0.992557123 | 49.63 | 8716.36 | 9.35 | | | 1000.00 | + | 8 | 09 January 2019 | 50.00 | | 0.9914983866 | 49.57 | 8675.67 | 9.31 | | | 1000.00 | + | 9 | 10 January 2019 | 50.00 | | 0.9904407796 | 49.52 | 8634.94 | 9.26 | | | 1000.00 | + | 10 | 11 January 2019 | 50.00 | | 0.9893843007 | 49.47 | 8594.16 | 9.22 | | | 1000.00 | + | 11 | 12 January 2019 | 50.00 | | 0.9883289487 | 49.42 | 8553.33 | 9.18 | | | 1000.00 | + | 12 | 13 January 2019 | 50.00 | | 0.9872747225 | 49.36 | 8512.47 | 9.13 | | | 1000.00 | + | 13 | 14 January 2019 | 50.00 | | 0.9862216208 | 49.31 | 8471.56 | 9.09 | | | 1000.00 | + | 14 | 15 January 2019 | 50.00 | | 0.9851696423 | 49.26 | 8430.60 | 9.05 | | | 1000.00 | + | 15 | 16 January 2019 | 50.00 | | 0.984118786 | 49.21 | 8389.61 | 9.00 | | | 1000.00 | + | 16 | 17 January 2019 | 50.00 | | 0.9830690507 | 49.15 | 8348.56 | 8.96 | | | 1000.00 | + | 17 | 18 January 2019 | 50.00 | | 0.982020435 | 49.10 | 8307.48 | 8.91 | | | 1000.00 | + | 18 | 19 January 2019 | 50.00 | | 0.9809729379 | 49.05 | 8266.35 | 8.87 | | | 1000.00 | + | 19 | 20 January 2019 | 50.00 | | 0.9799265581 | 49.00 | 8225.18 | 8.83 | | | 1000.00 | + | 20 | 21 January 2019 | 50.00 | | 0.9788812945 | 48.94 | 8183.96 | 8.78 | | | 1000.00 | + | 21 | 22 January 2019 | 50.00 | | 0.9778371458 | 48.89 | 8142.70 | 8.74 | | | 1000.00 | + | 22 | 23 January 2019 | 50.00 | | 0.9767941109 | 48.84 | 8101.39 | 8.69 | | | 1000.00 | + | 23 | 24 January 2019 | 50.00 | | 0.9757521886 | 48.79 | 8060.04 | 8.65 | | | 1000.00 | + | 24 | 25 January 2019 | 50.00 | | 0.9747113777 | 48.74 | 8018.65 | 8.61 | | | 1000.00 | + | 25 | 26 January 2019 | 50.00 | | 0.9736716769498249835 | 48.68 | 7977.21 | 8.56 | | | 1000.00 | + | 26 | 27 January 2019 | 50.00 | | 0.9726330853 | 48.63 | 7935.73 | 8.52 | | | 1000.00 | + | 27 | 28 January 2019 | 50.00 | | 0.9715956014 | 48.58 | 7894.21 | 8.47 | | | 1000.00 | + | 28 | 29 January 2019 | 50.00 | | 0.9705592242 | 48.53 | 7852.63 | 8.43 | | | 1000.00 | + | 29 | 30 January 2019 | 50.00 | | 0.9695239525 | 48.48 | 7811.02 | 8.39 | | | 1000.00 | + | 30 | 31 January 2019 | 50.00 | | 0.968489785 | 48.42 | 7769.36 | 8.34 | | | 1000.00 | + | 31 | 01 February 2019 | 50.00 | | 0.9674567207 | 48.37 | 7727.66 | 8.30 | | | 1000.00 | + | 32 | 02 February 2019 | 50.00 | | 0.9664247584 | 48.32 | 7685.91 | 8.25 | | | 1000.00 | + | 33 | 03 February 2019 | 50.00 | | 0.9653938968 | 48.27 | 7644.12 | 8.21 | | | 1000.00 | + | 34 | 04 February 2019 | 50.00 | | 0.9643641348 | 48.22 | 7602.28 | 8.16 | | | 1000.00 | + | 35 | 05 February 2019 | 50.00 | | 0.9633354712 | 48.17 | 7560.40 | 8.12 | | | 1000.00 | + | 36 | 06 February 2019 | 50.00 | | 0.9623079049 | 48.12 | 7518.47 | 8.07 | | | 1000.00 | + | 37 | 07 February 2019 | 50.00 | | 0.9612814347 | 48.06 | 7476.50 | 8.03 | | | 1000.00 | + | 38 | 08 February 2019 | 50.00 | | 0.9602560593 | 48.01 | 7434.48 | 7.98 | | | 1000.00 | + | 39 | 09 February 2019 | 50.00 | | 0.9592317777 | 47.96 | 7392.42 | 7.94 | | | 1000.00 | + | 40 | 10 February 2019 | 50.00 | | 0.9582085887 | 47.91 | 7350.31 | 7.89 | | | 1000.00 | + | 41 | 11 February 2019 | 50.00 | | 0.9571864911 | 47.86 | 7308.16 | 7.85 | | | 1000.00 | + | 42 | 12 February 2019 | 50.00 | | 0.9561654838 | 47.81 | 7265.97 | 7.80 | | | 1000.00 | + | 43 | 13 February 2019 | 50.00 | | 0.9551455655 | 47.76 | 7223.72 | 7.76 | | | 1000.00 | + | 44 | 14 February 2019 | 50.00 | | 0.9541267351 | 47.71 | 7181.44 | 7.71 | | | 1000.00 | + | 45 | 15 February 2019 | 50.00 | | 0.9531089916 | 47.66 | 7139.11 | 7.67 | | | 1000.00 | + | 46 | 16 February 2019 | 50.00 | | 0.9520923336 | 47.60 | 7096.73 | 7.62 | | | 1000.00 | + | 47 | 17 February 2019 | 50.00 | | 0.95107676 | 47.55 | 7054.31 | 7.58 | | | 1000.00 | + | 48 | 18 February 2019 | 50.00 | | 0.9500622698 | 47.50 | 7011.84 | 7.53 | | | 1000.00 | + | 49 | 19 February 2019 | 50.00 | | 0.9490488616 | 47.45 | 6969.33 | 7.49 | | | 1000.00 | + | 50 | 20 February 2019 | 50.00 | | 0.9480365345 | 47.40 | 6926.77 | 7.44 | | | 1000.00 | + | 51 | 21 February 2019 | 50.00 | | 0.9470252872 | 47.35 | 6884.17 | 7.40 | | | 1000.00 | + | 52 | 22 February 2019 | 50.00 | | 0.9460151185 | 47.30 | 6841.52 | 7.35 | | | 1000.00 | + | 53 | 23 February 2019 | 50.00 | | 0.9450060274 | 47.25 | 6798.82 | 7.31 | | | 1000.00 | + | 54 | 24 February 2019 | 50.00 | | 0.9439980126 | 47.20 | 6756.08 | 7.26 | | | 1000.00 | + | 55 | 25 February 2019 | 50.00 | | 0.9429910731 | 47.15 | 6713.30 | 7.21 | | | 1000.00 | + | 56 | 26 February 2019 | 50.00 | | 0.9419852077 | 47.10 | 6670.47 | 7.17 | | | 1000.00 | + | 57 | 27 February 2019 | 50.00 | | 0.9409804151 | 47.05 | 6627.59 | 7.12 | | | 1000.00 | + | 58 | 28 February 2019 | 50.00 | | 0.9399766944 | 47.00 | 6584.67 | 7.08 | | | 1000.00 | + | 59 | 01 March 2019 | 50.00 | | 0.9389740443 | 46.95 | 6541.70 | 7.03 | | | 1000.00 | + | 60 | 02 March 2019 | 50.00 | | 0.9379724637 | 46.90 | 6498.68 | 6.99 | | | 1000.00 | + | 61 | 03 March 2019 | 50.00 | | 0.9369719515 | 46.85 | 6455.62 | 6.94 | | | 1000.00 | + | 62 | 04 March 2019 | 50.00 | | 0.9359725065 | 46.80 | 6412.51 | 6.89 | | | 1000.00 | + | 63 | 05 March 2019 | 50.00 | | 0.9349741276 | 46.75 | 6369.36 | 6.85 | | | 1000.00 | + | 64 | 06 March 2019 | 50.00 | | 0.9339768136 | 46.70 | 6326.16 | 6.80 | | | 1000.00 | + | 65 | 07 March 2019 | 50.00 | | 0.9329805635 | 46.65 | 6282.92 | 6.76 | | | 1000.00 | + | 66 | 08 March 2019 | 50.00 | | 0.931985376 | 46.60 | 6239.63 | 6.71 | | | 1000.00 | + | 67 | 09 March 2019 | 50.00 | | 0.93099125 | 46.55 | 6196.29 | 6.66 | | | 1000.00 | + | 68 | 10 March 2019 | 50.00 | | 0.9299981845 | 46.50 | 6152.91 | 6.62 | | | 1000.00 | + | 69 | 11 March 2019 | 50.00 | | 0.9290061782 | 46.45 | 6109.48 | 6.57 | | | 1000.00 | + | 70 | 12 March 2019 | 50.00 | | 0.9280152301 | 46.40 | 6066.00 | 6.52 | | | 1000.00 | + | 71 | 13 March 2019 | 50.00 | | 0.927025339 | 46.35 | 6022.48 | 6.48 | | | 1000.00 | + | 72 | 14 March 2019 | 50.00 | | 0.9260365038 | 46.30 | 5978.91 | 6.43 | | | 1000.00 | + | 73 | 15 March 2019 | 50.00 | | 0.9250487234 | 46.25 | 5935.29 | 6.38 | | | 1000.00 | + | 74 | 16 March 2019 | 50.00 | | 0.9240619966 | 46.20 | 5891.63 | 6.34 | | | 1000.00 | + | 75 | 17 March 2019 | 50.00 | | 0.9230763224 | 46.15 | 5847.92 | 6.29 | | | 1000.00 | + | 76 | 18 March 2019 | 50.00 | | 0.9220916995 | 46.10 | 5804.17 | 6.24 | | | 1000.00 | + | 77 | 19 March 2019 | 50.00 | | 0.9211081269 | 46.06 | 5760.36 | 6.20 | | | 1000.00 | + | 78 | 20 March 2019 | 50.00 | | 0.9201256034 | 46.01 | 5716.52 | 6.15 | | | 1000.00 | + | 79 | 21 March 2019 | 50.00 | | 0.919144128 | 45.96 | 5672.62 | 6.10 | | | 1000.00 | + | 80 | 22 March 2019 | 50.00 | | 0.9181636995 | 45.91 | 5628.68 | 6.06 | | | 1000.00 | + | 81 | 23 March 2019 | 50.00 | | 0.9171843168 | 45.86 | 5584.69 | 6.01 | | | 1000.00 | + | 82 | 24 March 2019 | 50.00 | | 0.9162059788 | 45.81 | 5540.65 | 5.96 | | | 1000.00 | + | 83 | 25 March 2019 | 50.00 | | 0.9152286843 | 45.76 | 5496.57 | 5.92 | | | 1000.00 | + | 84 | 26 March 2019 | 50.00 | | 0.9142524323 | 45.71 | 5452.44 | 5.87 | | | 1000.00 | + | 85 | 27 March 2019 | 50.00 | | 0.9132772217 | 45.66 | 5408.26 | 5.82 | | | 1000.00 | + | 86 | 28 March 2019 | 50.00 | | 0.9123030513 | 45.62 | 5364.03 | 5.78 | | | 1000.00 | + | 87 | 29 March 2019 | 50.00 | | 0.91132992 | 45.57 | 5319.76 | 5.73 | | | 1000.00 | + | 88 | 30 March 2019 | 50.00 | | 0.9103578267 | 45.52 | 5275.44 | 5.68 | | | 1000.00 | + | 89 | 31 March 2019 | 50.00 | | 0.9093867703 | 45.47 | 5231.08 | 5.63 | | | 1000.00 | + | 90 | 01 April 2019 | 50.00 | | 0.9084167498 | 45.42 | 5186.66 | 5.59 | | | 1000.00 | + | 91 | 02 April 2019 | 50.00 | | 0.9074477639 | 45.37 | 5142.20 | 5.54 | | | 1000.00 | + | 92 | 03 April 2019 | 50.00 | | 0.9064798116 | 45.32 | 5097.69 | 5.49 | | | 1000.00 | + | 93 | 04 April 2019 | 50.00 | | 0.9055128918 | 45.28 | 5053.13 | 5.44 | | | 1000.00 | + | 94 | 05 April 2019 | 50.00 | | 0.9045470035 | 45.23 | 5008.53 | 5.40 | | | 1000.00 | + | 95 | 06 April 2019 | 50.00 | | 0.9035821453 | 45.18 | 4963.88 | 5.35 | | | 1000.00 | + | 96 | 07 April 2019 | 50.00 | | 0.9026183164 | 45.13 | 4919.18 | 5.30 | | | 1000.00 | + | 97 | 08 April 2019 | 50.00 | | 0.9016555156 | 45.08 | 4874.43 | 5.25 | | | 1000.00 | + | 98 | 09 April 2019 | 50.00 | | 0.9006937418 | 45.03 | 4829.64 | 5.20 | | | 1000.00 | + | 99 | 10 April 2019 | 50.00 | | 0.8997329939 | 44.99 | 4784.79 | 5.16 | | | 1000.00 | + | 100 | 11 April 2019 | 50.00 | | 0.8987732707 | 44.94 | 4739.90 | 5.11 | | | 1000.00 | + | 101 | 12 April 2019 | 50.00 | | 0.8978145713 | 44.89 | 4694.96 | 5.06 | | | 1000.00 | + | 102 | 13 April 2019 | 50.00 | | 0.8968568945 | 44.84 | 4649.98 | 5.01 | | | 1000.00 | + | 103 | 14 April 2019 | 50.00 | | 0.8959002393 | 44.80 | 4604.94 | 4.97 | | | 1000.00 | + | 104 | 15 April 2019 | 50.00 | | 0.8949446045 | 44.75 | 4559.86 | 4.92 | | | 1000.00 | + | 105 | 16 April 2019 | 50.00 | | 0.893989989 | 44.70 | 4514.73 | 4.87 | | | 1000.00 | + | 106 | 17 April 2019 | 50.00 | | 0.8930363918 | 44.65 | 4469.55 | 4.82 | | | 1000.00 | + | 107 | 18 April 2019 | 50.00 | | 0.8920838118 | 44.60 | 4424.32 | 4.77 | | | 1000.00 | + | 108 | 19 April 2019 | 50.00 | | 0.8911322479 | 44.56 | 4379.05 | 4.72 | | | 1000.00 | + | 109 | 20 April 2019 | 50.00 | | 0.890181699 | 44.51 | 4333.72 | 4.68 | | | 1000.00 | + | 110 | 21 April 2019 | 50.00 | | 0.889232164 | 44.46 | 4288.35 | 4.63 | | | 1000.00 | + | 111 | 22 April 2019 | 50.00 | | 0.8882836418 | 44.41 | 4242.93 | 4.58 | | | 1000.00 | + | 112 | 23 April 2019 | 50.00 | | 0.8873361314 | 44.37 | 4197.46 | 4.53 | | | 1000.00 | + | 113 | 24 April 2019 | 50.00 | | 0.8863896318 | 44.32 | 4151.94 | 4.48 | | | 1000.00 | + | 114 | 25 April 2019 | 50.00 | | 0.8854441417 | 44.27 | 4106.38 | 4.43 | | | 1000.00 | + | 115 | 26 April 2019 | 50.00 | | 0.8844996601 | 44.22 | 4060.76 | 4.38 | | | 1000.00 | + | 116 | 27 April 2019 | 50.00 | | 0.883556186 | 44.18 | 4015.10 | 4.34 | | | 1000.00 | + | 117 | 28 April 2019 | 50.00 | | 0.8826137183 | 44.13 | 3969.38 | 4.29 | | | 1000.00 | + | 118 | 29 April 2019 | 50.00 | | 0.8816722559 | 44.08 | 3923.62 | 4.24 | | | 1000.00 | + | 119 | 30 April 2019 | 50.00 | | 0.8807317977 | 44.04 | 3877.81 | 4.19 | | | 1000.00 | + | 120 | 01 May 2019 | 50.00 | | 0.8797923427 | 43.99 | 3831.95 | 4.14 | | | 1000.00 | + | 121 | 02 May 2019 | 50.00 | | 0.8788538898 | 43.94 | 3786.04 | 4.09 | | | 1000.00 | + | 122 | 03 May 2019 | 50.00 | | 0.8779164379 | 43.90 | 3740.09 | 4.04 | | | 1000.00 | + | 123 | 04 May 2019 | 50.00 | | 0.876979986 | 43.85 | 3694.08 | 3.99 | | | 1000.00 | + | 124 | 05 May 2019 | 50.00 | | 0.8760445329 | 43.80 | 3648.03 | 3.94 | | | 1000.00 | + | 125 | 06 May 2019 | 50.00 | | 0.8751100777 | 43.76 | 3601.92 | 3.90 | | | 1000.00 | + | 126 | 07 May 2019 | 50.00 | | 0.8741766193 | 43.71 | 3555.77 | 3.85 | | | 1000.00 | + | 127 | 08 May 2019 | 50.00 | | 0.8732441565 | 43.66 | 3509.56 | 3.80 | | | 1000.00 | + | 128 | 09 May 2019 | 50.00 | | 0.8723126884 | 43.62 | 3463.31 | 3.75 | | | 1000.00 | + | 129 | 10 May 2019 | 50.00 | | 0.8713822138 | 43.57 | 3417.01 | 3.70 | | | 1000.00 | + | 130 | 11 May 2019 | 50.00 | | 0.8704527318 | 43.52 | 3370.66 | 3.65 | | | 1000.00 | + | 131 | 12 May 2019 | 50.00 | | 0.8695242412 | 43.48 | 3324.26 | 3.60 | | | 1000.00 | + | 132 | 13 May 2019 | 50.00 | | 0.868596741 | 43.43 | 3277.81 | 3.55 | | | 1000.00 | + | 133 | 14 May 2019 | 50.00 | | 0.8676702302 | 43.38 | 3231.31 | 3.50 | | | 1000.00 | + | 134 | 15 May 2019 | 50.00 | | 0.8667447076 | 43.34 | 3184.76 | 3.45 | | | 1000.00 | + | 135 | 16 May 2019 | 50.00 | | 0.8658201723 | 43.29 | 3138.16 | 3.40 | | | 1000.00 | + | 136 | 17 May 2019 | 50.00 | | 0.8648966231 | 43.24 | 3091.51 | 3.35 | | | 1000.00 | + | 137 | 18 May 2019 | 50.00 | | 0.8639740591 | 43.20 | 3044.81 | 3.30 | | | 1000.00 | + | 138 | 19 May 2019 | 50.00 | | 0.8630524792 | 43.15 | 2998.06 | 3.25 | | | 1000.00 | + | 139 | 20 May 2019 | 50.00 | | 0.8621318823 | 43.11 | 2951.26 | 3.20 | | | 1000.00 | + | 140 | 21 May 2019 | 50.00 | | 0.8612122673 | 43.06 | 2904.42 | 3.15 | | | 1000.00 | + | 141 | 22 May 2019 | 50.00 | | 0.8602936333 | 43.01 | 2857.52 | 3.10 | | | 1000.00 | + | 142 | 23 May 2019 | 50.00 | | 0.8593759792 | 42.97 | 2810.57 | 3.05 | | | 1000.00 | + | 143 | 24 May 2019 | 50.00 | | 0.8584593039 | 42.92 | 2763.57 | 3.00 | | | 1000.00 | + | 144 | 25 May 2019 | 50.00 | | 0.8575436064 | 42.88 | 2716.52 | 2.95 | | | 1000.00 | + | 145 | 26 May 2019 | 50.00 | | 0.8566288857 | 42.83 | 2669.42 | 2.90 | | | 1000.00 | + | 146 | 27 May 2019 | 50.00 | | 0.8557151407 | 42.79 | 2622.27 | 2.85 | | | 1000.00 | + | 147 | 28 May 2019 | 50.00 | | 0.8548023703 | 42.74 | 2575.07 | 2.80 | | | 1000.00 | + | 148 | 29 May 2019 | 50.00 | | 0.8538905736 | 42.69 | 2527.82 | 2.75 | | | 1000.00 | + | 149 | 30 May 2019 | 50.00 | | 0.8529797495 | 42.65 | 2480.52 | 2.70 | | | 1000.00 | + | 150 | 31 May 2019 | 50.00 | | 0.8520698969 | 42.60 | 2433.17 | 2.65 | | | 1000.00 | + | 151 | 01 June 2019 | 50.00 | | 0.8511610148 | 42.56 | 2385.77 | 2.60 | | | 1000.00 | + | 152 | 02 June 2019 | 50.00 | | 0.8502531022 | 42.51 | 2338.31 | 2.55 | | | 1000.00 | + | 153 | 03 June 2019 | 50.00 | | 0.8493461581 | 42.47 | 2290.81 | 2.50 | | | 1000.00 | + | 154 | 04 June 2019 | 50.00 | | 0.8484401814 | 42.42 | 2243.26 | 2.45 | | | 1000.00 | + | 155 | 05 June 2019 | 50.00 | | 0.8475351711 | 42.38 | 2195.65 | 2.40 | | | 1000.00 | + | 156 | 06 June 2019 | 50.00 | | 0.8466311261 | 42.33 | 2148.00 | 2.34 | | | 1000.00 | + | 157 | 07 June 2019 | 50.00 | | 0.8457280454 | 42.29 | 2100.29 | 2.29 | | | 1000.00 | + | 158 | 08 June 2019 | 50.00 | | 0.844825928 | 42.24 | 2052.53 | 2.24 | | | 1000.00 | + | 159 | 09 June 2019 | 50.00 | | 0.8439247729 | 42.20 | 2004.73 | 2.19 | | | 1000.00 | + | 160 | 10 June 2019 | 50.00 | | 0.8430245791 | 42.15 | 1956.87 | 2.14 | | | 1000.00 | + | 161 | 11 June 2019 | 50.00 | | 0.8421253454 | 42.11 | 1908.96 | 2.09 | | | 1000.00 | + | 162 | 12 June 2019 | 50.00 | | 0.841227071 | 42.06 | 1860.99 | 2.04 | | | 1000.00 | + | 163 | 13 June 2019 | 50.00 | | 0.8403297547 | 42.02 | 1812.98 | 1.99 | | | 1000.00 | + | 164 | 14 June 2019 | 50.00 | | 0.8394333956 | 41.97 | 1764.92 | 1.94 | | | 1000.00 | + | 165 | 15 June 2019 | 50.00 | | 0.8385379925 | 41.93 | 1716.80 | 1.88 | | | 1000.00 | + | 166 | 16 June 2019 | 50.00 | | 0.8376435446 | 41.88 | 1668.64 | 1.83 | | | 1000.00 | + | 167 | 17 June 2019 | 50.00 | | 0.8367500508 | 41.84 | 1620.42 | 1.78 | | | 1000.00 | + | 168 | 18 June 2019 | 50.00 | | 0.83585751 | 41.79 | 1572.15 | 1.73 | | | 1000.00 | + | 169 | 19 June 2019 | 50.00 | | 0.8349659213 | 41.75 | 1523.83 | 1.68 | | | 1000.00 | + | 170 | 20 June 2019 | 50.00 | | 0.8340752837 | 41.70 | 1475.45 | 1.63 | | | 1000.00 | + | 171 | 21 June 2019 | 50.00 | | 0.833185596 | 41.66 | 1427.03 | 1.58 | | | 1000.00 | + | 172 | 22 June 2019 | 50.00 | | 0.8322968574 | 41.61 | 1378.55 | 1.52 | | | 1000.00 | + | 173 | 23 June 2019 | 50.00 | | 0.8314090667 | 41.57 | 1330.02 | 1.47 | | | 1000.00 | + | 174 | 24 June 2019 | 50.00 | | 0.8305222231 | 41.53 | 1281.45 | 1.42 | | | 1000.00 | + | 175 | 25 June 2019 | 50.00 | | 0.8296363254 | 41.48 | 1232.81 | 1.37 | | | 1000.00 | + | 176 | 26 June 2019 | 50.00 | | 0.8287513727 | 41.44 | 1184.13 | 1.32 | | | 1000.00 | + | 177 | 27 June 2019 | 50.00 | | 0.8278673639 | 41.39 | 1135.39 | 1.26 | | | 1000.00 | + | 178 | 28 June 2019 | 50.00 | | 0.8269842981 | 41.35 | 1086.61 | 1.21 | | | 1000.00 | + | 179 | 29 June 2019 | 50.00 | | 0.8261021742 | 41.31 | 1037.77 | 1.16 | | | 1000.00 | + | 180 | 30 June 2019 | 50.00 | | 0.8252209913 | 41.26 | 988.88 | 1.11 | | | 1000.00 | + | 181 | 01 July 2019 | 50.00 | | 0.8243407483 | 41.22 | 939.93 | 1.06 | | | 1000.00 | + | 182 | 02 July 2019 | 50.00 | | 0.8234614442 | 41.17 | 890.93 | 1.00 | | | 1000.00 | + | 183 | 03 July 2019 | 50.00 | | 0.8225830781 | 41.13 | 841.89 | 0.95 | | | 1000.00 | + | 184 | 04 July 2019 | 50.00 | | 0.8217056489 | 41.09 | 792.79 | 0.90 | | | 1000.00 | + | 185 | 05 July 2019 | 50.00 | | 0.8208291556 | 41.04 | 743.63 | 0.85 | | | 1000.00 | + | 186 | 06 July 2019 | 50.00 | | 0.8199535973 | 41.00 | 694.43 | 0.79 | | | 1000.00 | + | 187 | 07 July 2019 | 50.00 | | 0.8190789729 | 40.95 | 645.17 | 0.74 | | | 1000.00 | + | 188 | 08 July 2019 | 50.00 | | 0.8182052815 | 40.91 | 595.86 | 0.69 | | | 1000.00 | + | 189 | 09 July 2019 | 50.00 | | 0.8173325219 | 40.87 | 546.49 | 0.64 | | | 1000.00 | + | 190 | 10 July 2019 | 50.00 | | 0.8164606934 | 40.82 | 497.08 | 0.58 | | | 1000.00 | + | 191 | 11 July 2019 | 50.00 | | 0.8155897948 | 40.78 | 447.61 | 0.53 | | | 1000.00 | + | 192 | 12 July 2019 | 50.00 | | 0.8147198252 | 40.74 | 398.08 | 0.48 | | | 1000.00 | + | 193 | 13 July 2019 | 50.00 | | 0.8138507835 | 40.69 | 348.51 | 0.43 | | | 1000.00 | + | 194 | 14 July 2019 | 50.00 | | 0.8129826688 | 40.65 | 298.88 | 0.37 | | | 1000.00 | + | 195 | 15 July 2019 | 50.00 | | 0.8121154802 | 40.61 | 249.20 | 0.32 | | | 1000.00 | + | 196 | 16 July 2019 | 50.00 | | 0.8112492165 | 40.56 | 199.47 | 0.27 | | | 1000.00 | + | 197 | 17 July 2019 | 50.00 | | 0.8103838768 | 40.52 | 149.68 | 0.21 | | | 1000.00 | + | 198 | 18 July 2019 | 50.00 | | 0.8095194602 | 40.48 | 99.84 | 0.16 | | | 1000.00 | + | 199 | 19 July 2019 | 50.00 | | 0.8086559657 | 40.43 | 49.95 | 0.11 | | | 1000.00 | + | 200 | 20 July 2019 | 50.00 | | 0.8077933922 | 40.39 | 0.00 | 0.05 | | | 1000.00 | When Admin sets the business date to "02 January 2019" And Customer makes repayment on "02 January 2019" with 50 transaction amount on Working Capital loan When Admin sets the business date to "03 January 2019" @@ -946,205 +946,205 @@ Feature: Working Capital Loan Repayment When Admin sets the business date to "04 January 2019" And Customer makes repayment on "04 January 2019" with 50 transaction amount on Working Capital loan Then Working Capital loan amortization schedule has 201 periods, with the following data for periods: - | paymentNo | paymentDate | count | paymentsLeft | expectedPaymentAmount | forecastPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | netAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | - | 0 | 01 January 2019 | 203 | 0 | -9000.00 | | | 1 | -9000.00 | 9000.00 | | | | | 1000.00 | - | 1 | 02 January 2019 | 202 | 0 | 50.00 | 50.00 | 50.00 | 1 | 50.00 | 8959.61 | 9.61 | 28.70 | 9.61 | 0.00 | 990.39 | - | 2 | 03 January 2019 | 201 | 0 | 50.00 | 50.00 | 50.00 | 1 | 50.00 | 8919.18 | 9.57 | 19.09 | 9.57 | 0.00 | 980.82 | - | 3 | 04 January 2019 | 200 | 0 | 50.00 | 50.00 | 50.00 | 1 | 50.00 | 8878.70 | 9.52 | 9.52 | 9.52 | 0.00 | 971.30 | - | 4 | 05 January 2019 | 199 | 1 | 50.00 | 50.00 | | 0.9989333245 | 49.95 | 8838.18 | 9.48 | 9.52 | | -9.48 | 971.30 | - | 5 | 06 January 2019 | 198 | 2 | 50.00 | 50.00 | | 0.9978677868 | 49.89 | 8797.62 | 9.44 | 9.52 | | -9.44 | 971.30 | - | 6 | 07 January 2019 | 197 | 3 | 50.00 | 50.00 | | 0.9968033857 | 49.84 | 8757.01 | 9.39 | 9.52 | | -9.39 | 971.30 | - | 7 | 08 January 2019 | 196 | 4 | 50.00 | 50.00 | | 0.99574012 | 49.79 | 8716.36 | 9.35 | 9.52 | | -9.35 | 971.30 | - | 8 | 09 January 2019 | 195 | 5 | 50.00 | 50.00 | | 0.9946779885 | 49.73 | 8675.67 | 9.31 | 9.52 | | -9.31 | 971.30 | - | 9 | 10 January 2019 | 194 | 6 | 50.00 | 50.00 | | 0.9936169898 | 49.68 | 8634.94 | 9.26 | 9.52 | | -9.26 | 971.30 | - | 10 | 11 January 2019 | 193 | 7 | 50.00 | 50.00 | | 0.992557123 | 49.63 | 8594.16 | 9.22 | 9.52 | | -9.22 | 971.30 | - | 11 | 12 January 2019 | 192 | 8 | 50.00 | 50.00 | | 0.9914983866 | 49.57 | 8553.33 | 9.18 | 9.52 | | -9.18 | 971.30 | - | 12 | 13 January 2019 | 191 | 9 | 50.00 | 50.00 | | 0.9904407796 | 49.52 | 8512.47 | 9.13 | 9.52 | | -9.13 | 971.30 | - | 13 | 14 January 2019 | 190 | 10 | 50.00 | 50.00 | | 0.9893843007 | 49.47 | 8471.56 | 9.09 | 9.52 | | -9.09 | 971.30 | - | 14 | 15 January 2019 | 189 | 11 | 50.00 | 50.00 | | 0.9883289487 | 49.42 | 8430.60 | 9.05 | 9.52 | | -9.05 | 971.30 | - | 15 | 16 January 2019 | 188 | 12 | 50.00 | 50.00 | | 0.9872747225 | 49.36 | 8389.61 | 9.00 | 9.52 | | -9.00 | 971.30 | - | 16 | 17 January 2019 | 187 | 13 | 50.00 | 50.00 | | 0.9862216208 | 49.31 | 8348.56 | 8.96 | 9.52 | | -8.96 | 971.30 | - | 17 | 18 January 2019 | 186 | 14 | 50.00 | 50.00 | | 0.9851696423 | 49.26 | 8307.48 | 8.91 | 9.52 | | -8.91 | 971.30 | - | 18 | 19 January 2019 | 185 | 15 | 50.00 | 50.00 | | 0.984118786 | 49.21 | 8266.35 | 8.87 | 9.52 | | -8.87 | 971.30 | - | 19 | 20 January 2019 | 184 | 16 | 50.00 | 50.00 | | 0.9830690507 | 49.15 | 8225.18 | 8.83 | 9.52 | | -8.83 | 971.30 | - | 20 | 21 January 2019 | 183 | 17 | 50.00 | 50.00 | | 0.982020435 | 49.10 | 8183.96 | 8.78 | 9.52 | | -8.78 | 971.30 | - | 21 | 22 January 2019 | 182 | 18 | 50.00 | 50.00 | | 0.9809729379 | 49.05 | 8142.70 | 8.74 | 9.52 | | -8.74 | 971.30 | - | 22 | 23 January 2019 | 181 | 19 | 50.00 | 50.00 | | 0.9799265581 | 49.00 | 8101.39 | 8.69 | 9.52 | | -8.69 | 971.30 | - | 23 | 24 January 2019 | 180 | 20 | 50.00 | 50.00 | | 0.9788812945 | 48.94 | 8060.04 | 8.65 | 9.52 | | -8.65 | 971.30 | - | 24 | 25 January 2019 | 179 | 21 | 50.00 | 50.00 | | 0.9778371458 | 48.89 | 8018.65 | 8.61 | 9.52 | | -8.61 | 971.30 | - | 25 | 26 January 2019 | 178 | 22 | 50.00 | 50.00 | | 0.9767941109 | 48.84 | 7977.21 | 8.56 | 9.52 | | -8.56 | 971.30 | - | 26 | 27 January 2019 | 177 | 23 | 50.00 | 50.00 | | 0.9757521886 | 48.79 | 7935.73 | 8.52 | 9.52 | | -8.52 | 971.30 | - | 27 | 28 January 2019 | 176 | 24 | 50.00 | 50.00 | | 0.9747113777 | 48.74 | 7894.21 | 8.47 | 9.52 | | -8.47 | 971.30 | - | 28 | 29 January 2019 | 175 | 25 | 50.00 | 50.00 | | 0.9736716769498249835 | 48.68 | 7852.63 | 8.43 | 9.52 | | -8.43 | 971.30 | - | 29 | 30 January 2019 | 174 | 26 | 50.00 | 50.00 | | 0.9726330853 | 48.63 | 7811.02 | 8.39 | 9.52 | | -8.39 | 971.30 | - | 30 | 31 January 2019 | 173 | 27 | 50.00 | 50.00 | | 0.9715956014 | 48.58 | 7769.36 | 8.34 | 9.52 | | -8.34 | 971.30 | - | 31 | 01 February 2019 | 172 | 28 | 50.00 | 50.00 | | 0.9705592242 | 48.53 | 7727.66 | 8.30 | 9.52 | | -8.30 | 971.30 | - | 32 | 02 February 2019 | 171 | 29 | 50.00 | 50.00 | | 0.9695239525 | 48.48 | 7685.91 | 8.25 | 9.52 | | -8.25 | 971.30 | - | 33 | 03 February 2019 | 170 | 30 | 50.00 | 50.00 | | 0.968489785 | 48.42 | 7644.12 | 8.21 | 9.52 | | -8.21 | 971.30 | - | 34 | 04 February 2019 | 169 | 31 | 50.00 | 50.00 | | 0.9674567207 | 48.37 | 7602.28 | 8.16 | 9.52 | | -8.16 | 971.30 | - | 35 | 05 February 2019 | 168 | 32 | 50.00 | 50.00 | | 0.9664247584 | 48.32 | 7560.40 | 8.12 | 9.52 | | -8.12 | 971.30 | - | 36 | 06 February 2019 | 167 | 33 | 50.00 | 50.00 | | 0.9653938968 | 48.27 | 7518.47 | 8.07 | 9.52 | | -8.07 | 971.30 | - | 37 | 07 February 2019 | 166 | 34 | 50.00 | 50.00 | | 0.9643641348 | 48.22 | 7476.50 | 8.03 | 9.52 | | -8.03 | 971.30 | - | 38 | 08 February 2019 | 165 | 35 | 50.00 | 50.00 | | 0.9633354712 | 48.17 | 7434.48 | 7.98 | 9.52 | | -7.98 | 971.30 | - | 39 | 09 February 2019 | 164 | 36 | 50.00 | 50.00 | | 0.9623079049 | 48.12 | 7392.42 | 7.94 | 9.52 | | -7.94 | 971.30 | - | 40 | 10 February 2019 | 163 | 37 | 50.00 | 50.00 | | 0.9612814347 | 48.06 | 7350.31 | 7.89 | 9.52 | | -7.89 | 971.30 | - | 41 | 11 February 2019 | 162 | 38 | 50.00 | 50.00 | | 0.9602560593 | 48.01 | 7308.16 | 7.85 | 9.52 | | -7.85 | 971.30 | - | 42 | 12 February 2019 | 161 | 39 | 50.00 | 50.00 | | 0.9592317777 | 47.96 | 7265.97 | 7.80 | 9.52 | | -7.80 | 971.30 | - | 43 | 13 February 2019 | 160 | 40 | 50.00 | 50.00 | | 0.9582085887 | 47.91 | 7223.72 | 7.76 | 9.52 | | -7.76 | 971.30 | - | 44 | 14 February 2019 | 159 | 41 | 50.00 | 50.00 | | 0.9571864911 | 47.86 | 7181.44 | 7.71 | 9.52 | | -7.71 | 971.30 | - | 45 | 15 February 2019 | 158 | 42 | 50.00 | 50.00 | | 0.9561654838 | 47.81 | 7139.11 | 7.67 | 9.52 | | -7.67 | 971.30 | - | 46 | 16 February 2019 | 157 | 43 | 50.00 | 50.00 | | 0.9551455655 | 47.76 | 7096.73 | 7.62 | 9.52 | | -7.62 | 971.30 | - | 47 | 17 February 2019 | 156 | 44 | 50.00 | 50.00 | | 0.9541267351 | 47.71 | 7054.31 | 7.58 | 9.52 | | -7.58 | 971.30 | - | 48 | 18 February 2019 | 155 | 45 | 50.00 | 50.00 | | 0.9531089916 | 47.66 | 7011.84 | 7.53 | 9.52 | | -7.53 | 971.30 | - | 49 | 19 February 2019 | 154 | 46 | 50.00 | 50.00 | | 0.9520923336 | 47.60 | 6969.33 | 7.49 | 9.52 | | -7.49 | 971.30 | - | 50 | 20 February 2019 | 153 | 47 | 50.00 | 50.00 | | 0.95107676 | 47.55 | 6926.77 | 7.44 | 9.52 | | -7.44 | 971.30 | - | 51 | 21 February 2019 | 152 | 48 | 50.00 | 50.00 | | 0.9500622698 | 47.50 | 6884.17 | 7.40 | 9.52 | | -7.40 | 971.30 | - | 52 | 22 February 2019 | 151 | 49 | 50.00 | 50.00 | | 0.9490488616 | 47.45 | 6841.52 | 7.35 | 9.52 | | -7.35 | 971.30 | - | 53 | 23 February 2019 | 150 | 50 | 50.00 | 50.00 | | 0.9480365345 | 47.40 | 6798.82 | 7.31 | 9.52 | | -7.31 | 971.30 | - | 54 | 24 February 2019 | 149 | 51 | 50.00 | 50.00 | | 0.9470252872 | 47.35 | 6756.08 | 7.26 | 9.52 | | -7.26 | 971.30 | - | 55 | 25 February 2019 | 148 | 52 | 50.00 | 50.00 | | 0.9460151185 | 47.30 | 6713.30 | 7.21 | 9.52 | | -7.21 | 971.30 | - | 56 | 26 February 2019 | 147 | 53 | 50.00 | 50.00 | | 0.9450060274 | 47.25 | 6670.47 | 7.17 | 9.52 | | -7.17 | 971.30 | - | 57 | 27 February 2019 | 146 | 54 | 50.00 | 50.00 | | 0.9439980126 | 47.20 | 6627.59 | 7.12 | 9.52 | | -7.12 | 971.30 | - | 58 | 28 February 2019 | 145 | 55 | 50.00 | 50.00 | | 0.9429910731 | 47.15 | 6584.67 | 7.08 | 9.52 | | -7.08 | 971.30 | - | 59 | 01 March 2019 | 144 | 56 | 50.00 | 50.00 | | 0.9419852077 | 47.10 | 6541.70 | 7.03 | 9.52 | | -7.03 | 971.30 | - | 60 | 02 March 2019 | 143 | 57 | 50.00 | 50.00 | | 0.9409804151 | 47.05 | 6498.68 | 6.99 | 9.52 | | -6.99 | 971.30 | - | 61 | 03 March 2019 | 142 | 58 | 50.00 | 50.00 | | 0.9399766944 | 47.00 | 6455.62 | 6.94 | 9.52 | | -6.94 | 971.30 | - | 62 | 04 March 2019 | 141 | 59 | 50.00 | 50.00 | | 0.9389740443 | 46.95 | 6412.51 | 6.89 | 9.52 | | -6.89 | 971.30 | - | 63 | 05 March 2019 | 140 | 60 | 50.00 | 50.00 | | 0.9379724637 | 46.90 | 6369.36 | 6.85 | 9.52 | | -6.85 | 971.30 | - | 64 | 06 March 2019 | 139 | 61 | 50.00 | 50.00 | | 0.9369719515 | 46.85 | 6326.16 | 6.80 | 9.52 | | -6.80 | 971.30 | - | 65 | 07 March 2019 | 138 | 62 | 50.00 | 50.00 | | 0.9359725065 | 46.80 | 6282.92 | 6.76 | 9.52 | | -6.76 | 971.30 | - | 66 | 08 March 2019 | 137 | 63 | 50.00 | 50.00 | | 0.9349741276 | 46.75 | 6239.63 | 6.71 | 9.52 | | -6.71 | 971.30 | - | 67 | 09 March 2019 | 136 | 64 | 50.00 | 50.00 | | 0.9339768136 | 46.70 | 6196.29 | 6.66 | 9.52 | | -6.66 | 971.30 | - | 68 | 10 March 2019 | 135 | 65 | 50.00 | 50.00 | | 0.9329805635 | 46.65 | 6152.91 | 6.62 | 9.52 | | -6.62 | 971.30 | - | 69 | 11 March 2019 | 134 | 66 | 50.00 | 50.00 | | 0.931985376 | 46.60 | 6109.48 | 6.57 | 9.52 | | -6.57 | 971.30 | - | 70 | 12 March 2019 | 133 | 67 | 50.00 | 50.00 | | 0.93099125 | 46.55 | 6066.00 | 6.52 | 9.52 | | -6.52 | 971.30 | - | 71 | 13 March 2019 | 132 | 68 | 50.00 | 50.00 | | 0.9299981845 | 46.50 | 6022.48 | 6.48 | 9.52 | | -6.48 | 971.30 | - | 72 | 14 March 2019 | 131 | 69 | 50.00 | 50.00 | | 0.9290061782 | 46.45 | 5978.91 | 6.43 | 9.52 | | -6.43 | 971.30 | - | 73 | 15 March 2019 | 130 | 70 | 50.00 | 50.00 | | 0.9280152301 | 46.40 | 5935.29 | 6.38 | 9.52 | | -6.38 | 971.30 | - | 74 | 16 March 2019 | 129 | 71 | 50.00 | 50.00 | | 0.927025339 | 46.35 | 5891.63 | 6.34 | 9.52 | | -6.34 | 971.30 | - | 75 | 17 March 2019 | 128 | 72 | 50.00 | 50.00 | | 0.9260365038 | 46.30 | 5847.92 | 6.29 | 9.52 | | -6.29 | 971.30 | - | 76 | 18 March 2019 | 127 | 73 | 50.00 | 50.00 | | 0.9250487234 | 46.25 | 5804.17 | 6.24 | 9.52 | | -6.24 | 971.30 | - | 77 | 19 March 2019 | 126 | 74 | 50.00 | 50.00 | | 0.9240619966 | 46.20 | 5760.36 | 6.20 | 9.52 | | -6.20 | 971.30 | - | 78 | 20 March 2019 | 125 | 75 | 50.00 | 50.00 | | 0.9230763224 | 46.15 | 5716.52 | 6.15 | 9.52 | | -6.15 | 971.30 | - | 79 | 21 March 2019 | 124 | 76 | 50.00 | 50.00 | | 0.9220916995 | 46.10 | 5672.62 | 6.10 | 9.52 | | -6.10 | 971.30 | - | 80 | 22 March 2019 | 123 | 77 | 50.00 | 50.00 | | 0.9211081269 | 46.06 | 5628.68 | 6.06 | 9.52 | | -6.06 | 971.30 | - | 81 | 23 March 2019 | 122 | 78 | 50.00 | 50.00 | | 0.9201256034 | 46.01 | 5584.69 | 6.01 | 9.52 | | -6.01 | 971.30 | - | 82 | 24 March 2019 | 121 | 79 | 50.00 | 50.00 | | 0.919144128 | 45.96 | 5540.65 | 5.96 | 9.52 | | -5.96 | 971.30 | - | 83 | 25 March 2019 | 120 | 80 | 50.00 | 50.00 | | 0.9181636995 | 45.91 | 5496.57 | 5.92 | 9.52 | | -5.92 | 971.30 | - | 84 | 26 March 2019 | 119 | 81 | 50.00 | 50.00 | | 0.9171843168 | 45.86 | 5452.44 | 5.87 | 9.52 | | -5.87 | 971.30 | - | 85 | 27 March 2019 | 118 | 82 | 50.00 | 50.00 | | 0.9162059788 | 45.81 | 5408.26 | 5.82 | 9.52 | | -5.82 | 971.30 | - | 86 | 28 March 2019 | 117 | 83 | 50.00 | 50.00 | | 0.9152286843 | 45.76 | 5364.03 | 5.78 | 9.52 | | -5.78 | 971.30 | - | 87 | 29 March 2019 | 116 | 84 | 50.00 | 50.00 | | 0.9142524323 | 45.71 | 5319.76 | 5.73 | 9.52 | | -5.73 | 971.30 | - | 88 | 30 March 2019 | 115 | 85 | 50.00 | 50.00 | | 0.9132772217 | 45.66 | 5275.44 | 5.68 | 9.52 | | -5.68 | 971.30 | - | 89 | 31 March 2019 | 114 | 86 | 50.00 | 50.00 | | 0.9123030513 | 45.62 | 5231.08 | 5.63 | 9.52 | | -5.63 | 971.30 | - | 90 | 01 April 2019 | 113 | 87 | 50.00 | 50.00 | | 0.91132992 | 45.57 | 5186.66 | 5.59 | 9.52 | | -5.59 | 971.30 | - | 91 | 02 April 2019 | 112 | 88 | 50.00 | 50.00 | | 0.9103578267 | 45.52 | 5142.20 | 5.54 | 9.52 | | -5.54 | 971.30 | - | 92 | 03 April 2019 | 111 | 89 | 50.00 | 50.00 | | 0.9093867703 | 45.47 | 5097.69 | 5.49 | 9.52 | | -5.49 | 971.30 | - | 93 | 04 April 2019 | 110 | 90 | 50.00 | 50.00 | | 0.9084167498 | 45.42 | 5053.13 | 5.44 | 9.52 | | -5.44 | 971.30 | - | 94 | 05 April 2019 | 109 | 91 | 50.00 | 50.00 | | 0.9074477639 | 45.37 | 5008.53 | 5.40 | 9.52 | | -5.40 | 971.30 | - | 95 | 06 April 2019 | 108 | 92 | 50.00 | 50.00 | | 0.9064798116 | 45.32 | 4963.88 | 5.35 | 9.52 | | -5.35 | 971.30 | - | 96 | 07 April 2019 | 107 | 93 | 50.00 | 50.00 | | 0.9055128918 | 45.28 | 4919.18 | 5.30 | 9.52 | | -5.30 | 971.30 | - | 97 | 08 April 2019 | 106 | 94 | 50.00 | 50.00 | | 0.9045470035 | 45.23 | 4874.43 | 5.25 | 9.52 | | -5.25 | 971.30 | - | 98 | 09 April 2019 | 105 | 95 | 50.00 | 50.00 | | 0.9035821453 | 45.18 | 4829.64 | 5.20 | 9.52 | | -5.20 | 971.30 | - | 99 | 10 April 2019 | 104 | 96 | 50.00 | 50.00 | | 0.9026183164 | 45.13 | 4784.79 | 5.16 | 9.52 | | -5.16 | 971.30 | - | 100 | 11 April 2019 | 103 | 97 | 50.00 | 50.00 | | 0.9016555156 | 45.08 | 4739.90 | 5.11 | 9.52 | | -5.11 | 971.30 | - | 101 | 12 April 2019 | 102 | 98 | 50.00 | 50.00 | | 0.9006937418 | 45.03 | 4694.96 | 5.06 | 9.52 | | -5.06 | 971.30 | - | 102 | 13 April 2019 | 101 | 99 | 50.00 | 50.00 | | 0.8997329939 | 44.99 | 4649.98 | 5.01 | 9.52 | | -5.01 | 971.30 | - | 103 | 14 April 2019 | 100 | 100 | 50.00 | 50.00 | | 0.8987732707 | 44.94 | 4604.94 | 4.97 | 9.52 | | -4.97 | 971.30 | - | 104 | 15 April 2019 | 99 | 101 | 50.00 | 50.00 | | 0.8978145713 | 44.89 | 4559.86 | 4.92 | 9.52 | | -4.92 | 971.30 | - | 105 | 16 April 2019 | 98 | 102 | 50.00 | 50.00 | | 0.8968568945 | 44.84 | 4514.73 | 4.87 | 9.52 | | -4.87 | 971.30 | - | 106 | 17 April 2019 | 97 | 103 | 50.00 | 50.00 | | 0.8959002393 | 44.80 | 4469.55 | 4.82 | 9.52 | | -4.82 | 971.30 | - | 107 | 18 April 2019 | 96 | 104 | 50.00 | 50.00 | | 0.8949446045 | 44.75 | 4424.32 | 4.77 | 9.52 | | -4.77 | 971.30 | - | 108 | 19 April 2019 | 95 | 105 | 50.00 | 50.00 | | 0.893989989 | 44.70 | 4379.05 | 4.72 | 9.52 | | -4.72 | 971.30 | - | 109 | 20 April 2019 | 94 | 106 | 50.00 | 50.00 | | 0.8930363918 | 44.65 | 4333.72 | 4.68 | 9.52 | | -4.68 | 971.30 | - | 110 | 21 April 2019 | 93 | 107 | 50.00 | 50.00 | | 0.8920838118 | 44.60 | 4288.35 | 4.63 | 9.52 | | -4.63 | 971.30 | - | 111 | 22 April 2019 | 92 | 108 | 50.00 | 50.00 | | 0.8911322479 | 44.56 | 4242.93 | 4.58 | 9.52 | | -4.58 | 971.30 | - | 112 | 23 April 2019 | 91 | 109 | 50.00 | 50.00 | | 0.890181699 | 44.51 | 4197.46 | 4.53 | 9.52 | | -4.53 | 971.30 | - | 113 | 24 April 2019 | 90 | 110 | 50.00 | 50.00 | | 0.889232164 | 44.46 | 4151.94 | 4.48 | 9.52 | | -4.48 | 971.30 | - | 114 | 25 April 2019 | 89 | 111 | 50.00 | 50.00 | | 0.8882836418 | 44.41 | 4106.38 | 4.43 | 9.52 | | -4.43 | 971.30 | - | 115 | 26 April 2019 | 88 | 112 | 50.00 | 50.00 | | 0.8873361314 | 44.37 | 4060.76 | 4.38 | 9.52 | | -4.38 | 971.30 | - | 116 | 27 April 2019 | 87 | 113 | 50.00 | 50.00 | | 0.8863896318 | 44.32 | 4015.10 | 4.34 | 9.52 | | -4.34 | 971.30 | - | 117 | 28 April 2019 | 86 | 114 | 50.00 | 50.00 | | 0.8854441417 | 44.27 | 3969.38 | 4.29 | 9.52 | | -4.29 | 971.30 | - | 118 | 29 April 2019 | 85 | 115 | 50.00 | 50.00 | | 0.8844996601 | 44.22 | 3923.62 | 4.24 | 9.52 | | -4.24 | 971.30 | - | 119 | 30 April 2019 | 84 | 116 | 50.00 | 50.00 | | 0.883556186 | 44.18 | 3877.81 | 4.19 | 9.52 | | -4.19 | 971.30 | - | 120 | 01 May 2019 | 83 | 117 | 50.00 | 50.00 | | 0.8826137183 | 44.13 | 3831.95 | 4.14 | 9.52 | | -4.14 | 971.30 | - | 121 | 02 May 2019 | 82 | 118 | 50.00 | 50.00 | | 0.8816722559 | 44.08 | 3786.04 | 4.09 | 9.52 | | -4.09 | 971.30 | - | 122 | 03 May 2019 | 81 | 119 | 50.00 | 50.00 | | 0.8807317977 | 44.04 | 3740.09 | 4.04 | 9.52 | | -4.04 | 971.30 | - | 123 | 04 May 2019 | 80 | 120 | 50.00 | 50.00 | | 0.8797923427 | 43.99 | 3694.08 | 3.99 | 9.52 | | -3.99 | 971.30 | - | 124 | 05 May 2019 | 79 | 121 | 50.00 | 50.00 | | 0.8788538898 | 43.94 | 3648.03 | 3.94 | 9.52 | | -3.94 | 971.30 | - | 125 | 06 May 2019 | 78 | 122 | 50.00 | 50.00 | | 0.8779164379 | 43.90 | 3601.92 | 3.90 | 9.52 | | -3.90 | 971.30 | - | 126 | 07 May 2019 | 77 | 123 | 50.00 | 50.00 | | 0.876979986 | 43.85 | 3555.77 | 3.85 | 9.52 | | -3.85 | 971.30 | - | 127 | 08 May 2019 | 76 | 124 | 50.00 | 50.00 | | 0.8760445329 | 43.80 | 3509.56 | 3.80 | 9.52 | | -3.80 | 971.30 | - | 128 | 09 May 2019 | 75 | 125 | 50.00 | 50.00 | | 0.8751100777 | 43.76 | 3463.31 | 3.75 | 9.52 | | -3.75 | 971.30 | - | 129 | 10 May 2019 | 74 | 126 | 50.00 | 50.00 | | 0.8741766193 | 43.71 | 3417.01 | 3.70 | 9.52 | | -3.70 | 971.30 | - | 130 | 11 May 2019 | 73 | 127 | 50.00 | 50.00 | | 0.8732441565 | 43.66 | 3370.66 | 3.65 | 9.52 | | -3.65 | 971.30 | - | 131 | 12 May 2019 | 72 | 128 | 50.00 | 50.00 | | 0.8723126884 | 43.62 | 3324.26 | 3.60 | 9.52 | | -3.60 | 971.30 | - | 132 | 13 May 2019 | 71 | 129 | 50.00 | 50.00 | | 0.8713822138 | 43.57 | 3277.81 | 3.55 | 9.52 | | -3.55 | 971.30 | - | 133 | 14 May 2019 | 70 | 130 | 50.00 | 50.00 | | 0.8704527318 | 43.52 | 3231.31 | 3.50 | 9.52 | | -3.50 | 971.30 | - | 134 | 15 May 2019 | 69 | 131 | 50.00 | 50.00 | | 0.8695242412 | 43.48 | 3184.76 | 3.45 | 9.52 | | -3.45 | 971.30 | - | 135 | 16 May 2019 | 68 | 132 | 50.00 | 50.00 | | 0.868596741 | 43.43 | 3138.16 | 3.40 | 9.52 | | -3.40 | 971.30 | - | 136 | 17 May 2019 | 67 | 133 | 50.00 | 50.00 | | 0.8676702302 | 43.38 | 3091.51 | 3.35 | 9.52 | | -3.35 | 971.30 | - | 137 | 18 May 2019 | 66 | 134 | 50.00 | 50.00 | | 0.8667447076 | 43.34 | 3044.81 | 3.30 | 9.52 | | -3.30 | 971.30 | - | 138 | 19 May 2019 | 65 | 135 | 50.00 | 50.00 | | 0.8658201723 | 43.29 | 2998.06 | 3.25 | 9.52 | | -3.25 | 971.30 | - | 139 | 20 May 2019 | 64 | 136 | 50.00 | 50.00 | | 0.8648966231 | 43.24 | 2951.26 | 3.20 | 9.52 | | -3.20 | 971.30 | - | 140 | 21 May 2019 | 63 | 137 | 50.00 | 50.00 | | 0.8639740591 | 43.20 | 2904.42 | 3.15 | 9.52 | | -3.15 | 971.30 | - | 141 | 22 May 2019 | 62 | 138 | 50.00 | 50.00 | | 0.8630524792 | 43.15 | 2857.52 | 3.10 | 9.52 | | -3.10 | 971.30 | - | 142 | 23 May 2019 | 61 | 139 | 50.00 | 50.00 | | 0.8621318823 | 43.11 | 2810.57 | 3.05 | 9.52 | | -3.05 | 971.30 | - | 143 | 24 May 2019 | 60 | 140 | 50.00 | 50.00 | | 0.8612122673 | 43.06 | 2763.57 | 3.00 | 9.52 | | -3.00 | 971.30 | - | 144 | 25 May 2019 | 59 | 141 | 50.00 | 50.00 | | 0.8602936333 | 43.01 | 2716.52 | 2.95 | 9.52 | | -2.95 | 971.30 | - | 145 | 26 May 2019 | 58 | 142 | 50.00 | 50.00 | | 0.8593759792 | 42.97 | 2669.42 | 2.90 | 9.52 | | -2.90 | 971.30 | - | 146 | 27 May 2019 | 57 | 143 | 50.00 | 50.00 | | 0.8584593039 | 42.92 | 2622.27 | 2.85 | 9.52 | | -2.85 | 971.30 | - | 147 | 28 May 2019 | 56 | 144 | 50.00 | 50.00 | | 0.8575436064 | 42.88 | 2575.07 | 2.80 | 9.52 | | -2.80 | 971.30 | - | 148 | 29 May 2019 | 55 | 145 | 50.00 | 50.00 | | 0.8566288857 | 42.83 | 2527.82 | 2.75 | 9.52 | | -2.75 | 971.30 | - | 149 | 30 May 2019 | 54 | 146 | 50.00 | 50.00 | | 0.8557151407 | 42.79 | 2480.52 | 2.70 | 9.52 | | -2.70 | 971.30 | - | 150 | 31 May 2019 | 53 | 147 | 50.00 | 50.00 | | 0.8548023703 | 42.74 | 2433.17 | 2.65 | 9.52 | | -2.65 | 971.30 | - | 151 | 01 June 2019 | 52 | 148 | 50.00 | 50.00 | | 0.8538905736 | 42.69 | 2385.77 | 2.60 | 9.52 | | -2.60 | 971.30 | - | 152 | 02 June 2019 | 51 | 149 | 50.00 | 50.00 | | 0.8529797495 | 42.65 | 2338.31 | 2.55 | 9.52 | | -2.55 | 971.30 | - | 153 | 03 June 2019 | 50 | 150 | 50.00 | 50.00 | | 0.8520698969 | 42.60 | 2290.81 | 2.50 | 9.52 | | -2.50 | 971.30 | - | 154 | 04 June 2019 | 49 | 151 | 50.00 | 50.00 | | 0.8511610148 | 42.56 | 2243.26 | 2.45 | 9.52 | | -2.45 | 971.30 | - | 155 | 05 June 2019 | 48 | 152 | 50.00 | 50.00 | | 0.8502531022 | 42.51 | 2195.65 | 2.40 | 9.52 | | -2.40 | 971.30 | - | 156 | 06 June 2019 | 47 | 153 | 50.00 | 50.00 | | 0.8493461581 | 42.47 | 2148.00 | 2.34 | 9.52 | | -2.34 | 971.30 | - | 157 | 07 June 2019 | 46 | 154 | 50.00 | 50.00 | | 0.8484401814 | 42.42 | 2100.29 | 2.29 | 9.52 | | -2.29 | 971.30 | - | 158 | 08 June 2019 | 45 | 155 | 50.00 | 50.00 | | 0.8475351711 | 42.38 | 2052.53 | 2.24 | 9.52 | | -2.24 | 971.30 | - | 159 | 09 June 2019 | 44 | 156 | 50.00 | 50.00 | | 0.8466311261 | 42.33 | 2004.73 | 2.19 | 9.52 | | -2.19 | 971.30 | - | 160 | 10 June 2019 | 43 | 157 | 50.00 | 50.00 | | 0.8457280454 | 42.29 | 1956.87 | 2.14 | 9.52 | | -2.14 | 971.30 | - | 161 | 11 June 2019 | 42 | 158 | 50.00 | 50.00 | | 0.844825928 | 42.24 | 1908.96 | 2.09 | 9.52 | | -2.09 | 971.30 | - | 162 | 12 June 2019 | 41 | 159 | 50.00 | 50.00 | | 0.8439247729 | 42.20 | 1860.99 | 2.04 | 9.52 | | -2.04 | 971.30 | - | 163 | 13 June 2019 | 40 | 160 | 50.00 | 50.00 | | 0.8430245791 | 42.15 | 1812.98 | 1.99 | 9.52 | | -1.99 | 971.30 | - | 164 | 14 June 2019 | 39 | 161 | 50.00 | 50.00 | | 0.8421253454 | 42.11 | 1764.92 | 1.94 | 9.52 | | -1.94 | 971.30 | - | 165 | 15 June 2019 | 38 | 162 | 50.00 | 50.00 | | 0.841227071 | 42.06 | 1716.80 | 1.88 | 9.52 | | -1.88 | 971.30 | - | 166 | 16 June 2019 | 37 | 163 | 50.00 | 50.00 | | 0.8403297547 | 42.02 | 1668.64 | 1.83 | 9.52 | | -1.83 | 971.30 | - | 167 | 17 June 2019 | 36 | 164 | 50.00 | 50.00 | | 0.8394333956 | 41.97 | 1620.42 | 1.78 | 9.52 | | -1.78 | 971.30 | - | 168 | 18 June 2019 | 35 | 165 | 50.00 | 50.00 | | 0.8385379925 | 41.93 | 1572.15 | 1.73 | 9.52 | | -1.73 | 971.30 | - | 169 | 19 June 2019 | 34 | 166 | 50.00 | 50.00 | | 0.8376435446 | 41.88 | 1523.83 | 1.68 | 9.52 | | -1.68 | 971.30 | - | 170 | 20 June 2019 | 33 | 167 | 50.00 | 50.00 | | 0.8367500508 | 41.84 | 1475.45 | 1.63 | 9.52 | | -1.63 | 971.30 | - | 171 | 21 June 2019 | 32 | 168 | 50.00 | 50.00 | | 0.83585751 | 41.79 | 1427.03 | 1.58 | 9.52 | | -1.58 | 971.30 | - | 172 | 22 June 2019 | 31 | 169 | 50.00 | 50.00 | | 0.8349659213 | 41.75 | 1378.55 | 1.52 | 9.52 | | -1.52 | 971.30 | - | 173 | 23 June 2019 | 30 | 170 | 50.00 | 50.00 | | 0.8340752837 | 41.70 | 1330.02 | 1.47 | 9.52 | | -1.47 | 971.30 | - | 174 | 24 June 2019 | 29 | 171 | 50.00 | 50.00 | | 0.833185596 | 41.66 | 1281.45 | 1.42 | 9.52 | | -1.42 | 971.30 | - | 175 | 25 June 2019 | 28 | 172 | 50.00 | 50.00 | | 0.8322968574 | 41.61 | 1232.81 | 1.37 | 9.52 | | -1.37 | 971.30 | - | 176 | 26 June 2019 | 27 | 173 | 50.00 | 50.00 | | 0.8314090667 | 41.57 | 1184.13 | 1.32 | 9.52 | | -1.32 | 971.30 | - | 177 | 27 June 2019 | 26 | 174 | 50.00 | 50.00 | | 0.8305222231 | 41.53 | 1135.39 | 1.26 | 9.52 | | -1.26 | 971.30 | - | 178 | 28 June 2019 | 25 | 175 | 50.00 | 50.00 | | 0.8296363254 | 41.48 | 1086.61 | 1.21 | 9.52 | | -1.21 | 971.30 | - | 179 | 29 June 2019 | 24 | 176 | 50.00 | 50.00 | | 0.8287513727 | 41.44 | 1037.77 | 1.16 | 9.52 | | -1.16 | 971.30 | - | 180 | 30 June 2019 | 23 | 177 | 50.00 | 50.00 | | 0.8278673639 | 41.39 | 988.88 | 1.11 | 9.52 | | -1.11 | 971.30 | - | 181 | 01 July 2019 | 22 | 178 | 50.00 | 50.00 | | 0.8269842981 | 41.35 | 939.93 | 1.06 | 9.52 | | -1.06 | 971.30 | - | 182 | 02 July 2019 | 21 | 179 | 50.00 | 50.00 | | 0.8261021742 | 41.31 | 890.93 | 1.00 | 9.52 | | -1.00 | 971.30 | - | 183 | 03 July 2019 | 20 | 180 | 50.00 | 50.00 | | 0.8252209913 | 41.26 | 841.89 | 0.95 | 9.52 | | -0.95 | 971.30 | - | 184 | 04 July 2019 | 19 | 181 | 50.00 | 50.00 | | 0.8243407483 | 41.22 | 792.79 | 0.90 | 9.52 | | -0.90 | 971.30 | - | 185 | 05 July 2019 | 18 | 182 | 50.00 | 50.00 | | 0.8234614442 | 41.17 | 743.63 | 0.85 | 9.52 | | -0.85 | 971.30 | - | 186 | 06 July 2019 | 17 | 183 | 50.00 | 50.00 | | 0.8225830781 | 41.13 | 694.43 | 0.79 | 9.52 | | -0.79 | 971.30 | - | 187 | 07 July 2019 | 16 | 184 | 50.00 | 50.00 | | 0.8217056489 | 41.09 | 645.17 | 0.74 | 9.52 | | -0.74 | 971.30 | - | 188 | 08 July 2019 | 15 | 185 | 50.00 | 50.00 | | 0.8208291556 | 41.04 | 595.86 | 0.69 | 9.52 | | -0.69 | 971.30 | - | 189 | 09 July 2019 | 14 | 186 | 50.00 | 50.00 | | 0.8199535973 | 41.00 | 546.49 | 0.64 | 9.52 | | -0.64 | 971.30 | - | 190 | 10 July 2019 | 13 | 187 | 50.00 | 50.00 | | 0.8190789729 | 40.95 | 497.08 | 0.58 | 9.52 | | -0.58 | 971.30 | - | 191 | 11 July 2019 | 12 | 188 | 50.00 | 50.00 | | 0.8182052815 | 40.91 | 447.61 | 0.53 | 9.52 | | -0.53 | 971.30 | - | 192 | 12 July 2019 | 11 | 189 | 50.00 | 50.00 | | 0.8173325219 | 40.87 | 398.08 | 0.48 | 9.52 | | -0.48 | 971.30 | - | 193 | 13 July 2019 | 10 | 190 | 50.00 | 50.00 | | 0.8164606934 | 40.82 | 348.51 | 0.43 | 9.52 | | -0.43 | 971.30 | - | 194 | 14 July 2019 | 9 | 191 | 50.00 | 50.00 | | 0.8155897948 | 40.78 | 298.88 | 0.37 | 9.52 | | -0.37 | 971.30 | - | 195 | 15 July 2019 | 8 | 192 | 50.00 | 50.00 | | 0.8147198252 | 40.74 | 249.20 | 0.32 | 9.52 | | -0.32 | 971.30 | - | 196 | 16 July 2019 | 7 | 193 | 50.00 | 50.00 | | 0.8138507835 | 40.69 | 199.47 | 0.27 | 9.52 | | -0.27 | 971.30 | - | 197 | 17 July 2019 | 6 | 194 | 50.00 | 50.00 | | 0.8129826688 | 40.65 | 149.68 | 0.21 | 9.52 | | -0.21 | 971.30 | - | 198 | 18 July 2019 | 5 | 195 | 50.00 | 50.00 | | 0.8121154802 | 40.61 | 99.84 | 0.16 | 9.52 | | -0.16 | 971.30 | - | 199 | 19 July 2019 | 4 | 196 | 50.00 | 50.00 | | 0.8112492165 | 40.56 | 49.95 | 0.11 | 9.52 | | -0.11 | 971.30 | - | 200 | 20 July 2019 | 3 | 197 | 50.00 | 50.00 | | 0.8103838768 | 40.52 | 0.00 | 0.05 | 9.52 | | -0.05 | 971.30 | + | paymentNo | paymentDate | expectedPaymentAmount | actualPaymentAmount | discountFactor | npvValue | balance | expectedAmortizationAmount | actualAmortizationAmount | incomeModification | deferredBalance | + | 0 | 01 January 2019 | -9000.00 | | 1 | -9000.00 | 9000.00 | | | | 1000.00 | + | 1 | 02 January 2019 | 50.00 | 50.00 | 1 | 50.00 | 8959.61 | 9.61 | 9.61 | 0.00 | 990.39 | + | 2 | 03 January 2019 | 50.00 | 50.00 | 1 | 50.00 | 8919.18 | 9.57 | 9.57 | 0.00 | 980.82 | + | 3 | 04 January 2019 | 50.00 | 50.00 | 1 | 50.00 | 8878.70 | 9.52 | 9.52 | 0.00 | 971.30 | + | 4 | 05 January 2019 | 50.00 | | 0.9989333245 | 49.95 | 8838.18 | 9.48 | | | 971.30 | + | 5 | 06 January 2019 | 50.00 | | 0.9978677868 | 49.89 | 8797.62 | 9.44 | | | 971.30 | + | 6 | 07 January 2019 | 50.00 | | 0.9968033857 | 49.84 | 8757.01 | 9.39 | | | 971.30 | + | 7 | 08 January 2019 | 50.00 | | 0.99574012 | 49.79 | 8716.36 | 9.35 | | | 971.30 | + | 8 | 09 January 2019 | 50.00 | | 0.9946779885 | 49.73 | 8675.67 | 9.31 | | | 971.30 | + | 9 | 10 January 2019 | 50.00 | | 0.9936169898 | 49.68 | 8634.94 | 9.26 | | | 971.30 | + | 10 | 11 January 2019 | 50.00 | | 0.992557123 | 49.63 | 8594.16 | 9.22 | | | 971.30 | + | 11 | 12 January 2019 | 50.00 | | 0.9914983866 | 49.57 | 8553.33 | 9.18 | | | 971.30 | + | 12 | 13 January 2019 | 50.00 | | 0.9904407796 | 49.52 | 8512.47 | 9.13 | | | 971.30 | + | 13 | 14 January 2019 | 50.00 | | 0.9893843007 | 49.47 | 8471.56 | 9.09 | | | 971.30 | + | 14 | 15 January 2019 | 50.00 | | 0.9883289487 | 49.42 | 8430.60 | 9.05 | | | 971.30 | + | 15 | 16 January 2019 | 50.00 | | 0.9872747225 | 49.36 | 8389.61 | 9.00 | | | 971.30 | + | 16 | 17 January 2019 | 50.00 | | 0.9862216208 | 49.31 | 8348.56 | 8.96 | | | 971.30 | + | 17 | 18 January 2019 | 50.00 | | 0.9851696423 | 49.26 | 8307.48 | 8.91 | | | 971.30 | + | 18 | 19 January 2019 | 50.00 | | 0.984118786 | 49.21 | 8266.35 | 8.87 | | | 971.30 | + | 19 | 20 January 2019 | 50.00 | | 0.9830690507 | 49.15 | 8225.18 | 8.83 | | | 971.30 | + | 20 | 21 January 2019 | 50.00 | | 0.982020435 | 49.10 | 8183.96 | 8.78 | | | 971.30 | + | 21 | 22 January 2019 | 50.00 | | 0.9809729379 | 49.05 | 8142.70 | 8.74 | | | 971.30 | + | 22 | 23 January 2019 | 50.00 | | 0.9799265581 | 49.00 | 8101.39 | 8.69 | | | 971.30 | + | 23 | 24 January 2019 | 50.00 | | 0.9788812945 | 48.94 | 8060.04 | 8.65 | | | 971.30 | + | 24 | 25 January 2019 | 50.00 | | 0.9778371458 | 48.89 | 8018.65 | 8.61 | | | 971.30 | + | 25 | 26 January 2019 | 50.00 | | 0.9767941109 | 48.84 | 7977.21 | 8.56 | | | 971.30 | + | 26 | 27 January 2019 | 50.00 | | 0.9757521886 | 48.79 | 7935.73 | 8.52 | | | 971.30 | + | 27 | 28 January 2019 | 50.00 | | 0.9747113777 | 48.74 | 7894.21 | 8.47 | | | 971.30 | + | 28 | 29 January 2019 | 50.00 | | 0.9736716769498249835 | 48.68 | 7852.63 | 8.43 | | | 971.30 | + | 29 | 30 January 2019 | 50.00 | | 0.9726330853 | 48.63 | 7811.02 | 8.39 | | | 971.30 | + | 30 | 31 January 2019 | 50.00 | | 0.9715956014 | 48.58 | 7769.36 | 8.34 | | | 971.30 | + | 31 | 01 February 2019 | 50.00 | | 0.9705592242 | 48.53 | 7727.66 | 8.30 | | | 971.30 | + | 32 | 02 February 2019 | 50.00 | | 0.9695239525 | 48.48 | 7685.91 | 8.25 | | | 971.30 | + | 33 | 03 February 2019 | 50.00 | | 0.968489785 | 48.42 | 7644.12 | 8.21 | | | 971.30 | + | 34 | 04 February 2019 | 50.00 | | 0.9674567207 | 48.37 | 7602.28 | 8.16 | | | 971.30 | + | 35 | 05 February 2019 | 50.00 | | 0.9664247584 | 48.32 | 7560.40 | 8.12 | | | 971.30 | + | 36 | 06 February 2019 | 50.00 | | 0.9653938968 | 48.27 | 7518.47 | 8.07 | | | 971.30 | + | 37 | 07 February 2019 | 50.00 | | 0.9643641348 | 48.22 | 7476.50 | 8.03 | | | 971.30 | + | 38 | 08 February 2019 | 50.00 | | 0.9633354712 | 48.17 | 7434.48 | 7.98 | | | 971.30 | + | 39 | 09 February 2019 | 50.00 | | 0.9623079049 | 48.12 | 7392.42 | 7.94 | | | 971.30 | + | 40 | 10 February 2019 | 50.00 | | 0.9612814347 | 48.06 | 7350.31 | 7.89 | | | 971.30 | + | 41 | 11 February 2019 | 50.00 | | 0.9602560593 | 48.01 | 7308.16 | 7.85 | | | 971.30 | + | 42 | 12 February 2019 | 50.00 | | 0.9592317777 | 47.96 | 7265.97 | 7.80 | | | 971.30 | + | 43 | 13 February 2019 | 50.00 | | 0.9582085887 | 47.91 | 7223.72 | 7.76 | | | 971.30 | + | 44 | 14 February 2019 | 50.00 | | 0.9571864911 | 47.86 | 7181.44 | 7.71 | | | 971.30 | + | 45 | 15 February 2019 | 50.00 | | 0.9561654838 | 47.81 | 7139.11 | 7.67 | | | 971.30 | + | 46 | 16 February 2019 | 50.00 | | 0.9551455655 | 47.76 | 7096.73 | 7.62 | | | 971.30 | + | 47 | 17 February 2019 | 50.00 | | 0.9541267351 | 47.71 | 7054.31 | 7.58 | | | 971.30 | + | 48 | 18 February 2019 | 50.00 | | 0.9531089916 | 47.66 | 7011.84 | 7.53 | | | 971.30 | + | 49 | 19 February 2019 | 50.00 | | 0.9520923336 | 47.60 | 6969.33 | 7.49 | | | 971.30 | + | 50 | 20 February 2019 | 50.00 | | 0.95107676 | 47.55 | 6926.77 | 7.44 | | | 971.30 | + | 51 | 21 February 2019 | 50.00 | | 0.9500622698 | 47.50 | 6884.17 | 7.40 | | | 971.30 | + | 52 | 22 February 2019 | 50.00 | | 0.9490488616 | 47.45 | 6841.52 | 7.35 | | | 971.30 | + | 53 | 23 February 2019 | 50.00 | | 0.9480365345 | 47.40 | 6798.82 | 7.31 | | | 971.30 | + | 54 | 24 February 2019 | 50.00 | | 0.9470252872 | 47.35 | 6756.08 | 7.26 | | | 971.30 | + | 55 | 25 February 2019 | 50.00 | | 0.9460151185 | 47.30 | 6713.30 | 7.21 | | | 971.30 | + | 56 | 26 February 2019 | 50.00 | | 0.9450060274 | 47.25 | 6670.47 | 7.17 | | | 971.30 | + | 57 | 27 February 2019 | 50.00 | | 0.9439980126 | 47.20 | 6627.59 | 7.12 | | | 971.30 | + | 58 | 28 February 2019 | 50.00 | | 0.9429910731 | 47.15 | 6584.67 | 7.08 | | | 971.30 | + | 59 | 01 March 2019 | 50.00 | | 0.9419852077 | 47.10 | 6541.70 | 7.03 | | | 971.30 | + | 60 | 02 March 2019 | 50.00 | | 0.9409804151 | 47.05 | 6498.68 | 6.99 | | | 971.30 | + | 61 | 03 March 2019 | 50.00 | | 0.9399766944 | 47.00 | 6455.62 | 6.94 | | | 971.30 | + | 62 | 04 March 2019 | 50.00 | | 0.9389740443 | 46.95 | 6412.51 | 6.89 | | | 971.30 | + | 63 | 05 March 2019 | 50.00 | | 0.9379724637 | 46.90 | 6369.36 | 6.85 | | | 971.30 | + | 64 | 06 March 2019 | 50.00 | | 0.9369719515 | 46.85 | 6326.16 | 6.80 | | | 971.30 | + | 65 | 07 March 2019 | 50.00 | | 0.9359725065 | 46.80 | 6282.92 | 6.76 | | | 971.30 | + | 66 | 08 March 2019 | 50.00 | | 0.9349741276 | 46.75 | 6239.63 | 6.71 | | | 971.30 | + | 67 | 09 March 2019 | 50.00 | | 0.9339768136 | 46.70 | 6196.29 | 6.66 | | | 971.30 | + | 68 | 10 March 2019 | 50.00 | | 0.9329805635 | 46.65 | 6152.91 | 6.62 | | | 971.30 | + | 69 | 11 March 2019 | 50.00 | | 0.931985376 | 46.60 | 6109.48 | 6.57 | | | 971.30 | + | 70 | 12 March 2019 | 50.00 | | 0.93099125 | 46.55 | 6066.00 | 6.52 | | | 971.30 | + | 71 | 13 March 2019 | 50.00 | | 0.9299981845 | 46.50 | 6022.48 | 6.48 | | | 971.30 | + | 72 | 14 March 2019 | 50.00 | | 0.9290061782 | 46.45 | 5978.91 | 6.43 | | | 971.30 | + | 73 | 15 March 2019 | 50.00 | | 0.9280152301 | 46.40 | 5935.29 | 6.38 | | | 971.30 | + | 74 | 16 March 2019 | 50.00 | | 0.927025339 | 46.35 | 5891.63 | 6.34 | | | 971.30 | + | 75 | 17 March 2019 | 50.00 | | 0.9260365038 | 46.30 | 5847.92 | 6.29 | | | 971.30 | + | 76 | 18 March 2019 | 50.00 | | 0.9250487234 | 46.25 | 5804.17 | 6.24 | | | 971.30 | + | 77 | 19 March 2019 | 50.00 | | 0.9240619966 | 46.20 | 5760.36 | 6.20 | | | 971.30 | + | 78 | 20 March 2019 | 50.00 | | 0.9230763224 | 46.15 | 5716.52 | 6.15 | | | 971.30 | + | 79 | 21 March 2019 | 50.00 | | 0.9220916995 | 46.10 | 5672.62 | 6.10 | | | 971.30 | + | 80 | 22 March 2019 | 50.00 | | 0.9211081269 | 46.06 | 5628.68 | 6.06 | | | 971.30 | + | 81 | 23 March 2019 | 50.00 | | 0.9201256034 | 46.01 | 5584.69 | 6.01 | | | 971.30 | + | 82 | 24 March 2019 | 50.00 | | 0.919144128 | 45.96 | 5540.65 | 5.96 | | | 971.30 | + | 83 | 25 March 2019 | 50.00 | | 0.9181636995 | 45.91 | 5496.57 | 5.92 | | | 971.30 | + | 84 | 26 March 2019 | 50.00 | | 0.9171843168 | 45.86 | 5452.44 | 5.87 | | | 971.30 | + | 85 | 27 March 2019 | 50.00 | | 0.9162059788 | 45.81 | 5408.26 | 5.82 | | | 971.30 | + | 86 | 28 March 2019 | 50.00 | | 0.9152286843 | 45.76 | 5364.03 | 5.78 | | | 971.30 | + | 87 | 29 March 2019 | 50.00 | | 0.9142524323 | 45.71 | 5319.76 | 5.73 | | | 971.30 | + | 88 | 30 March 2019 | 50.00 | | 0.9132772217 | 45.66 | 5275.44 | 5.68 | | | 971.30 | + | 89 | 31 March 2019 | 50.00 | | 0.9123030513 | 45.62 | 5231.08 | 5.63 | | | 971.30 | + | 90 | 01 April 2019 | 50.00 | | 0.91132992 | 45.57 | 5186.66 | 5.59 | | | 971.30 | + | 91 | 02 April 2019 | 50.00 | | 0.9103578267 | 45.52 | 5142.20 | 5.54 | | | 971.30 | + | 92 | 03 April 2019 | 50.00 | | 0.9093867703 | 45.47 | 5097.69 | 5.49 | | | 971.30 | + | 93 | 04 April 2019 | 50.00 | | 0.9084167498 | 45.42 | 5053.13 | 5.44 | | | 971.30 | + | 94 | 05 April 2019 | 50.00 | | 0.9074477639 | 45.37 | 5008.53 | 5.40 | | | 971.30 | + | 95 | 06 April 2019 | 50.00 | | 0.9064798116 | 45.32 | 4963.88 | 5.35 | | | 971.30 | + | 96 | 07 April 2019 | 50.00 | | 0.9055128918 | 45.28 | 4919.18 | 5.30 | | | 971.30 | + | 97 | 08 April 2019 | 50.00 | | 0.9045470035 | 45.23 | 4874.43 | 5.25 | | | 971.30 | + | 98 | 09 April 2019 | 50.00 | | 0.9035821453 | 45.18 | 4829.64 | 5.20 | | | 971.30 | + | 99 | 10 April 2019 | 50.00 | | 0.9026183164 | 45.13 | 4784.79 | 5.16 | | | 971.30 | + | 100 | 11 April 2019 | 50.00 | | 0.9016555156 | 45.08 | 4739.90 | 5.11 | | | 971.30 | + | 101 | 12 April 2019 | 50.00 | | 0.9006937418 | 45.03 | 4694.96 | 5.06 | | | 971.30 | + | 102 | 13 April 2019 | 50.00 | | 0.8997329939 | 44.99 | 4649.98 | 5.01 | | | 971.30 | + | 103 | 14 April 2019 | 50.00 | | 0.8987732707 | 44.94 | 4604.94 | 4.97 | | | 971.30 | + | 104 | 15 April 2019 | 50.00 | | 0.8978145713 | 44.89 | 4559.86 | 4.92 | | | 971.30 | + | 105 | 16 April 2019 | 50.00 | | 0.8968568945 | 44.84 | 4514.73 | 4.87 | | | 971.30 | + | 106 | 17 April 2019 | 50.00 | | 0.8959002393 | 44.80 | 4469.55 | 4.82 | | | 971.30 | + | 107 | 18 April 2019 | 50.00 | | 0.8949446045 | 44.75 | 4424.32 | 4.77 | | | 971.30 | + | 108 | 19 April 2019 | 50.00 | | 0.893989989 | 44.70 | 4379.05 | 4.72 | | | 971.30 | + | 109 | 20 April 2019 | 50.00 | | 0.8930363918 | 44.65 | 4333.72 | 4.68 | | | 971.30 | + | 110 | 21 April 2019 | 50.00 | | 0.8920838118 | 44.60 | 4288.35 | 4.63 | | | 971.30 | + | 111 | 22 April 2019 | 50.00 | | 0.8911322479 | 44.56 | 4242.93 | 4.58 | | | 971.30 | + | 112 | 23 April 2019 | 50.00 | | 0.890181699 | 44.51 | 4197.46 | 4.53 | | | 971.30 | + | 113 | 24 April 2019 | 50.00 | | 0.889232164 | 44.46 | 4151.94 | 4.48 | | | 971.30 | + | 114 | 25 April 2019 | 50.00 | | 0.8882836418 | 44.41 | 4106.38 | 4.43 | | | 971.30 | + | 115 | 26 April 2019 | 50.00 | | 0.8873361314 | 44.37 | 4060.76 | 4.38 | | | 971.30 | + | 116 | 27 April 2019 | 50.00 | | 0.8863896318 | 44.32 | 4015.10 | 4.34 | | | 971.30 | + | 117 | 28 April 2019 | 50.00 | | 0.8854441417 | 44.27 | 3969.38 | 4.29 | | | 971.30 | + | 118 | 29 April 2019 | 50.00 | | 0.8844996601 | 44.22 | 3923.62 | 4.24 | | | 971.30 | + | 119 | 30 April 2019 | 50.00 | | 0.883556186 | 44.18 | 3877.81 | 4.19 | | | 971.30 | + | 120 | 01 May 2019 | 50.00 | | 0.8826137183 | 44.13 | 3831.95 | 4.14 | | | 971.30 | + | 121 | 02 May 2019 | 50.00 | | 0.8816722559 | 44.08 | 3786.04 | 4.09 | | | 971.30 | + | 122 | 03 May 2019 | 50.00 | | 0.8807317977 | 44.04 | 3740.09 | 4.04 | | | 971.30 | + | 123 | 04 May 2019 | 50.00 | | 0.8797923427 | 43.99 | 3694.08 | 3.99 | | | 971.30 | + | 124 | 05 May 2019 | 50.00 | | 0.8788538898 | 43.94 | 3648.03 | 3.94 | | | 971.30 | + | 125 | 06 May 2019 | 50.00 | | 0.8779164379 | 43.90 | 3601.92 | 3.90 | | | 971.30 | + | 126 | 07 May 2019 | 50.00 | | 0.876979986 | 43.85 | 3555.77 | 3.85 | | | 971.30 | + | 127 | 08 May 2019 | 50.00 | | 0.8760445329 | 43.80 | 3509.56 | 3.80 | | | 971.30 | + | 128 | 09 May 2019 | 50.00 | | 0.8751100777 | 43.76 | 3463.31 | 3.75 | | | 971.30 | + | 129 | 10 May 2019 | 50.00 | | 0.8741766193 | 43.71 | 3417.01 | 3.70 | | | 971.30 | + | 130 | 11 May 2019 | 50.00 | | 0.8732441565 | 43.66 | 3370.66 | 3.65 | | | 971.30 | + | 131 | 12 May 2019 | 50.00 | | 0.8723126884 | 43.62 | 3324.26 | 3.60 | | | 971.30 | + | 132 | 13 May 2019 | 50.00 | | 0.8713822138 | 43.57 | 3277.81 | 3.55 | | | 971.30 | + | 133 | 14 May 2019 | 50.00 | | 0.8704527318 | 43.52 | 3231.31 | 3.50 | | | 971.30 | + | 134 | 15 May 2019 | 50.00 | | 0.8695242412 | 43.48 | 3184.76 | 3.45 | | | 971.30 | + | 135 | 16 May 2019 | 50.00 | | 0.868596741 | 43.43 | 3138.16 | 3.40 | | | 971.30 | + | 136 | 17 May 2019 | 50.00 | | 0.8676702302 | 43.38 | 3091.51 | 3.35 | | | 971.30 | + | 137 | 18 May 2019 | 50.00 | | 0.8667447076 | 43.34 | 3044.81 | 3.30 | | | 971.30 | + | 138 | 19 May 2019 | 50.00 | | 0.8658201723 | 43.29 | 2998.06 | 3.25 | | | 971.30 | + | 139 | 20 May 2019 | 50.00 | | 0.8648966231 | 43.24 | 2951.26 | 3.20 | | | 971.30 | + | 140 | 21 May 2019 | 50.00 | | 0.8639740591 | 43.20 | 2904.42 | 3.15 | | | 971.30 | + | 141 | 22 May 2019 | 50.00 | | 0.8630524792 | 43.15 | 2857.52 | 3.10 | | | 971.30 | + | 142 | 23 May 2019 | 50.00 | | 0.8621318823 | 43.11 | 2810.57 | 3.05 | | | 971.30 | + | 143 | 24 May 2019 | 50.00 | | 0.8612122673 | 43.06 | 2763.57 | 3.00 | | | 971.30 | + | 144 | 25 May 2019 | 50.00 | | 0.8602936333 | 43.01 | 2716.52 | 2.95 | | | 971.30 | + | 145 | 26 May 2019 | 50.00 | | 0.8593759792 | 42.97 | 2669.42 | 2.90 | | | 971.30 | + | 146 | 27 May 2019 | 50.00 | | 0.8584593039 | 42.92 | 2622.27 | 2.85 | | | 971.30 | + | 147 | 28 May 2019 | 50.00 | | 0.8575436064 | 42.88 | 2575.07 | 2.80 | | | 971.30 | + | 148 | 29 May 2019 | 50.00 | | 0.8566288857 | 42.83 | 2527.82 | 2.75 | | | 971.30 | + | 149 | 30 May 2019 | 50.00 | | 0.8557151407 | 42.79 | 2480.52 | 2.70 | | | 971.30 | + | 150 | 31 May 2019 | 50.00 | | 0.8548023703 | 42.74 | 2433.17 | 2.65 | | | 971.30 | + | 151 | 01 June 2019 | 50.00 | | 0.8538905736 | 42.69 | 2385.77 | 2.60 | | | 971.30 | + | 152 | 02 June 2019 | 50.00 | | 0.8529797495 | 42.65 | 2338.31 | 2.55 | | | 971.30 | + | 153 | 03 June 2019 | 50.00 | | 0.8520698969 | 42.60 | 2290.81 | 2.50 | | | 971.30 | + | 154 | 04 June 2019 | 50.00 | | 0.8511610148 | 42.56 | 2243.26 | 2.45 | | | 971.30 | + | 155 | 05 June 2019 | 50.00 | | 0.8502531022 | 42.51 | 2195.65 | 2.40 | | | 971.30 | + | 156 | 06 June 2019 | 50.00 | | 0.8493461581 | 42.47 | 2148.00 | 2.34 | | | 971.30 | + | 157 | 07 June 2019 | 50.00 | | 0.8484401814 | 42.42 | 2100.29 | 2.29 | | | 971.30 | + | 158 | 08 June 2019 | 50.00 | | 0.8475351711 | 42.38 | 2052.53 | 2.24 | | | 971.30 | + | 159 | 09 June 2019 | 50.00 | | 0.8466311261 | 42.33 | 2004.73 | 2.19 | | | 971.30 | + | 160 | 10 June 2019 | 50.00 | | 0.8457280454 | 42.29 | 1956.87 | 2.14 | | | 971.30 | + | 161 | 11 June 2019 | 50.00 | | 0.844825928 | 42.24 | 1908.96 | 2.09 | | | 971.30 | + | 162 | 12 June 2019 | 50.00 | | 0.8439247729 | 42.20 | 1860.99 | 2.04 | | | 971.30 | + | 163 | 13 June 2019 | 50.00 | | 0.8430245791 | 42.15 | 1812.98 | 1.99 | | | 971.30 | + | 164 | 14 June 2019 | 50.00 | | 0.8421253454 | 42.11 | 1764.92 | 1.94 | | | 971.30 | + | 165 | 15 June 2019 | 50.00 | | 0.841227071 | 42.06 | 1716.80 | 1.88 | | | 971.30 | + | 166 | 16 June 2019 | 50.00 | | 0.8403297547 | 42.02 | 1668.64 | 1.83 | | | 971.30 | + | 167 | 17 June 2019 | 50.00 | | 0.8394333956 | 41.97 | 1620.42 | 1.78 | | | 971.30 | + | 168 | 18 June 2019 | 50.00 | | 0.8385379925 | 41.93 | 1572.15 | 1.73 | | | 971.30 | + | 169 | 19 June 2019 | 50.00 | | 0.8376435446 | 41.88 | 1523.83 | 1.68 | | | 971.30 | + | 170 | 20 June 2019 | 50.00 | | 0.8367500508 | 41.84 | 1475.45 | 1.63 | | | 971.30 | + | 171 | 21 June 2019 | 50.00 | | 0.83585751 | 41.79 | 1427.03 | 1.58 | | | 971.30 | + | 172 | 22 June 2019 | 50.00 | | 0.8349659213 | 41.75 | 1378.55 | 1.52 | | | 971.30 | + | 173 | 23 June 2019 | 50.00 | | 0.8340752837 | 41.70 | 1330.02 | 1.47 | | | 971.30 | + | 174 | 24 June 2019 | 50.00 | | 0.833185596 | 41.66 | 1281.45 | 1.42 | | | 971.30 | + | 175 | 25 June 2019 | 50.00 | | 0.8322968574 | 41.61 | 1232.81 | 1.37 | | | 971.30 | + | 176 | 26 June 2019 | 50.00 | | 0.8314090667 | 41.57 | 1184.13 | 1.32 | | | 971.30 | + | 177 | 27 June 2019 | 50.00 | | 0.8305222231 | 41.53 | 1135.39 | 1.26 | | | 971.30 | + | 178 | 28 June 2019 | 50.00 | | 0.8296363254 | 41.48 | 1086.61 | 1.21 | | | 971.30 | + | 179 | 29 June 2019 | 50.00 | | 0.8287513727 | 41.44 | 1037.77 | 1.16 | | | 971.30 | + | 180 | 30 June 2019 | 50.00 | | 0.8278673639 | 41.39 | 988.88 | 1.11 | | | 971.30 | + | 181 | 01 July 2019 | 50.00 | | 0.8269842981 | 41.35 | 939.93 | 1.06 | | | 971.30 | + | 182 | 02 July 2019 | 50.00 | | 0.8261021742 | 41.31 | 890.93 | 1.00 | | | 971.30 | + | 183 | 03 July 2019 | 50.00 | | 0.8252209913 | 41.26 | 841.89 | 0.95 | | | 971.30 | + | 184 | 04 July 2019 | 50.00 | | 0.8243407483 | 41.22 | 792.79 | 0.90 | | | 971.30 | + | 185 | 05 July 2019 | 50.00 | | 0.8234614442 | 41.17 | 743.63 | 0.85 | | | 971.30 | + | 186 | 06 July 2019 | 50.00 | | 0.8225830781 | 41.13 | 694.43 | 0.79 | | | 971.30 | + | 187 | 07 July 2019 | 50.00 | | 0.8217056489 | 41.09 | 645.17 | 0.74 | | | 971.30 | + | 188 | 08 July 2019 | 50.00 | | 0.8208291556 | 41.04 | 595.86 | 0.69 | | | 971.30 | + | 189 | 09 July 2019 | 50.00 | | 0.8199535973 | 41.00 | 546.49 | 0.64 | | | 971.30 | + | 190 | 10 July 2019 | 50.00 | | 0.8190789729 | 40.95 | 497.08 | 0.58 | | | 971.30 | + | 191 | 11 July 2019 | 50.00 | | 0.8182052815 | 40.91 | 447.61 | 0.53 | | | 971.30 | + | 192 | 12 July 2019 | 50.00 | | 0.8173325219 | 40.87 | 398.08 | 0.48 | | | 971.30 | + | 193 | 13 July 2019 | 50.00 | | 0.8164606934 | 40.82 | 348.51 | 0.43 | | | 971.30 | + | 194 | 14 July 2019 | 50.00 | | 0.8155897948 | 40.78 | 298.88 | 0.37 | | | 971.30 | + | 195 | 15 July 2019 | 50.00 | | 0.8147198252 | 40.74 | 249.20 | 0.32 | | | 971.30 | + | 196 | 16 July 2019 | 50.00 | | 0.8138507835 | 40.69 | 199.47 | 0.27 | | | 971.30 | + | 197 | 17 July 2019 | 50.00 | | 0.8129826688 | 40.65 | 149.68 | 0.21 | | | 971.30 | + | 198 | 18 July 2019 | 50.00 | | 0.8121154802 | 40.61 | 99.84 | 0.16 | | | 971.30 | + | 199 | 19 July 2019 | 50.00 | | 0.8112492165 | 40.56 | 49.95 | 0.11 | | | 971.30 | + | 200 | 20 July 2019 | 50.00 | | 0.8103838768 | 40.52 | 0.00 | 0.05 | | | 971.30 | diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalPeriodPaymentRate.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalPeriodPaymentRate.feature new file mode 100644 index 00000000000..9f69eed575d --- /dev/null +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalPeriodPaymentRate.feature @@ -0,0 +1,284 @@ +@WorkingCapitalPeriodPaymentRateFeature +Feature: Working Capital Period Payment Rate + + @TestRailId:C78817 + Scenario: Verify Working Capital period payment rate added successfully on loan account - UC1 + When Admin sets the business date to "01 January 2026" + And Admin creates a client with random data + And Admin creates a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP | 01 January 2026 | 01 January 2026 | 100 | 100 | 1 | 0 | + Then Working capital loan creation was successful + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP | 2026-01-01 | 2026-01-01 | Submitted and pending approval | 100.0 | 0.0 | 100.0 | 1.0 | 0.0 | + Then Admin successfully approves the working capital loan on "01 January 2026" with "100" amount and expected disbursement date on "01 January 2026" + Then Admin successfully disburse the Working Capital loan on "01 January 2026" with "100" EUR transaction amount + Then Working Capital loan status will be "ACTIVE" + Then Verify Working Capital loan disbursement was successful + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP | 2026-01-01 | 2026-01-01 | Active | 100.0 | 100.0 | 100.0 | 1.0 | 0.0 | +#--- update period payment rate ---# + When Admin sets the business date to "15 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin update Working Capital period payment rate with "12.5" value + Then Working Capital Loan Period Payment Rate changes history contains the following data: + | Effective Date | Previous Rate | New Rate | Reversed | + | 15 January 2026 | 1.0 | 12.5 | false | + When Admin sets the business date to "15 March 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP | 2026-01-01 | 2026-01-01 | Active | 100.0 | 100.0 | 100.0 | 12.5 | 0.0 | + + @TestRailId:C78818 + Scenario: Verify Working Capital period payment rate added on first day of disbursement successfully on loan account - UC2 + When Admin sets the business date to "01 January 2026" + And Admin creates a client with random data + And Admin creates a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP | 01 January 2026 | 01 January 2026 | 100 | 100 | 1 | 0 | + Then Working capital loan creation was successful + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP | 2026-01-01 | 2026-01-01 | Submitted and pending approval | 100.0 | 0.0 | 100.0 | 1.0 | 0.0 | + Then Admin successfully approves the working capital loan on "01 January 2026" with "100" amount and expected disbursement date on "01 January 2026" + Then Admin successfully disburse the Working Capital loan on "01 January 2026" with "100" EUR transaction amount + Then Working Capital loan status will be "ACTIVE" + Then Verify Working Capital loan disbursement was successful + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP | 2026-01-01 | 2026-01-01 | Active | 100.0 | 100.0 | 100.0 | 1.0 | 0.0 | +#--- update period payment rate ---# + And Admin update Working Capital period payment rate with "12.5" value + Then Working Capital Loan Period Payment Rate changes history contains the following data: + | Effective Date | Previous Rate | New Rate | Reversed | + | 01 January 2026 | 1.0 | 12.5 | false | + When Admin sets the business date to "15 March 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP | 2026-01-01 | 2026-01-01 | Active | 100.0 | 100.0 | 100.0 | 12.5 | 0.0 | + + @TestRailId:C78819 + Scenario: Verify Working Capital period payment rate added successfully a few times a day on loan account - UC3 + When Admin sets the business date to "01 January 2026" + And Admin creates a client with random data + And Admin creates a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP | 01 January 2026 | 01 January 2026 | 100 | 100 | 1 | 0 | + Then Working capital loan creation was successful + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP | 2026-01-01 | 2026-01-01 | Submitted and pending approval | 100.0 | 0.0 | 100.0 | 1.0 | 0.0 | + Then Admin successfully approves the working capital loan on "01 January 2026" with "100" amount and expected disbursement date on "01 January 2026" + Then Admin successfully disburse the Working Capital loan on "01 January 2026" with "100" EUR transaction amount + Then Working Capital loan status will be "ACTIVE" + Then Verify Working Capital loan disbursement was successful + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP | 2026-01-01 | 2026-01-01 | Active | 100.0 | 100.0 | 100.0 | 1.0 | 0.0 | +#--- update period payment rate ---# + And Admin update Working Capital period payment rate with "12.5" value + Then Working Capital Loan Period Payment Rate changes history contains the following data: + | Effective Date | Previous Rate | New Rate | Reversed | + | 01 January 2026 | 1.0 | 12.5 | false | +#--- update period payment rate ---# + And Admin update Working Capital period payment rate with "19.38" value + Then Working Capital Loan Period Payment Rate changes history contains the following data: + | Effective Date | Previous Rate | New Rate | Reversed | + | 01 January 2026 | 1.0 | 12.5 | true | + | 01 January 2026 | 12.5 | 19.38 | false | + When Admin sets the business date to "15 March 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP | 2026-01-01 | 2026-01-01 | Active | 100.0 | 100.0 | 100.0 | 19.38 | 0.0 | + + @TestRailId:C78820 + Scenario: Verify Working Capital period payment rate added successfully a few times on different dates on loan account - UC4 + When Admin sets the business date to "01 January 2026" + And Admin creates a client with random data + And Admin creates a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP | 01 January 2026 | 01 January 2026 | 100 | 100 | 1 | 0 | + Then Working capital loan creation was successful + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP | 2026-01-01 | 2026-01-01 | Submitted and pending approval | 100.0 | 0.0 | 100.0 | 1.0 | 0.0 | + Then Admin successfully approves the working capital loan on "01 January 2026" with "100" amount and expected disbursement date on "01 January 2026" + Then Admin successfully disburse the Working Capital loan on "01 January 2026" with "100" EUR transaction amount + Then Working Capital loan status will be "ACTIVE" + Then Verify Working Capital loan disbursement was successful + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP | 2026-01-01 | 2026-01-01 | Active | 100.0 | 100.0 | 100.0 | 1.0 | 0.0 | +#--- update period payment rate ---# + And Admin update Working Capital period payment rate with "12.5" value + Then Working Capital Loan Period Payment Rate changes history contains the following data: + | Effective Date | Previous Rate | New Rate | Reversed | + | 01 January 2026 | 1.0 | 12.5 | false | +#--- update period payment rate ---# + When Admin sets the business date to "15 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin update Working Capital period payment rate with "19.38" value + Then Working Capital Loan Period Payment Rate changes history contains the following data: + | Effective Date | Previous Rate | New Rate | Reversed | + | 01 January 2026 | 1.0 | 12.5 | true | + | 15 January 2026 | 12.5 | 19.38 | false | +#--- update period payment rate ---# + When Admin sets the business date to "25 February 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin update Working Capital period payment rate with "18.09" value + Then Working Capital Loan Period Payment Rate changes history contains the following data: + | Effective Date | Previous Rate | New Rate | Reversed | + | 01 January 2026 | 1.0 | 12.5 | true | + | 15 January 2026 | 12.5 | 19.38 | true | + | 25 February 2026 | 19.38 | 18.09 | false | + When Admin sets the business date to "15 March 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP | 2026-01-01 | 2026-01-01 | Active | 100.0 | 100.0 | 100.0 | 18.09 | 0.0 | + + @TestRailId:C78821 + Scenario: Verify Working Capital period payment rate added successfully on different date on loan account - UC5 + When Admin sets the business date to "01 January 2026" + And Admin creates a client with random data + And Admin creates a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP | 01 January 2026 | 01 January 2026 | 100 | 100 | 12.5 | 0 | + Then Working capital loan creation was successful + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP | 2026-01-01 | 2026-01-01 | Submitted and pending approval | 100.0 | 0.0 | 100.0 | 12.5 | 0.0 | + Then Admin successfully approves the working capital loan on "01 January 2026" with "100" amount and expected disbursement date on "01 January 2026" + Then Admin successfully disburse the Working Capital loan on "01 January 2026" with "100" EUR transaction amount + Then Working Capital loan status will be "ACTIVE" + Then Verify Working Capital loan disbursement was successful + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP | 2026-01-01 | 2026-01-01 | Active | 100.0 | 100.0 | 100.0 | 12.5 | 0.0 | +#--- update period payment rate ---# + When Admin sets the business date to "25 April 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin update Working Capital period payment rate with "15" value + Then Working Capital Loan Period Payment Rate changes history contains the following data: + | Effective Date | Previous Rate | New Rate | Reversed | + | 25 April 2026 | 12.5 | 15.0 | false | + When Admin sets the business date to "28 April 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP | 2026-01-01 | 2026-01-01 | Active | 100.0 | 100.0 | 100.0 | 15.0 | 0.0 | + + @TestRailId:C78822 + Scenario Outline: Verify update Working Capital period payment rate failed with outranged rate change value within loan product level defined range - UC6 + When Admin sets the business date to "01 January 2026" + And Admin creates a client with random data + And Admin creates a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_PERIOD_PAYMENT_RATE | 01 January 2026 | 01 January 2026 | 100 | 100 | 15 | 0 | + Then Working capital loan creation was successful + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP_PERIOD_PAYMENT_RATE | 2026-01-01 | 2026-01-01 | Submitted and pending approval | 100.0 | 0.0 | 100.0 | 15.0 | 0.0 | + Then Admin successfully approves the working capital loan on "01 January 2026" with "100" amount and expected disbursement date on "01 January 2026" + Then Admin successfully disburse the Working Capital loan on "01 January 2026" with "100" EUR transaction amount + Then Working Capital loan status will be "ACTIVE" + Then Verify Working Capital loan disbursement was successful + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP_PERIOD_PAYMENT_RATE | 2026-01-01 | 2026-01-01 | Active | 100.0 | 100.0 | 100.0 | 15.0 | 0.0 | +#--- update period payment rate with invalid value that is out of allowed min/max values defined on loan product level ---# + And Admin update Working Capital period payment rate failed with "" value with error message + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP_PERIOD_PAYMENT_RATE | 2026-01-01 | 2026-01-01 | Active | 100.0 | 100.0 | 100.0 | 15.0 | 0.0 | + + Examples: + | rate_change_value | rate_change_error_message | + | 0.5 | Failed data validation due to: rate.below.product.minimum. | + | 99.5 | Failed data validation due to: rate.exceeds.product.maximum. | + + @TestRailId:C78823 + Scenario: Verify update Working Capital period payment rate update failed within non active loan - UC7 + When Admin sets the business date to "01 January 2026" + And Admin creates a client with random data + And Admin creates a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_PERIOD_PAYMENT_RATE | 01 January 2026 | 01 January 2026 | 100 | 100 | 15 | 0 | + Then Working capital loan creation was successful + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP_PERIOD_PAYMENT_RATE | 2026-01-01 | 2026-01-01 | Submitted and pending approval | 100.0 | 0.0 | 100.0 | 15.0 | 0.0 | + Then Admin update Working Capital period payment rate failed with "18" value on non active loan + Then Admin successfully approves the working capital loan on "01 January 2026" with "100" amount and expected disbursement date on "01 January 2026" + Then Admin update Working Capital period payment rate failed with "18" value on non active loan + Then Admin successfully disburse the Working Capital loan on "01 January 2026" with "100" EUR transaction amount + Then Working Capital loan status will be "ACTIVE" + Then Verify Working Capital loan disbursement was successful + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP_PERIOD_PAYMENT_RATE | 2026-01-01 | 2026-01-01 | Active | 100.0 | 100.0 | 100.0 | 15.0 | 0.0 | + + @TestRailId:C78824 + Scenario Outline: Verify update Working Capital period payment rate failed with invalid rate change value - UC8 + When Admin sets the business date to "01 January 2026" + And Admin creates a client with random data + And Admin creates a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_PERIOD_PAYMENT_RATE | 01 January 2026 | 01 January 2026 | 100 | 100 | 15 | 0 | + Then Working capital loan creation was successful + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP_PERIOD_PAYMENT_RATE | 2026-01-01 | 2026-01-01 | Submitted and pending approval | 100.0 | 0.0 | 100.0 | 15.0 | 0.0 | + Then Admin successfully approves the working capital loan on "01 January 2026" with "100" amount and expected disbursement date on "01 January 2026" + Then Admin successfully disburse the Working Capital loan on "01 January 2026" with "100" EUR transaction amount + Then Working Capital loan status will be "ACTIVE" + Then Verify Working Capital loan disbursement was successful + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP_PERIOD_PAYMENT_RATE | 2026-01-01 | 2026-01-01 | Active | 100.0 | 100.0 | 100.0 | 15.0 | 0.0 | +#--- update period payment rate with invalid or already set up value ---# + And Admin update Working Capital period payment rate failed with "" value with error message + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP_PERIOD_PAYMENT_RATE | 2026-01-01 | 2026-01-01 | Active | 100.0 | 100.0 | 100.0 | 15.0 | 0.0 | + + Examples: + | rate_change_value | rate_change_error_message | + | 0 | The parameter `periodPaymentRate` must be greater than 0. | + | 15 | New period payment rate is the same as the current rate | + + @TestRailId:C78825 + Scenario: Verify Working Capital period payment rate added successfully by externalId on loan account - UC9 + When Admin sets the business date to "01 January 2026" + And Admin creates a client with random data + And Admin creates a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP | 01 January 2026 | 01 January 2026 | 100 | 100 | 1 | 0 | + Then Working capital loan creation was successful + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP | 2026-01-01 | 2026-01-01 | Submitted and pending approval | 100.0 | 0.0 | 100.0 | 1.0 | 0.0 | + Then Admin successfully approves the working capital loan on "01 January 2026" with "100" amount and expected disbursement date on "01 January 2026" + Then Admin successfully disburse the Working Capital loan on "01 January 2026" with "100" EUR transaction amount + Then Working Capital loan status will be "ACTIVE" + Then Verify Working Capital loan disbursement was successful + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP | 2026-01-01 | 2026-01-01 | Active | 100.0 | 100.0 | 100.0 | 1.0 | 0.0 | +#--- update period payment rate by externalId ---# + When Admin sets the business date to "15 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin update Working Capital period payment rate with "12.5" value by externalId + Then Working Capital Loan Period Payment Rate changes history by externalId contains the following data: + | Effective Date | Previous Rate | New Rate | Reversed | + | 15 January 2026 | 1.0 | 12.5 | false | + When Admin sets the business date to "15 March 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Working capital loan account has the correct data: + | product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount | + | WCLP | 2026-01-01 | 2026-01-01 | Active | 100.0 | 100.0 | 100.0 | 12.5 | 0.0 | diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/WorkingCapitalLoanConstants.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/WorkingCapitalLoanConstants.java index b1f5b6f7d10..59c0b041c68 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/WorkingCapitalLoanConstants.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/WorkingCapitalLoanConstants.java @@ -81,4 +81,7 @@ private WorkingCapitalLoanConstants() { public static final String WRITE_OFF_REASONS = "WriteOffReasons"; public static final String CHARGE_OFF_REASONS = "ChargeOffReasons"; + + // Period payment rate change parameters + public static final String periodPaymentRateParamName = "periodPaymentRate"; } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/api/WorkingCapitalLoanAmortizationScheduleApiResource.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/api/WorkingCapitalLoanAmortizationScheduleApiResource.java index 23efa5538f0..cbd0482b9be 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/api/WorkingCapitalLoanAmortizationScheduleApiResource.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/api/WorkingCapitalLoanAmortizationScheduleApiResource.java @@ -49,7 +49,7 @@ public class WorkingCapitalLoanAmortizationScheduleApiResource { @Operation(summary = "Retrieve Projected Amortization Schedule", description = """ Returns the projected amortization schedule for a Working Capital Loan. - The schedule contains per-payment details including expected and forecast payments, \ + The schedule contains per-payment details including expected payments, \ discount factors, NPV values, balances, expected and actual amortization amounts, \ income modifications, and deferred balance. diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/api/WorkingCapitalLoanApiResource.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/api/WorkingCapitalLoanApiResource.java index 51e7ca9ce10..a53a057e794 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/api/WorkingCapitalLoanApiResource.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/api/WorkingCapitalLoanApiResource.java @@ -54,10 +54,12 @@ import org.apache.fineract.portfolio.workingcapitalloan.WorkingCapitalLoanConstants; import org.apache.fineract.portfolio.workingcapitalloan.data.WorkingCapitalLoanData; import org.apache.fineract.portfolio.workingcapitalloan.data.WorkingCapitalLoanDelinquencyTagHistoryData; +import org.apache.fineract.portfolio.workingcapitalloan.data.WorkingCapitalLoanPeriodPaymentRateChangeData; import org.apache.fineract.portfolio.workingcapitalloan.data.WorkingCapitalLoanTemplateData; import org.apache.fineract.portfolio.workingcapitalloan.exception.WorkingCapitalLoanNotFoundException; import org.apache.fineract.portfolio.workingcapitalloan.service.WorkingCapitalLoanApplicationReadPlatformService; import org.apache.fineract.portfolio.workingcapitalloan.service.WorkingCapitalLoanDelinquencyReadPlatformService; +import org.apache.fineract.portfolio.workingcapitalloan.service.WorkingCapitalLoanPeriodPaymentRateChangeReadService; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Component; @@ -74,6 +76,7 @@ public class WorkingCapitalLoanApiResource { private final WorkingCapitalLoanApplicationReadPlatformService readPlatformService; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; private final WorkingCapitalLoanDelinquencyReadPlatformService workingCapitalLoanDelinquencyReadPlatformService; + private final WorkingCapitalLoanPeriodPaymentRateChangeReadService rateChangeReadService; @GET @Path("template") @@ -310,4 +313,102 @@ private CommandProcessingResult handleStateTransition(final Long loanId, final S return this.commandsSourceWritePlatformService.logCommandSource(commandRequest); } + @PUT + @Path("{loanId}/discount") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(operationId = "updateWorkingCapitalLoanDiscountById", summary = "Update discount for a disbursed Working Capital Loan", description = "Discount can be added one time after disbursement and only on disbursement date.") + @RequestBody(required = true, content = @Content(schema = @Schema(implementation = WorkingCapitalLoanApiResourceSwagger.PutWorkingCapitalLoansLoanIdDiscountRequest.class))) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = WorkingCapitalLoanApiResourceSwagger.PutWorkingCapitalLoansLoanIdResponse.class))) }) + public CommandProcessingResult updateDiscountById( + @PathParam("loanId") @Parameter(description = "loanId", required = true) final Long loanId, + @Parameter(hidden = true) final String apiRequestBodyAsJson) { + return updateDiscount(loanId, null, apiRequestBodyAsJson); + } + + @PUT + @Path("external-id/{loanExternalId}/discount") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(operationId = "updateWorkingCapitalLoanDiscountByExternalId", summary = "Update discount for a disbursed Working Capital Loan by external id", description = "Discount can be added one time after disbursement and only on disbursement date.") + @RequestBody(required = true, content = @Content(schema = @Schema(implementation = WorkingCapitalLoanApiResourceSwagger.PutWorkingCapitalLoansLoanIdDiscountRequest.class))) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = WorkingCapitalLoanApiResourceSwagger.PutWorkingCapitalLoansLoanIdResponse.class))) }) + public CommandProcessingResult updateDiscountByExternalId( + @PathParam("loanExternalId") @Parameter(description = "loanExternalId", required = true) final String loanExternalId, + @Parameter(hidden = true) final String apiRequestBodyAsJson) { + return updateDiscount(null, loanExternalId, apiRequestBodyAsJson); + } + + private CommandProcessingResult updateDiscount(final Long loanId, final String loanExternalIdStr, final String apiRequestBodyAsJson) { + final Long resolvedLoanId = loanId != null ? loanId + : readPlatformService.getResolvedLoanId(ExternalIdFactory.produce(loanExternalIdStr)); + if (resolvedLoanId == null) { + throw new WorkingCapitalLoanNotFoundException(ExternalIdFactory.produce(loanExternalIdStr)); + } + final CommandWrapper commandRequest = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson) + .updateDiscountWorkingCapitalLoanApplication(resolvedLoanId).build(); + return this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } + + @PUT + @Path("{loanId}/payment-rate") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(operationId = "updateWorkingCapitalLoanRateById", summary = "Update period payment rate for an active Working Capital Loan", description = "Modifies the period payment rate and triggers schedule recalculation for the remaining term.") + @RequestBody(required = true, content = @Content(schema = @Schema(implementation = WorkingCapitalLoanApiResourceSwagger.PutWorkingCapitalLoansLoanIdRateRequest.class))) + public CommandProcessingResult updatePeriodPaymentRateById( + @PathParam("loanId") @Parameter(description = "loanId", required = true) final Long loanId, + @Parameter(hidden = true) final String apiRequestBodyAsJson) { + return updatePeriodPaymentRate(loanId, null, apiRequestBodyAsJson); + } + + @PUT + @Path("external-id/{loanExternalId}/payment-rate") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(operationId = "updateWorkingCapitalLoanRateByExternalId", summary = "Update period payment rate for an active Working Capital Loan by external id", description = "Modifies the period payment rate and triggers schedule recalculation for the remaining term.") + @RequestBody(required = true, content = @Content(schema = @Schema(implementation = WorkingCapitalLoanApiResourceSwagger.PutWorkingCapitalLoansLoanIdRateRequest.class))) + public CommandProcessingResult updatePeriodPaymentRateByExternalId( + @PathParam("loanExternalId") @Parameter(description = "loanExternalId", required = true) final String loanExternalId, + @Parameter(hidden = true) final String apiRequestBodyAsJson) { + return updatePeriodPaymentRate(null, loanExternalId, apiRequestBodyAsJson); + } + + private CommandProcessingResult updatePeriodPaymentRate(final Long loanId, final String loanExternalIdStr, + final String apiRequestBodyAsJson) { + final Long resolvedLoanId = loanId != null ? loanId + : readPlatformService.getResolvedLoanId(ExternalIdFactory.produce(loanExternalIdStr)); + if (resolvedLoanId == null) { + throw new WorkingCapitalLoanNotFoundException(ExternalIdFactory.produce(loanExternalIdStr)); + } + final CommandWrapper commandRequest = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson) + .updatePeriodPaymentRateWorkingCapitalLoanApplication(resolvedLoanId).build(); + return this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } + + @GET + @Path("{loanId}/rate-changes") + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(operationId = "getWorkingCapitalLoanRateChangeHistoryById", summary = "Retrieve rate change history for a Working Capital Loan", description = "Returns all rate change records for the loan, ordered by most recent first.") + public List getRateChangeHistoryById( + @PathParam("loanId") @Parameter(description = "loanId", required = true) final Long loanId) { + this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS); + return this.rateChangeReadService.retrieveRateChangeHistory(loanId); + } + + @GET + @Path("external-id/{loanExternalId}/rate-changes") + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(operationId = "getWorkingCapitalLoanRateChangeHistoryByExternalId", summary = "Retrieve rate change history for a Working Capital Loan by external id", description = "Returns all rate change records for the loan, ordered by most recent first.") + public List getRateChangeHistoryByExternalId( + @PathParam("loanExternalId") @Parameter(description = "loanExternalId", required = true) final String loanExternalId) { + this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS); + final Long resolvedLoanId = readPlatformService.getResolvedLoanId(ExternalIdFactory.produce(loanExternalId)); + if (resolvedLoanId == null) { + throw new WorkingCapitalLoanNotFoundException(ExternalIdFactory.produce(loanExternalId)); + } + return this.rateChangeReadService.retrieveRateChangeHistory(resolvedLoanId); + } } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/api/WorkingCapitalLoanApiResourceSwagger.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/api/WorkingCapitalLoanApiResourceSwagger.java index 9b5d28c333c..2f45ebda470 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/api/WorkingCapitalLoanApiResourceSwagger.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/api/WorkingCapitalLoanApiResourceSwagger.java @@ -617,4 +617,19 @@ private GetWorkingCapitalLoanDelinquencyRangeScheduleTagHistoryResponse() {} public BigDecimal delinquentAmount; } + @Schema(description = "Request for updating period payment rate on an active Working Capital Loan") + public static final class PutWorkingCapitalLoansLoanIdRateRequest { + + private PutWorkingCapitalLoansLoanIdRateRequest() {} + + @Schema(example = "0.17", description = "New period payment rate") + public BigDecimal periodPaymentRate; + + @Schema(example = "Rate change note") + public String note; + + @Schema(example = "en_GB") + public String locale; + } + } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/DefaultProjectedAmortizationScheduleCalculator.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/DefaultProjectedAmortizationScheduleCalculator.java index c639f697608..b6265311bec 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/DefaultProjectedAmortizationScheduleCalculator.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/DefaultProjectedAmortizationScheduleCalculator.java @@ -21,7 +21,7 @@ import java.math.BigDecimal; import java.math.MathContext; import java.time.LocalDate; -import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; @@ -34,12 +34,12 @@ public final class DefaultProjectedAmortizationScheduleCalculator implements Pro @Override @NonNull - public ProjectedAmortizationScheduleModel generateModel(@NonNull final BigDecimal originationFeeAmount, + public ProjectedAmortizationScheduleModel generateModel(@NonNull final BigDecimal discountFeeAmount, @NonNull final BigDecimal netDisbursementAmount, @NonNull final BigDecimal totalPaymentValue, @NonNull final BigDecimal periodPaymentRate, final int npvDayCount, @NonNull final LocalDate expectedDisbursementDate, - @NonNull final MathContext mc, @NonNull final MonetaryCurrency currency) { - return ProjectedAmortizationScheduleModel.generate(originationFeeAmount, netDisbursementAmount, totalPaymentValue, - periodPaymentRate, npvDayCount, expectedDisbursementDate, mc, currency); + @NonNull final MathContext mc, @NonNull final CurrencyData currency) { + return ProjectedAmortizationScheduleModel.generate(discountFeeAmount, netDisbursementAmount, totalPaymentValue, periodPaymentRate, + npvDayCount, expectedDisbursementDate, mc, currency); } @Override @@ -54,4 +54,12 @@ public void applyPayment(@NonNull final ProjectedAmortizationScheduleModel model @NonNull final BigDecimal paymentAmount) { model.applyPayment(paymentDate, paymentAmount); } + + @Override + @NonNull + public ProjectedAmortizationScheduleModel applyRateChange(@NonNull final ProjectedAmortizationScheduleModel model, + @NonNull final BigDecimal newPeriodPaymentRate, @NonNull final LocalDate rateChangeDate) { + model.applyRateChange(newPeriodPaymentRate, rateChangeDate); + return model; + } } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/ProjectedAmortizationScheduleCalculator.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/ProjectedAmortizationScheduleCalculator.java index f7e53452aad..6d4e1bd35b4 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/ProjectedAmortizationScheduleCalculator.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/ProjectedAmortizationScheduleCalculator.java @@ -21,7 +21,7 @@ import java.math.BigDecimal; import java.math.MathContext; import java.time.LocalDate; -import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.springframework.lang.NonNull; /** @@ -36,9 +36,9 @@ public interface ProjectedAmortizationScheduleCalculator { * @return model with no payments applied */ @NonNull - ProjectedAmortizationScheduleModel generateModel(@NonNull BigDecimal originationFeeAmount, @NonNull BigDecimal netDisbursementAmount, + ProjectedAmortizationScheduleModel generateModel(@NonNull BigDecimal discountFeeAmount, @NonNull BigDecimal netDisbursementAmount, @NonNull BigDecimal totalPaymentValue, @NonNull BigDecimal periodPaymentRate, int npvDayCount, - @NonNull LocalDate expectedDisbursementDate, @NonNull MathContext mc, @NonNull MonetaryCurrency currency); + @NonNull LocalDate expectedDisbursementDate, @NonNull MathContext mc, @NonNull CurrencyData currency); /** * Recalculates the model with updated financial parameters (at approval or disbursement). Preserves already applied @@ -70,4 +70,20 @@ ProjectedAmortizationScheduleModel addDisbursement(@NonNull ProjectedAmortizatio * actual payment amount */ void applyPayment(@NonNull ProjectedAmortizationScheduleModel model, @NonNull LocalDate paymentDate, @NonNull BigDecimal paymentAmount); + + /** + * Applies a rate change to the model in-place. Adds a {@link ProjectedAmortizationScheduleModel.RateSegment} and + * rebuilds the payment list. + * + * @param model + * the model to mutate + * @param newPeriodPaymentRate + * the new period payment rate + * @param rateChangeDate + * effective date of the rate change + * @return the same model instance (mutated) + */ + @NonNull + ProjectedAmortizationScheduleModel applyRateChange(@NonNull ProjectedAmortizationScheduleModel model, + @NonNull BigDecimal newPeriodPaymentRate, @NonNull LocalDate rateChangeDate); } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/ProjectedAmortizationScheduleModel.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/ProjectedAmortizationScheduleModel.java index d7980b39b4c..dc48f91c999 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/ProjectedAmortizationScheduleModel.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/ProjectedAmortizationScheduleModel.java @@ -18,12 +18,14 @@ */ package org.apache.fineract.portfolio.workingcapitalloan.calc; +import com.google.gson.annotations.SerializedName; import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; import java.time.LocalDate; import java.time.temporal.ChronoUnit; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -34,7 +36,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.fineract.infrastructure.core.serialization.gson.JsonExclude; import org.apache.fineract.infrastructure.core.service.MathUtil; -import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.domain.Money; /** @@ -45,6 +47,8 @@ *
  • {@link #generate} — create initial schedule (at loan creation)
  • *
  • {@link #regenerate} — recalculate with new amounts (at approval / disbursement)
  • *
  • {@link #applyPayment} — record payments by date; schedule rebuilds after each
  • + *
  • {@link #applyRateChange} — apply a mid-lifecycle rate change; adds a {@link RateSegment} and rebuilds the payment + * list in-place
  • * */ @Getter @@ -52,9 +56,10 @@ @Slf4j public final class ProjectedAmortizationScheduleModel { - private static final String MODEL_VERSION = "1"; + private static final String MODEL_VERSION = "3"; - private final Money originationFeeAmount; + @SerializedName(value = "discountFeeAmount", alternate = "originationFeeAmount") + private final Money discountFeeAmount; private final Money netDisbursementAmount; private final Money totalPaymentValue; private final BigDecimal periodPaymentRate; @@ -64,40 +69,50 @@ public final class ProjectedAmortizationScheduleModel { /** {@code (TPV × periodPaymentRate) / npvDayCount} — constant across payments. */ private final Money expectedPaymentAmount; - /** {@code roundUp((netDisbursementAmount + originationFeeAmount) / expectedPaymentAmount)} */ - private final int loanTerm; + /** {@code roundUp((netDisbursementAmount + discountFeeAmount) / expectedPaymentAmount)} */ + @SerializedName(value = "originalPaymentNumber", alternate = "loanTerm") + private final int originalPaymentNumber; - /** Periodic EIR from {@code RATE(loanTerm, -expectedPayment, netDisbursementAmount)}. */ + /** Periodic EIR from {@code RATE(originalPaymentNumber, -expectedPayment, netDisbursementAmount)}. */ private final BigDecimal effectiveInterestRate; @JsonExclude private final MathContext mc; @JsonExclude - private final MonetaryCurrency currency; + private final CurrencyData currency; @Getter(AccessLevel.NONE) - private final List appliedPayments; + @SerializedName(value = "actualPayments", alternate = "appliedPayments") + private final List actualPayments; @Getter(AccessLevel.NONE) - private List payments; + private final List rateSegments; - private ProjectedAmortizationScheduleModel(final Money originationFeeAmount, final Money netDisbursementAmount, + @Getter(AccessLevel.NONE) + @SerializedName(value = "projectedPayments", alternate = "payments") + private List projectedPayments; + + @Getter(AccessLevel.NONE) + private List originalProjectedPayments; + + private ProjectedAmortizationScheduleModel(final Money discountFeeAmount, final Money netDisbursementAmount, final Money totalPaymentValue, final BigDecimal periodPaymentRate, final int npvDayCount, - final LocalDate expectedDisbursementDate, final Money expectedPaymentAmount, final int loanTerm, - final BigDecimal effectiveInterestRate, final MathContext mc, final MonetaryCurrency currency) { - this.originationFeeAmount = originationFeeAmount; + final LocalDate expectedDisbursementDate, final Money expectedPaymentAmount, final int originalPaymentNumber, + final BigDecimal effectiveInterestRate, final MathContext mc, final CurrencyData currency) { + this.discountFeeAmount = discountFeeAmount; this.netDisbursementAmount = netDisbursementAmount; this.totalPaymentValue = totalPaymentValue; this.periodPaymentRate = periodPaymentRate; this.npvDayCount = npvDayCount; this.expectedDisbursementDate = expectedDisbursementDate; this.expectedPaymentAmount = expectedPaymentAmount; - this.loanTerm = loanTerm; + this.originalPaymentNumber = originalPaymentNumber; this.effectiveInterestRate = effectiveInterestRate; this.mc = mc; this.currency = currency; - this.appliedPayments = new ArrayList<>(); + this.actualPayments = new ArrayList<>(); + this.rateSegments = new ArrayList<>(); rebuildPayments(); } @@ -105,35 +120,56 @@ private ProjectedAmortizationScheduleModel(final Money originationFeeAmount, fin * Creates a skeleton instance for Gson deserialization. Gson will overwrite final fields via reflection; payments * are restored from JSON directly (no rebuild needed). */ - public static ProjectedAmortizationScheduleModel forDeserialization(final MathContext mc, final MonetaryCurrency currency) { + public static ProjectedAmortizationScheduleModel forDeserialization(final MathContext mc, final CurrencyData currency) { return new ProjectedAmortizationScheduleModel(mc, currency); } - private ProjectedAmortizationScheduleModel(final MathContext mc, final MonetaryCurrency currency) { - this.originationFeeAmount = null; + private ProjectedAmortizationScheduleModel(final MathContext mc, final CurrencyData currency) { + this.discountFeeAmount = null; this.netDisbursementAmount = null; this.totalPaymentValue = null; this.periodPaymentRate = null; this.npvDayCount = 0; this.expectedDisbursementDate = null; this.expectedPaymentAmount = null; - this.loanTerm = 0; + this.originalPaymentNumber = 0; this.effectiveInterestRate = null; this.mc = mc; this.currency = currency; - this.appliedPayments = new ArrayList<>(); - this.payments = List.of(); + this.actualPayments = new ArrayList<>(); + this.rateSegments = new ArrayList<>(); + this.projectedPayments = List.of(); + this.originalProjectedPayments = List.of(); + } + + public List projectedPayments() { + return projectedPayments; + } + + public List originalProjectedPayments() { + return originalProjectedPayments; + } + + public List rateSegments() { + return rateSegments != null ? List.copyOf(rateSegments) : List.of(); } - public List payments() { - return payments; + public int effectiveTotalTerm() { + if (rateSegments == null || rateSegments.isEmpty()) { + return originalPaymentNumber; + } + final RateSegment last = rateSegments.getLast(); + // When startDayIndex > 0, the segment overlaps one day with the base schedule (the split day), + // so subtract 1. When startDayIndex == 0, there are no base days — no overlap. + final int overlap = last.startDayIndex() > 0 ? 1 : 0; + return last.startDayIndex() + last.segmentTerm() - overlap; } - public static ProjectedAmortizationScheduleModel generate(final BigDecimal originationFeeAmount, final BigDecimal netDisbursementAmount, + public static ProjectedAmortizationScheduleModel generate(final BigDecimal discountFeeAmount, final BigDecimal netDisbursementAmount, final BigDecimal totalPaymentValue, final BigDecimal periodPaymentRate, final int npvDayCount, - final LocalDate expectedDisbursementDate, final MathContext mc, final MonetaryCurrency currency) { + final LocalDate expectedDisbursementDate, final MathContext mc, final CurrencyData currency) { - Objects.requireNonNull(originationFeeAmount, "originationFeeAmount"); + Objects.requireNonNull(discountFeeAmount, "discountFeeAmount"); Objects.requireNonNull(netDisbursementAmount, "netDisbursementAmount"); Objects.requireNonNull(totalPaymentValue, "totalPaymentValue"); Objects.requireNonNull(periodPaymentRate, "periodPaymentRate"); @@ -151,35 +187,35 @@ public static ProjectedAmortizationScheduleModel generate(final BigDecimal origi throw new IllegalArgumentException("expectedPaymentAmount must be positive (check totalPaymentValue and periodPaymentRate)"); } - final int term = netDisbursementAmount.add(originationFeeAmount, mc).divide(expectedPayment, mc).setScale(0, RoundingMode.UP) - .intValueExact(); - if (term <= 0) { - throw new IllegalArgumentException("computed loan term must be positive, got: " + term); + final int originalPaymentNumber = netDisbursementAmount.add(discountFeeAmount, mc).divide(expectedPayment, mc) + .setScale(0, RoundingMode.UP).intValueExact(); + if (originalPaymentNumber <= 0) { + throw new IllegalArgumentException("computed originalPaymentNumber must be positive, got: " + originalPaymentNumber); } - final BigDecimal eir = TvmFunctions.rate(term, expectedPayment.negate(), netDisbursementAmount, mc); + final BigDecimal eir = TvmFunctions.rate(originalPaymentNumber, expectedPayment.negate(), netDisbursementAmount, mc); - return new ProjectedAmortizationScheduleModel(Money.of(currency, originationFeeAmount, mc), + return new ProjectedAmortizationScheduleModel(Money.of(currency, discountFeeAmount, mc), Money.of(currency, netDisbursementAmount, mc), Money.of(currency, totalPaymentValue, mc), periodPaymentRate, npvDayCount, - expectedDisbursementDate, Money.of(currency, expectedPayment, mc), term, eir, mc, currency); + expectedDisbursementDate, Money.of(currency, expectedPayment, mc), originalPaymentNumber, eir, mc, currency); } public LocalDate normalizePaymentDateForSchedule(final LocalDate paymentDate) { Objects.requireNonNull(paymentDate, "paymentDate"); final LocalDate firstInstallmentDate = expectedDisbursementDate.plusDays(1); - final LocalDate lastInstallmentDate = expectedDisbursementDate.plusDays(loanTerm); + final LocalDate lastInstallmentDate = expectedDisbursementDate.plusDays(effectiveTotalTerm()); if (paymentDate.isBefore(firstInstallmentDate) || paymentDate.equals(expectedDisbursementDate)) { return firstInstallmentDate; } - if (payments == null || payments.isEmpty()) { + if (projectedPayments == null || projectedPayments.isEmpty()) { if (paymentDate.isAfter(lastInstallmentDate)) { return lastInstallmentDate; } return paymentDate; } - final ProjectedPayment nearestUnpaid = payments.stream().filter(p -> p.paymentNo() > 0).filter(p -> p.actualPaymentAmount() == null) - .findFirst().orElse(null); + final ProjectedPayment nearestUnpaid = projectedPayments.stream().filter(p -> p.paymentNo() > 0) + .filter(p -> p.actualPaymentAmount() == null).findFirst().orElse(null); if (nearestUnpaid != null && nearestUnpaid.date() != null) { if (nearestUnpaid.date().isBefore(firstInstallmentDate)) { return firstInstallmentDate; @@ -197,11 +233,11 @@ public void applyPayment(final LocalDate paymentDate, final BigDecimal amount) { Objects.requireNonNull(amount, "amount"); final LocalDate scheduleDate = normalizePaymentDateForSchedule(paymentDate); final int index = resolvePaymentIndex(scheduleDate); - if (index < 0 || index >= loanTerm) { + if (index < 0 || index >= effectiveTotalTerm()) { throw new IllegalArgumentException("paymentDate " + paymentDate + " is outside the valid range [" - + expectedDisbursementDate.plusDays(1) + " .. " + expectedDisbursementDate.plusDays(loanTerm) + "]"); + + expectedDisbursementDate.plusDays(1) + " .. " + expectedDisbursementDate.plusDays(effectiveTotalTerm()) + "]"); } - appliedPayments.add(new AppliedPayment(scheduleDate, amount)); + actualPayments.add(new ActualPayment(scheduleDate, money(amount))); rebuildPayments(); } @@ -210,60 +246,180 @@ public ProjectedAmortizationScheduleModel regenerate(final BigDecimal newDiscoun final LocalDate newStartDate) { final ProjectedAmortizationScheduleModel newModel = generate(newDiscountAmount, newNetAmount, totalPaymentValue.getAmount(), periodPaymentRate, npvDayCount, newStartDate, mc, currency); - newModel.appliedPayments.addAll(appliedPayments); + newModel.actualPayments.addAll(actualPayments); newModel.rebuildPayments(); return newModel; } public void recalculateNetAmortizationAndDeferredBalanceFrom(final LocalDate repaymentDate) { - if (repaymentDate == null || payments == null || payments.isEmpty()) { + if (repaymentDate == null || projectedPayments == null || projectedPayments.isEmpty()) { return; } - final ProjectedPayment lastRepayment = payments.stream().filter(p -> p.paymentNo() > 0).filter(p -> repaymentDate.equals(p.date())) - .reduce((a, b) -> b).orElse(null); + final ProjectedPayment lastRepayment = projectedPayments.stream().filter(p -> p.paymentNo() > 0) + .filter(p -> repaymentDate.equals(p.date())).reduce((a, b) -> b).orElse(null); if (lastRepayment == null) { log.warn("Repayment date {} not found among projected payments; skipping net/deferred recalculation", repaymentDate); return; } - int fromIndex = payments.indexOf(lastRepayment); + int fromIndex = projectedPayments.indexOf(lastRepayment); - BigDecimal runningNetAmortization = amountOrZero(payments.get(fromIndex).netAmortizationAmount()); - BigDecimal runningDeferredBalance = amountOrZero(payments.get(fromIndex).deferredBalance()); + BigDecimal runningNetAmortization = amountOrZero(projectedPayments.get(fromIndex).totalAmortizedAmount()); + BigDecimal runningDeferredBalance = amountOrZero(projectedPayments.get(fromIndex).deferredBalance()); - final List adjusted = new ArrayList<>(payments.subList(0, fromIndex + 1)); - for (int i = fromIndex + 1; i < payments.size(); i++) { - final ProjectedPayment current = payments.get(i); + final List adjusted = new ArrayList<>(projectedPayments.subList(0, fromIndex + 1)); + for (int i = fromIndex + 1; i < projectedPayments.size(); i++) { + final ProjectedPayment current = projectedPayments.get(i); final BigDecimal actualTotalAmortization = amountOrZero(current.actualAmortizationAmount()); runningNetAmortization = runningNetAmortization.subtract(actualTotalAmortization, mc); runningDeferredBalance = runningDeferredBalance.subtract(actualTotalAmortization, mc); - adjusted.add(new ProjectedPayment(current.paymentNo(), current.date(), current.count(), current.paymentsLeft(), - current.expectedPaymentAmount(), current.forecastPaymentAmount(), current.discountFactor(), current.npvValue(), - current.balance(), current.expectedAmortizationAmount(), money(runningNetAmortization), current.actualPaymentAmount(), + adjusted.add(new ProjectedPayment(current.paymentNo(), current.date(), current.paymentsLeft(), current.expectedPaymentAmount(), + current.forecastPaymentAmount(), current.discountFactor(), current.npvValue(), current.balance(), + current.expectedAmortizationAmount(), money(runningNetAmortization), current.actualPaymentAmount(), current.actualAmortizationAmount(), current.incomeModification(), money(runningDeferredBalance))); } - this.payments = List.copyOf(adjusted); + this.projectedPayments = List.copyOf(adjusted); + } + + /** + * Applies a rate change at the given date. Adds a {@link RateSegment} covering the remaining term from the change + * date forward. The model is mutated in-place; the payment list is rebuilt. + * + *

    + * Any existing segments at or after the split point are removed first (supports undo/overwrite). + * + * @param newPeriodPaymentRate + * the new period payment rate + * @param rateChangeDate + * the date of the rate change (must be within model's date range) + */ + public void applyRateChange(final BigDecimal newPeriodPaymentRate, final LocalDate rateChangeDate) { + Objects.requireNonNull(newPeriodPaymentRate, "newPeriodPaymentRate"); + Objects.requireNonNull(rateChangeDate, "rateChangeDate"); + + final int rawSplitDayIndex = (int) ChronoUnit.DAYS.between(expectedDisbursementDate, rateChangeDate); + if (rawSplitDayIndex < 0) { + throw new IllegalArgumentException("rateChangeDate must not be before expectedDisbursementDate"); + } + + // When the rate change is past the base schedule's term, clamp the segment start + // to originalPaymentNumber. The loan is still active (borrower hasn't paid), so the remaining + // balance is netDisbursement - paymentsReceived. + final int splitDayIndex = Math.min(rawSplitDayIndex, originalPaymentNumber); + + // Remove existing segments at or after split (supports overwrite on second rate change) + // Guard against null rateSegments from V1 model deserialization + if (rateSegments == null) { + throw new IllegalStateException("Model not properly initialized; rateSegments is null"); + } + rateSegments.removeIf(s -> s.startDayIndex() >= splitDayIndex); + + // Collect actual payments received before the split + BigDecimal paymentsReceived = BigDecimal.ZERO; + for (final ProjectedPayment p : projectedPayments) { + if (p.paymentNo() <= 0 || p.paymentNo() > splitDayIndex) { + continue; + } + if (p.actualPaymentAmount() != null) { + paymentsReceived = paymentsReceived.add(p.actualPaymentAmount().getAmount(), mc); + } + } + + // Compute balance at split: if past term, use remaining principal; otherwise use base amortization + final BigDecimal balanceAtSplit; + if (rawSplitDayIndex >= originalPaymentNumber) { + balanceAtSplit = netDisbursementAmount.getAmount().subtract(paymentsReceived, mc); + } else if (splitDayIndex > 0) { + final BalancesAndAmortizations ba = computeBaseBalancesUpTo(splitDayIndex); + balanceAtSplit = ba.balances().get(splitDayIndex - 1).getAmount(); + } else { + balanceAtSplit = netDisbursementAmount.getAmount(); + } + + final BigDecimal origNet = netDisbursementAmount.getAmount(); + final BigDecimal origDiscount = discountFeeAmount.getAmount(); + final BigDecimal tpv = totalPaymentValue.getAmount(); + + final BigDecimal newNetDisb = balanceAtSplit; + final BigDecimal newDiscount = origDiscount.add(origNet, mc).subtract(balanceAtSplit, mc).subtract(paymentsReceived, mc); + final int scale = currency.getDecimalPlaces(); + final BigDecimal newDailyPayment = tpv.multiply(newPeriodPaymentRate, mc).divide(BigDecimal.valueOf(npvDayCount), mc) + .setScale(scale, RoundingMode.HALF_UP); + final BigDecimal fractionalTotalDays = origNet.add(origDiscount, mc).subtract(paymentsReceived, mc).divide(newDailyPayment, mc) + .setScale(scale, RoundingMode.HALF_UP); + final int newTerm = fractionalTotalDays.intValue(); + + // When daily payment exceeds remaining gross (e.g., very short-term loan with high TPV), + // the fractional term rounds to 0. Use at least 1 period. + final int safeTerm = Math.max(newTerm, 1); + if (newNetDisb.signum() <= 0) { + throw new IllegalArgumentException("balance at split must be positive for rate change"); + } + + final BigDecimal newEir = TvmFunctions.rate(safeTerm, newDailyPayment.negate(), newNetDisb, mc); + + rateSegments.add(new RateSegment(splitDayIndex, money(newDailyPayment), safeTerm, newEir, money(newNetDisb), money(newDiscount))); + rateSegments.sort(Comparator.comparingInt(RateSegment::startDayIndex)); + + rebuildPayments(); + } + + /** + * Removes the last rate change segment and rebuilds the schedule. + */ + public void removeLastRateChange() { + if (rateSegments != null && !rateSegments.isEmpty()) { + rateSegments.removeLast(); + rebuildPayments(); + } } private void rebuildPayments() { + rebuildOriginalProjectedPayments(); final Map paymentsByDate = aggregatePaymentsByDate(); final List paymentList = buildPaymentList(paymentsByDate); - this.payments = List.copyOf(buildPayments(paymentList, paymentsByDate.size())); + this.projectedPayments = List.copyOf(buildPayments(paymentList, paymentsByDate.size())); + } + + private void rebuildOriginalProjectedPayments() { + final BalancesAndAmortizations ba = computeBalancesAndAmortizations(); + final int totalTerm = effectiveTotalTerm(); + final BigDecimal discountFee = discountFeeAmount.getAmount(); + final List result = new ArrayList<>(totalTerm + 1); + + result.add(createDisbursementPayment()); + + for (int i = 0; i < totalTerm; i++) { + final int periodNo = i + 1; + final long paymentsLeft = (long) periodNo; + final BigDecimal periodExpectedPayment = MathUtil.negativeToZero(expectedPaymentForDay(periodNo)); + final BigDecimal safeDf = safeDiscountFactor(paymentsLeft, periodNo); + final BigDecimal npvValue = MathUtil.negativeToZero(periodExpectedPayment.multiply(safeDf, mc)); + final BigDecimal safeExpectedAmort = ba.expectedAmortizations().get(i).getAmount().min(discountFee); + final BigDecimal balance = ba.balances().get(i).getAmount(); + + result.add(new ProjectedPayment(periodNo, expectedDisbursementDate.plusDays(periodNo), paymentsLeft, + money(periodExpectedPayment), money(periodExpectedPayment), safeDf, money(npvValue), money(balance), + money(safeExpectedAmort), null, null, null, null, money(discountFee))); + } + + this.originalProjectedPayments = List.copyOf(result); } private Map aggregatePaymentsByDate() { final Map result = new HashMap<>(); - for (final AppliedPayment payment : appliedPayments) { - result.merge(payment.date(), payment.amount(), BigDecimal::add); + for (final ActualPayment payment : actualPayments) { + result.merge(payment.date(), payment.amount().getAmount(), BigDecimal::add); } return result; } private List buildPaymentList(final Map paymentsByDate) { - final List result = new ArrayList<>(loanTerm); - for (int i = 0; i < loanTerm; i++) { + final int totalTerm = effectiveTotalTerm(); + final List result = new ArrayList<>(totalTerm); + for (int i = 0; i < totalTerm; i++) { final LocalDate paymentDate = expectedDisbursementDate.plusDays(i + 1); result.add(paymentsByDate.get(paymentDate)); } @@ -277,29 +433,29 @@ private int resolvePaymentIndex(final LocalDate date) { private List buildPayments(final List payments, final int appliedCount) { final BalancesAndAmortizations ba = computeBalancesAndAmortizations(); final PaymentAnalysis pa = analyzePayments(payments, appliedCount); - final List actualAmortizations = computeActualAmortizations(ba.expectedAmortizations, payments, appliedCount); + final List expectedAmortizationAmounts = ba.expectedAmortizations().stream().map(Money::getAmount).toList(); + final List actualAmortizations = computeActualAmortizations(expectedAmortizationAmounts, payments, appliedCount); final List runningExpected = computeRunningExpectedPayments(pa.excess); final List tailPayments = new ArrayList<>(); final BigDecimal tailNpv = buildTailPeriodsAndComputeNpv(tailPayments, pa.shortfall, appliedCount); final BigDecimal totalNetAmortization = computeTotalNetAmortization(payments, runningExpected, appliedCount, tailNpv); - final BigDecimal originationFee = originationFeeAmount.getAmount(); - final BigDecimal safeExpectedPayment = MathUtil.negativeToZero(expectedPaymentAmount.getAmount()); + final BigDecimal discountFee = discountFeeAmount.getAmount(); - final List result = new ArrayList<>(loanTerm + 2 + tailPayments.size()); - result.add(createDisbursementPayment(appliedCount)); + final List result = new ArrayList<>(effectiveTotalTerm() + 2 + tailPayments.size()); + result.add(createDisbursementPayment()); BigDecimal cumulativeActualAmort = BigDecimal.ZERO; - for (int i = 0; i < loanTerm; i++) { + for (int i = 0; i < effectiveTotalTerm(); i++) { final int periodNo = i + 1; final boolean hasAppliedAmount = payments.get(i) != null; - final long count = (long) loanTerm + appliedCount - periodNo; final long paymentsLeft = paymentsLeft(periodNo, appliedCount); - final BigDecimal safeDf = safeDiscountFactor(paymentsLeft); + final BigDecimal safeDf = safeDiscountFactor(paymentsLeft, periodNo); + final BigDecimal periodExpectedPayment = MathUtil.negativeToZero(expectedPaymentForDay(periodNo)); final BigDecimal safeRunningExpected = MathUtil.negativeToZero(runningExpected.get(i)); final BigDecimal npvSource = hasAppliedAmount ? payments.get(i) : safeRunningExpected; final BigDecimal npvValue = MathUtil.negativeToZero(npvSource.multiply(safeDf, mc)); - final BigDecimal safeExpectedAmort = ba.expectedAmortizations.get(i).min(originationFee); + final BigDecimal safeExpectedAmort = ba.expectedAmortizations().get(i).getAmount().min(discountFee); final BigDecimal netAmortization; final BigDecimal actualAmortization; @@ -307,21 +463,23 @@ private List buildPayments(final List payments, fi if (hasAppliedAmount) { actualAmortization = actualAmortizations.get(i); - netAmortization = totalNetAmortization.subtract(cumulativeActualAmort, mc).min(originationFee); - cumulativeActualAmort = cumulativeActualAmort.add(actualAmortization, mc).min(originationFee); - incomeModification = actualAmortization.subtract(safeExpectedAmort, mc); + netAmortization = totalNetAmortization.subtract(cumulativeActualAmort, mc).min(discountFee); + cumulativeActualAmort = cumulativeActualAmort.add(actualAmortization, mc).min(discountFee); + final boolean hasPositivePayment = payments.get(i).signum() > 0; + incomeModification = hasPositivePayment ? actualAmortization.subtract(safeExpectedAmort, mc) : null; } else { netAmortization = BigDecimal.ZERO; actualAmortization = null; - incomeModification = safeExpectedAmort.negate(); + incomeModification = null; } - final BigDecimal deferredBalance = originationFee.subtract(cumulativeActualAmort, mc); - final BigDecimal balance = ba.balances.get(i); - result.add(new ProjectedPayment(periodNo, expectedDisbursementDate.plusDays(periodNo), count, paymentsLeft, - money(safeExpectedPayment), money(safeRunningExpected), safeDf, money(npvValue), money(balance), + final BigDecimal deferredBalance = discountFee.subtract(cumulativeActualAmort, mc); + final BigDecimal balance = ba.balances().get(i).getAmount(); + result.add(new ProjectedPayment(periodNo, expectedDisbursementDate.plusDays(periodNo), paymentsLeft, + money(periodExpectedPayment), money(safeRunningExpected), safeDf, money(npvValue), money(balance), money(safeExpectedAmort), money(netAmortization), hasAppliedAmount ? money(payments.get(i)) : null, - actualAmortization != null ? money(actualAmortization) : null, money(incomeModification), money(deferredBalance))); + actualAmortization != null ? money(actualAmortization) : null, + incomeModification != null ? money(incomeModification) : null, money(deferredBalance))); } result.addAll(tailPayments); @@ -342,11 +500,10 @@ private static BigDecimal amountOrZero(final Money value) { return value != null && value.getAmount() != null ? value.getAmount() : BigDecimal.ZERO; } - private ProjectedPayment createDisbursementPayment(final int appliedCount) { + private ProjectedPayment createDisbursementPayment() { final Money negDisbursement = netDisbursementAmount.negated(mc); - final long count = (long) loanTerm + appliedCount; - return new ProjectedPayment(0, expectedDisbursementDate, count, 0L, negDisbursement, null, BigDecimal.ONE, negDisbursement, - netDisbursementAmount, null, null, null, null, null, originationFeeAmount); + return new ProjectedPayment(0, expectedDisbursementDate, 0L, negDisbursement, null, BigDecimal.ONE, negDisbursement, + netDisbursementAmount, null, null, null, null, null, discountFeeAmount); } /** @@ -354,25 +511,33 @@ private ProjectedPayment createDisbursementPayment(final int appliedCount) { * {@code expectedAmort[i] = balance[i] + expectedPayment - balance[i-1]} */ private BalancesAndAmortizations computeBalancesAndAmortizations() { - final BigDecimal onePlusRate = BigDecimal.ONE.add(effectiveInterestRate, mc); - final BigDecimal expectedPayment = expectedPaymentAmount.getAmount(); - final List balances = new ArrayList<>(loanTerm); - final List expectedAmortizations = new ArrayList<>(loanTerm); + final int totalTerm = effectiveTotalTerm(); + final List balances = new ArrayList<>(totalTerm); + final List expectedAmortizations = new ArrayList<>(totalTerm); BigDecimal prevBalance = netDisbursementAmount.getAmount(); - for (int i = 0; i < loanTerm; i++) { - final BigDecimal balance = prevBalance.multiply(onePlusRate, mc).subtract(expectedPayment, mc); - balances.add(balance); - expectedAmortizations.add(balance.add(expectedPayment, mc).subtract(prevBalance, mc)); + for (int i = 0; i < totalTerm; i++) { + final int dayIndex = i + 1; + final RateSegment seg = segmentForDay(dayIndex); + // At segment boundary, reset balance to segment's net disbursement + if (seg != null && seg.startDayIndex() == dayIndex) { + prevBalance = seg.netDisbursementAtSplit().getAmount(); + } + final BigDecimal eir = seg != null ? seg.effectiveInterestRate() : effectiveInterestRate; + final BigDecimal payment = seg != null ? seg.expectedPaymentAmount().getAmount() : expectedPaymentAmount.getAmount(); + final BigDecimal onePlusRate = BigDecimal.ONE.add(eir, mc); + final BigDecimal balance = prevBalance.multiply(onePlusRate, mc).subtract(payment, mc); + balances.add(money(balance)); + expectedAmortizations.add(money(balance.add(payment, mc).subtract(prevBalance, mc))); prevBalance = balance; } return new BalancesAndAmortizations(balances, expectedAmortizations); } private PaymentAnalysis analyzePayments(final List payments, final int appliedCount) { - final BigDecimal expectedPayment = expectedPaymentAmount.getAmount(); BigDecimal shortfall = BigDecimal.ZERO; BigDecimal excess = BigDecimal.ZERO; for (int i = 0; i < appliedCount; i++) { + final BigDecimal expectedPayment = expectedPaymentForDay(i + 1); final BigDecimal diff = payments.get(i).subtract(expectedPayment, mc); if (diff.signum() > 0) { excess = excess.add(diff, mc); @@ -386,10 +551,10 @@ private PaymentAnalysis analyzePayments(final List payments, final i /** Cursor-based: each payment consumes {@code actualPayment/expectedPayment} periods of expected amortization. */ private List computeActualAmortizations(final List expectedAmortizations, final List payments, final int appliedCount) { - final BigDecimal expectedPayment = expectedPaymentAmount.getAmount(); final List result = new ArrayList<>(appliedCount); BigDecimal cursor = BigDecimal.ZERO; for (int i = 0; i < appliedCount; i++) { + final BigDecimal expectedPayment = expectedPaymentForDay(i + 1); final BigDecimal periodsConsumed = payments.get(i).divide(expectedPayment, mc); result.add(consumeExpectedAmortization(expectedAmortizations, cursor, periodsConsumed)); cursor = cursor.add(periodsConsumed, mc); @@ -418,14 +583,13 @@ private BigDecimal consumeExpectedAmortization(final List expectedAm } private List computeRunningExpectedPayments(final BigDecimal excess) { - final BigDecimal expectedPayment = expectedPaymentAmount.getAmount(); - final List running = new ArrayList<>(loanTerm); - for (int i = 0; i < loanTerm; i++) { - running.add(expectedPayment); + final int totalTerm = effectiveTotalTerm(); + final List running = new ArrayList<>(totalTerm); + for (int i = 0; i < totalTerm; i++) { + running.add(expectedPaymentForDay(i + 1)); } - BigDecimal remainingExcess = excess; - for (int i = loanTerm - 1; i >= 0 && remainingExcess.signum() > 0; i--) { + for (int i = totalTerm - 1; i >= 0 && remainingExcess.signum() > 0; i--) { final BigDecimal reduction = remainingExcess.min(running.get(i)); running.set(i, running.get(i).subtract(reduction, mc)); remainingExcess = remainingExcess.subtract(reduction, mc); @@ -435,22 +599,20 @@ private List computeRunningExpectedPayments(final BigDecimal excess) private BigDecimal buildTailPeriodsAndComputeNpv(final List tailPayments, final BigDecimal shortfall, final int appliedCount) { - final BigDecimal expectedPayment = expectedPaymentAmount.getAmount(); + final int totalTerm = effectiveTotalTerm(); BigDecimal tailNpv = BigDecimal.ZERO; BigDecimal remaining = shortfall; int tailIndex = 0; while (remaining.signum() > 0) { - final int periodNo = loanTerm + tailIndex + 1; + final int periodNo = totalTerm + tailIndex + 1; + final BigDecimal tailExpectedPayment = expectedPaymentForDay(totalTerm); // use last segment's payment final long dl = paymentsLeft(periodNo, appliedCount); - final BigDecimal df = safeDiscountFactor(dl); - final BigDecimal forecast = remaining.min(expectedPayment); + final BigDecimal df = safeDiscountFactor(dl, totalTerm); + final BigDecimal forecast = remaining.min(tailExpectedPayment); final BigDecimal npv = MathUtil.negativeToZero(forecast.multiply(df, mc)); - - final long count = (long) loanTerm + appliedCount - periodNo; tailNpv = tailNpv.add(npv, mc); - tailPayments.add(new ProjectedPayment(periodNo, expectedDisbursementDate.plusDays(periodNo), count, dl, null, money(forecast), - df, money(npv), null, null, money(BigDecimal.ZERO), null, null, null, null)); - + tailPayments.add(new ProjectedPayment(periodNo, expectedDisbursementDate.plusDays(periodNo), dl, null, money(forecast), df, + money(npv), null, null, money(BigDecimal.ZERO), null, null, null, null)); remaining = remaining.subtract(forecast, mc); tailIndex++; } @@ -460,17 +622,19 @@ private BigDecimal buildTailPeriodsAndComputeNpv(final List ta /** {@code totalNetAmortization = -netDisbursementAmount + sum(npvSource × DF) + tailNpv} */ private BigDecimal computeTotalNetAmortization(final List payments, final List runningExpected, final int appliedCount, final BigDecimal tailNpv) { + final int totalTerm = effectiveTotalTerm(); BigDecimal total = netDisbursementAmount.getAmount().negate(); - for (int i = 0; i < loanTerm; i++) { + for (int i = 0; i < totalTerm; i++) { final BigDecimal npvSource = payments.get(i) != null ? payments.get(i) : runningExpected.get(i); - final BigDecimal df = safeDiscountFactor(paymentsLeft(i + 1, appliedCount)); + final BigDecimal df = safeDiscountFactor(paymentsLeft(i + 1, appliedCount), i + 1); total = total.add(npvSource.multiply(df, mc), mc); } return total.add(tailNpv, mc); } - private BigDecimal safeDiscountFactor(final long paymentsLeft) { - final BigDecimal df = TvmFunctions.discountFactor(effectiveInterestRate, paymentsLeft, mc); + private BigDecimal safeDiscountFactor(final long paymentsLeft, final int dayIndex) { + final BigDecimal eir = eirForDay(dayIndex); + final BigDecimal df = TvmFunctions.discountFactor(eir, paymentsLeft, mc); return df.signum() <= 0 ? BigDecimal.ONE : df; } @@ -478,17 +642,61 @@ private long paymentsLeft(final int periodNumber, final int appliedCount) { return Math.max(0L, (long) periodNumber - appliedCount); } + private RateSegment segmentForDay(final int dayIndex) { + if (rateSegments == null || rateSegments.isEmpty()) { + return null; + } + RateSegment active = null; + for (final RateSegment seg : rateSegments) { + if (seg.startDayIndex() <= dayIndex) { + active = seg; + } else { + break; + } + } + return active; + } + + private BigDecimal eirForDay(final int dayIndex) { + final RateSegment seg = segmentForDay(dayIndex); + return seg != null ? seg.effectiveInterestRate() : effectiveInterestRate; + } + + private BigDecimal expectedPaymentForDay(final int dayIndex) { + final RateSegment seg = segmentForDay(dayIndex); + return seg != null ? seg.expectedPaymentAmount().getAmount() : expectedPaymentAmount.getAmount(); + } + + private BalancesAndAmortizations computeBaseBalancesUpTo(final int upToDayIndex) { + final BigDecimal onePlusRate = BigDecimal.ONE.add(effectiveInterestRate, mc); + final BigDecimal basePayment = expectedPaymentAmount.getAmount(); + final List balances = new ArrayList<>(upToDayIndex); + final List expectedAmortizations = new ArrayList<>(upToDayIndex); + BigDecimal prevBalance = netDisbursementAmount.getAmount(); + for (int i = 0; i < upToDayIndex; i++) { + final BigDecimal balance = prevBalance.multiply(onePlusRate, mc).subtract(basePayment, mc); + balances.add(money(balance)); + expectedAmortizations.add(money(balance.add(basePayment, mc).subtract(prevBalance, mc))); + prevBalance = balance; + } + return new BalancesAndAmortizations(balances, expectedAmortizations); + } + private Money money(final BigDecimal amount) { return Money.of(currency, amount, mc); } - private record BalancesAndAmortizations(List balances, List expectedAmortizations) { + private record BalancesAndAmortizations(List balances, List expectedAmortizations) { } private record PaymentAnalysis(BigDecimal shortfall, BigDecimal excess) { } - public record AppliedPayment(LocalDate date, BigDecimal amount) { + public record ActualPayment(LocalDate date, Money amount) { + } + + public record RateSegment(int startDayIndex, Money expectedPaymentAmount, int segmentTerm, BigDecimal effectiveInterestRate, + Money netDisbursementAtSplit, Money discountAtSplit) { } public static String getModelVersion() { diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/ProjectedPayment.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/ProjectedPayment.java index fe4f3def7c4..8579037fff6 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/ProjectedPayment.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/ProjectedPayment.java @@ -38,9 +38,6 @@ public class ProjectedPayment { private final LocalDate date; - /** Remaining periods for calculation: {@code loanTerm + appliedPaymentCount - paymentNo}. */ - private final long count; - /** Exponent for discount factor: {@code DF = 1/(1+EIR)^paymentsLeft}. Zero for paid periods. */ private final long paymentsLeft; @@ -64,8 +61,8 @@ public class ProjectedPayment { /** {@code balance[i] + expectedPayment - balance[i-1]} (equivalent to {@code prevBalance × EIR}) */ private final Money expectedAmortizationAmount; - /** First paid: sum of all NPV values; subsequent: {@code netAmort[i-1] - actualAmort[i-1]}. */ - private final Money netAmortizationAmount; + /** First paid: sum of all NPV values; subsequent: {@code totalAmort[i-1] - actualAmort[i-1]}. */ + private final Money totalAmortizedAmount; private final Money actualPaymentAmount; diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/TvmFunctions.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/TvmFunctions.java index 34d8a0e18bd..4fac27bcab4 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/TvmFunctions.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/TvmFunctions.java @@ -31,9 +31,11 @@ */ public final class TvmFunctions { - private static final int MAX_ITERATIONS = 100; - private static final BigDecimal TOLERANCE = new BigDecimal("1E-10"); + private static final int MAX_ITERATIONS = 500; + private static final BigDecimal TOLERANCE = new BigDecimal("1E-12"); private static final BigDecimal DEFAULT_GUESS = new BigDecimal("0.01"); + private static final BigDecimal MIN_GUESS = new BigDecimal("1E-9"); + private static final BigDecimal MAX_GUESS = new BigDecimal("0.1"); private static final BigDecimal TWO = BigDecimal.valueOf(2); private TvmFunctions() {} @@ -71,7 +73,8 @@ public static BigDecimal rate(final int nper, final BigDecimal pmt, final BigDec * * @see #rate(int, BigDecimal, BigDecimal, MathContext) */ - public static BigDecimal rate(final int nper, final BigDecimal pmt, final BigDecimal pv, final BigDecimal guess, final MathContext mc) { + private static BigDecimal rate(final int nper, final BigDecimal pmt, final BigDecimal pv, final BigDecimal guess, + final MathContext mc) { if (nper <= 0) { throw new IllegalArgumentException("nper must be positive, got: " + nper); } @@ -119,8 +122,14 @@ public static BigDecimal rate(final int nper, final BigDecimal pmt, final BigDec } /** - * Linear approximation for the initial Newton-Raphson guess: {@code r ≈ 2·(pmt·n + pv) / (pv·n)}. Falls back to - * {@value #DEFAULT_GUESS} if the estimate is non-positive. + * Linear approximation for the initial Newton-Raphson guess: {@code r ≈ 2·(pmt·n + pv) / (pv·n)}. + * + *

    + * When total payments exceed the present value (typical for loans with origination fees), the formula yields a + * negative estimate. Its absolute value is still a good approximation of the periodic rate — it equals + * {@code 2·interest / (pv·n)} — so we return {@code |estimate|} instead of a fixed default. This avoids + * catastrophic divergence in Newton-Raphson when {@code nper} is large (e.g., daily-payment loans with thousands of + * periods), where the old default of 0.01 caused {@code (1+0.01)^nper} to explode. */ private static BigDecimal estimateInitialGuess(final int nper, final BigDecimal pmt, final BigDecimal pv, final MathContext mc) { final BigDecimal n = BigDecimal.valueOf(nper); @@ -129,7 +138,10 @@ private static BigDecimal estimateInitialGuess(final int nper, final BigDecimal return DEFAULT_GUESS; } final BigDecimal estimate = pmt.multiply(n, mc).add(pv, mc).multiply(TWO, mc).divide(pvTimesN, mc); - return estimate.signum() > 0 ? estimate : DEFAULT_GUESS; + if (estimate.signum() == 0) { + return DEFAULT_GUESS; + } + return estimate.abs().max(MIN_GUESS); } /** diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/data/ProjectedAmortizationScheduleData.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/data/ProjectedAmortizationScheduleData.java index d5b34284f66..b49cd1675cd 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/data/ProjectedAmortizationScheduleData.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/data/ProjectedAmortizationScheduleData.java @@ -30,14 +30,14 @@ @AllArgsConstructor public class ProjectedAmortizationScheduleData { - private final BigDecimal originationFeeAmount; + private final BigDecimal discountFeeAmount; private final BigDecimal netDisbursementAmount; private final BigDecimal totalPaymentValue; private final BigDecimal periodPaymentRate; private final int npvDayCount; private final LocalDate expectedDisbursementDate; private final BigDecimal expectedPaymentAmount; - private final int loanTerm; + private final int originalPaymentNumber; private final BigDecimal effectiveInterestRate; private final List payments; } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/data/ProjectedAmortizationScheduleGenerateRequest.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/data/ProjectedAmortizationScheduleGenerateRequest.java index 380f0f228d8..bd4bee6eeff 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/data/ProjectedAmortizationScheduleGenerateRequest.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/data/ProjectedAmortizationScheduleGenerateRequest.java @@ -29,7 +29,7 @@ @NoArgsConstructor public class ProjectedAmortizationScheduleGenerateRequest { - private BigDecimal originationFeeAmount; + private BigDecimal discountFeeAmount; private BigDecimal netDisbursementAmount; private BigDecimal totalPaymentValue; private BigDecimal periodPaymentRate; diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/data/ProjectedAmortizationSchedulePaymentData.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/data/ProjectedAmortizationSchedulePaymentData.java index 34bc396156b..10f5a849b4d 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/data/ProjectedAmortizationSchedulePaymentData.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/data/ProjectedAmortizationSchedulePaymentData.java @@ -31,15 +31,11 @@ public class ProjectedAmortizationSchedulePaymentData { private final int paymentNo; private final LocalDate paymentDate; - private final long count; - private final long paymentsLeft; private final BigDecimal expectedPaymentAmount; - private final BigDecimal forecastPaymentAmount; private final BigDecimal discountFactor; private final BigDecimal npvValue; private final BigDecimal balance; private final BigDecimal expectedAmortizationAmount; - private final BigDecimal netAmortizationAmount; private final BigDecimal actualPaymentAmount; private final BigDecimal actualAmortizationAmount; private final BigDecimal incomeModification; diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/data/WorkingCapitalLoanPeriodPaymentRateChangeData.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/data/WorkingCapitalLoanPeriodPaymentRateChangeData.java new file mode 100644 index 00000000000..1046c9f84c4 --- /dev/null +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/data/WorkingCapitalLoanPeriodPaymentRateChangeData.java @@ -0,0 +1,28 @@ +/** + * 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.workingcapitalloan.data; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.OffsetDateTime; + +public record WorkingCapitalLoanPeriodPaymentRateChangeData(Long id, Long loanId, LocalDate effectiveDate, BigDecimal previousRate, + BigDecimal newRate, boolean reversed, LocalDate reversedOnDate, OffsetDateTime createdDate) { + +} diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/domain/WorkingCapitalLoanPeriodPaymentRateChange.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/domain/WorkingCapitalLoanPeriodPaymentRateChange.java new file mode 100644 index 00000000000..71957075f81 --- /dev/null +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/domain/WorkingCapitalLoanPeriodPaymentRateChange.java @@ -0,0 +1,79 @@ +/** + * 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.workingcapitalloan.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.persistence.Version; +import java.math.BigDecimal; +import java.time.LocalDate; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom; + +@Getter +@Setter +@NoArgsConstructor +@Entity +@Table(name = "m_wc_loan_period_payment_rate_change") +public class WorkingCapitalLoanPeriodPaymentRateChange extends AbstractAuditableWithUTCDateTimeCustom { + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "wc_loan_id", nullable = false) + private WorkingCapitalLoan workingCapitalLoan; + + @Column(name = "effective_date", nullable = false) + private LocalDate effectiveDate; + + @Column(name = "previous_rate", scale = 6, precision = 19, nullable = false) + private BigDecimal previousRate; + + @Column(name = "new_rate", scale = 6, precision = 19, nullable = false) + private BigDecimal newRate; + + @Column(name = "is_reversed", nullable = false) + private boolean reversed; + + @Column(name = "reversed_on_date") + private LocalDate reversedOnDate; + + @Version + private int version; + + public static WorkingCapitalLoanPeriodPaymentRateChange create(final WorkingCapitalLoan loan, final LocalDate effectiveDate, + final BigDecimal previousRate, final BigDecimal newRate) { + final WorkingCapitalLoanPeriodPaymentRateChange change = new WorkingCapitalLoanPeriodPaymentRateChange(); + change.workingCapitalLoan = loan; + change.effectiveDate = effectiveDate; + change.previousRate = previousRate; + change.newRate = newRate; + change.reversed = false; + return change; + } + + public void reverse(final LocalDate reversalDate) { + this.reversed = true; + this.reversedOnDate = reversalDate; + } +} diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/handler/UpdateRateWorkingCapitalLoanCommandHandler.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/handler/UpdateRateWorkingCapitalLoanCommandHandler.java new file mode 100644 index 00000000000..416bcaa4662 --- /dev/null +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/handler/UpdateRateWorkingCapitalLoanCommandHandler.java @@ -0,0 +1,42 @@ +/** + * 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.workingcapitalloan.handler; + +import lombok.RequiredArgsConstructor; +import org.apache.fineract.commands.annotation.CommandType; +import org.apache.fineract.commands.handler.NewCommandSourceHandler; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.portfolio.workingcapitalloan.service.WorkingCapitalLoanWritePlatformService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@CommandType(entity = "WORKINGCAPITALLOAN", action = "UPDATERATE") +public class UpdateRateWorkingCapitalLoanCommandHandler implements NewCommandSourceHandler { + + private final WorkingCapitalLoanWritePlatformService writePlatformService; + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.updatePeriodPaymentRate(command.entityId(), command); + } +} diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/mapper/ProjectedAmortizationScheduleMapper.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/mapper/ProjectedAmortizationScheduleMapper.java index 9888c922904..19cb8794ef9 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/mapper/ProjectedAmortizationScheduleMapper.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/mapper/ProjectedAmortizationScheduleMapper.java @@ -35,17 +35,18 @@ public class ProjectedAmortizationScheduleMapper { private static final RoundingMode DISPLAY_ROUNDING = RoundingMode.HALF_UP; public ProjectedAmortizationScheduleData toData(final ProjectedAmortizationScheduleModel model) { - final List paymentDataList = model.payments().stream().map(this::toPaymentData).toList(); + final List paymentDataList = model.projectedPayments().stream().map(this::toPaymentData) + .toList(); return ProjectedAmortizationScheduleData.builder() // - .originationFeeAmount(roundMoney(model.originationFeeAmount())) // + .discountFeeAmount(roundMoney(model.discountFeeAmount())) // .netDisbursementAmount(roundMoney(model.netDisbursementAmount())) // .totalPaymentValue(roundMoney(model.totalPaymentValue())) // .periodPaymentRate(model.periodPaymentRate()) // .npvDayCount(model.npvDayCount()) // .expectedDisbursementDate(model.expectedDisbursementDate()) // .expectedPaymentAmount(roundMoney(model.expectedPaymentAmount())) // - .loanTerm(model.loanTerm()) // + .originalPaymentNumber(model.originalPaymentNumber()) // .effectiveInterestRate(model.effectiveInterestRate()) // .payments(paymentDataList) // .build(); @@ -55,15 +56,11 @@ private ProjectedAmortizationSchedulePaymentData toPaymentData(final ProjectedPa return ProjectedAmortizationSchedulePaymentData.builder() // .paymentNo(payment.paymentNo()) // .paymentDate(payment.date()) // - .count(payment.count()) // - .paymentsLeft(payment.paymentsLeft()) // .expectedPaymentAmount(roundMoney(payment.expectedPaymentAmount())) // - .forecastPaymentAmount(roundMoney(payment.forecastPaymentAmount())) // .discountFactor(payment.discountFactor()) // .npvValue(roundMoney(payment.npvValue())) // .balance(roundMoney(payment.balance())) // .expectedAmortizationAmount(roundMoney(payment.expectedAmortizationAmount())) // - .netAmortizationAmount(roundMoney(payment.netAmortizationAmount())) // .actualPaymentAmount(roundMoney(payment.actualPaymentAmount())) // .actualAmortizationAmount(roundMoney(payment.actualAmortizationAmount())) // .incomeModification(roundMoney(payment.incomeModification())) // diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/repository/WorkingCapitalLoanPeriodPaymentRateChangeRepository.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/repository/WorkingCapitalLoanPeriodPaymentRateChangeRepository.java new file mode 100644 index 00000000000..f1c89773d79 --- /dev/null +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/repository/WorkingCapitalLoanPeriodPaymentRateChangeRepository.java @@ -0,0 +1,33 @@ +/** + * 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.workingcapitalloan.repository; + +import java.util.List; +import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanPeriodPaymentRateChange; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface WorkingCapitalLoanPeriodPaymentRateChangeRepository + extends JpaRepository { + + List findByWorkingCapitalLoanIdOrderByIdDesc(Long loanId); + + List findByWorkingCapitalLoanIdAndReversedFalse(Long loanId); +} diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/serialization/WorkingCapitalLoanDataValidator.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/serialization/WorkingCapitalLoanDataValidator.java index 7c1b93bd6d2..fc396a6fbbd 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/serialization/WorkingCapitalLoanDataValidator.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/serialization/WorkingCapitalLoanDataValidator.java @@ -44,6 +44,7 @@ import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.portfolio.client.exception.ClientNotActiveException; import org.apache.fineract.portfolio.loanaccount.domain.ExpectedDisbursementDateValidator; +import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus; import org.apache.fineract.portfolio.workingcapitalloan.WorkingCapitalLoanConstants; import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoan; import org.apache.fineract.portfolio.workingcapitalloan.repository.WorkingCapitalLoanTransactionRepository; @@ -94,6 +95,9 @@ public class WorkingCapitalLoanDataValidator { WorkingCapitalLoanConstants.noteParamName, WorkingCapitalLoanConstants.transactionDateParamName)); private static final Set CREDIT_BALANCE_REFUND_SUPPORTED_PARAMETERS = new HashSet<>(REPAYMENT_SUPPORTED_PARAMETERS); + private static final Set UPDATE_RATE_SUPPORTED_PARAMETERS = new HashSet<>( + Arrays.asList("locale", WorkingCapitalLoanConstants.periodPaymentRateParamName, WorkingCapitalLoanConstants.noteParamName)); + private static final int NOTE_MAX_LENGTH = 1000; private static final int EXTERNAL_ID_MAX_LENGTH = 100; private static final int PAYMENT_DETAIL_STRING_MAX_LENGTH = 50; @@ -588,6 +592,58 @@ public void validateCreditBalanceRefund(final String json, final WorkingCapitalL throwExceptionIfValidationWarningsExist(dataValidationErrors); } + public void validateUpdatePeriodPaymentRate(final String json, final WorkingCapitalLoan loan) { + if (StringUtils.isBlank(json)) { + throw new InvalidJsonException(); + } + + if (loan.getLoanStatus() != LoanStatus.ACTIVE) { + throw new PlatformApiDataValidationException("validation.msg.wc.loan.rate.change.not.allowed", + "Period payment rate change is allowed only for active loans", "loanStatus"); + } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, UPDATE_RATE_SUPPORTED_PARAMETERS); + + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(WorkingCapitalLoanConstants.RESOURCE_NAME); + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final BigDecimal periodPaymentRate = this.fromApiJsonHelper + .extractBigDecimalNamed(WorkingCapitalLoanConstants.periodPaymentRateParamName, element, new HashSet<>()); + baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.periodPaymentRateParamName).value(periodPaymentRate).notNull() + .positiveAmount(); + + if (periodPaymentRate != null) { + final BigDecimal previousRate = loan.getLoanProductRelatedDetails().getPeriodPaymentRate(); + if (previousRate != null && previousRate.compareTo(periodPaymentRate) == 0) { + + throw new PlatformApiDataValidationException("validation.msg.wc.loan.rate.change.same.rate", + "New period payment rate is the same as the current rate", WorkingCapitalLoanConstants.periodPaymentRateParamName); + } + + if (loan.getLoanProduct() != null && loan.getLoanProduct().getMinMaxConstraints() != null) { + final BigDecimal minRate = loan.getLoanProduct().getMinMaxConstraints().getMinPeriodPaymentRate(); + final BigDecimal maxRate = loan.getLoanProduct().getMinMaxConstraints().getMaxPeriodPaymentRate(); + if (minRate != null && periodPaymentRate.compareTo(minRate) < 0) { + baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.periodPaymentRateParamName) + .failWithCode("rate.below.product.minimum"); + } + if (maxRate != null && periodPaymentRate.compareTo(maxRate) > 0) { + baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.periodPaymentRateParamName) + .failWithCode("rate.exceeds.product.maximum"); + } + } + } + + final String note = this.fromApiJsonHelper.extractStringNamed(WorkingCapitalLoanConstants.noteParamName, element); + baseDataValidator.reset().parameter(WorkingCapitalLoanConstants.noteParamName).value(note).ignoreIfNull() + .notExceedingLengthOf(NOTE_MAX_LENGTH); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/ProjectedAmortizationScheduleModelParserService.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/ProjectedAmortizationScheduleModelParserService.java index 62a1cfacd15..b983065213c 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/ProjectedAmortizationScheduleModelParserService.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/ProjectedAmortizationScheduleModelParserService.java @@ -19,7 +19,7 @@ package org.apache.fineract.portfolio.workingcapitalloan.service; import java.math.MathContext; -import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.portfolio.workingcapitalloan.calc.ProjectedAmortizationScheduleModel; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; @@ -29,5 +29,5 @@ public interface ProjectedAmortizationScheduleModelParserService { String toJson(@NonNull ProjectedAmortizationScheduleModel model); @Nullable - ProjectedAmortizationScheduleModel fromJson(@Nullable String json, @NonNull MathContext mc, @NonNull MonetaryCurrency currency); + ProjectedAmortizationScheduleModel fromJson(@Nullable String json, @NonNull MathContext mc, @NonNull CurrencyData currency); } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/ProjectedAmortizationScheduleModelParserServiceGsonImpl.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/ProjectedAmortizationScheduleModelParserServiceGsonImpl.java index 3de2eced796..a905b5dc16f 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/ProjectedAmortizationScheduleModelParserServiceGsonImpl.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/ProjectedAmortizationScheduleModelParserServiceGsonImpl.java @@ -21,16 +21,17 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.InstanceCreator; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializer; import com.google.gson.ToNumberPolicy; import java.math.MathContext; import java.time.LocalDate; import lombok.extern.slf4j.Slf4j; import org.apache.fineract.infrastructure.core.serialization.gson.JsonExcludeAnnotationBasedExclusionStrategy; import org.apache.fineract.infrastructure.core.serialization.gson.LocalDateAdapter; -import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.domain.Money; -import org.apache.fineract.organisation.monetary.serialization.MoneyDeserializer; -import org.apache.fineract.organisation.monetary.serialization.MoneySerializer; import org.apache.fineract.portfolio.workingcapitalloan.calc.ProjectedAmortizationScheduleModel; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; @@ -45,17 +46,18 @@ public class ProjectedAmortizationScheduleModelParserServiceGsonImpl implements private static Gson createSerializer() { return new GsonBuilder() // .registerTypeAdapter(LocalDate.class, new LocalDateAdapter().nullSafe()) // - .registerTypeAdapter(Money.class, new MoneySerializer()) // + .registerTypeAdapter(Money.class, (JsonSerializer) (src, typeOfSrc, context) -> new JsonPrimitive(src.getAmount())) // .setNumberToNumberStrategy(ToNumberPolicy.BIG_DECIMAL) // .addSerializationExclusionStrategy(new JsonExcludeAnnotationBasedExclusionStrategy()) // .addDeserializationExclusionStrategy(new JsonExcludeAnnotationBasedExclusionStrategy()) // .create(); } - private static Gson createDeserializer(final MathContext mc, final MonetaryCurrency currency) { + private static Gson createDeserializer(final MathContext mc, final CurrencyData currency) { return new GsonBuilder() // .registerTypeAdapter(LocalDate.class, new LocalDateAdapter().nullSafe()) // - .registerTypeAdapter(Money.class, new MoneyDeserializer(mc, currency)) // + .registerTypeAdapter(Money.class, + (JsonDeserializer) (json, typeOfT, context) -> Money.of(currency, json.getAsBigDecimal(), mc)) // .setNumberToNumberStrategy(ToNumberPolicy.BIG_DECIMAL) // .registerTypeAdapter(ProjectedAmortizationScheduleModel.class, (InstanceCreator) type -> ProjectedAmortizationScheduleModel @@ -73,7 +75,7 @@ public String toJson(@NonNull final ProjectedAmortizationScheduleModel model) { @Override @Nullable public ProjectedAmortizationScheduleModel fromJson(@Nullable final String json, @NonNull final MathContext mc, - @NonNull final MonetaryCurrency currency) { + @NonNull final CurrencyData currency) { if (json == null) { return null; } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/ProjectedAmortizationScheduleRepositoryWrapper.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/ProjectedAmortizationScheduleRepositoryWrapper.java index 98c6b95dd93..fa2fc4c4e99 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/ProjectedAmortizationScheduleRepositoryWrapper.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/ProjectedAmortizationScheduleRepositoryWrapper.java @@ -20,14 +20,14 @@ import java.math.MathContext; import java.util.Optional; -import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.portfolio.workingcapitalloan.calc.ProjectedAmortizationScheduleModel; import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoan; import org.springframework.lang.NonNull; public interface ProjectedAmortizationScheduleRepositoryWrapper { - Optional readModel(Long loanId, @NonNull MathContext mc, @NonNull MonetaryCurrency currency); + Optional readModel(Long loanId, @NonNull MathContext mc, @NonNull CurrencyData currency); void writeModel(@NonNull WorkingCapitalLoan loan, @NonNull ProjectedAmortizationScheduleModel model); diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/ProjectedAmortizationScheduleRepositoryWrapperImpl.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/ProjectedAmortizationScheduleRepositoryWrapperImpl.java index b0448a73e73..e4f54607494 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/ProjectedAmortizationScheduleRepositoryWrapperImpl.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/ProjectedAmortizationScheduleRepositoryWrapperImpl.java @@ -23,7 +23,7 @@ import lombok.RequiredArgsConstructor; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; -import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.portfolio.workingcapitalloan.calc.ProjectedAmortizationScheduleModel; import org.apache.fineract.portfolio.workingcapitalloan.domain.ProjectedAmortizationLoanModel; import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoan; @@ -41,7 +41,7 @@ public class ProjectedAmortizationScheduleRepositoryWrapperImpl implements Proje @Override public Optional readModel(final Long loanId, @NonNull final MathContext mc, - @NonNull final MonetaryCurrency currency) { + @NonNull final CurrencyData currency) { return repository.findByLoanId(loanId) // .map(ProjectedAmortizationLoanModel::getJsonModel) // .map(json -> parserService.fromJson(json, mc, currency)); @@ -51,11 +51,7 @@ public Optional readModel(final Long loanId, @Transactional public void writeModel(@NonNull final WorkingCapitalLoan loan, @NonNull final ProjectedAmortizationScheduleModel model) { final String jsonModel = parserService.toJson(model); - final ProjectedAmortizationLoanModel entity = repository.findByLoanId(loan.getId()).orElseGet(() -> { - final ProjectedAmortizationLoanModel newEntity = new ProjectedAmortizationLoanModel(); - newEntity.setLoan(loan); - return newEntity; - }); + final ProjectedAmortizationLoanModel entity = findOrCreateEntity(loan); entity.setBusinessDate(ThreadLocalContextUtil.getBusinessDate()); entity.setLastModifiedDate(DateUtils.getAuditOffsetDateTime()); entity.setJsonModel(jsonModel); @@ -63,4 +59,12 @@ public void writeModel(@NonNull final WorkingCapitalLoan loan, @NonNull final Pr repository.save(entity); } + private ProjectedAmortizationLoanModel findOrCreateEntity(final WorkingCapitalLoan loan) { + return repository.findByLoanId(loan.getId()).orElseGet(() -> { + final ProjectedAmortizationLoanModel newEntity = new ProjectedAmortizationLoanModel(); + newEntity.setLoan(loan); + return newEntity; + }); + } + } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAmortizationScheduleReadServiceImpl.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAmortizationScheduleReadServiceImpl.java index b16d947236a..2ec8abcdf1e 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAmortizationScheduleReadServiceImpl.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAmortizationScheduleReadServiceImpl.java @@ -20,10 +20,11 @@ import java.math.MathContext; import lombok.RequiredArgsConstructor; -import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.domain.MoneyHelper; import org.apache.fineract.portfolio.workingcapitalloan.calc.ProjectedAmortizationScheduleModel; import org.apache.fineract.portfolio.workingcapitalloan.data.ProjectedAmortizationScheduleData; +import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoan; import org.apache.fineract.portfolio.workingcapitalloan.exception.ProjectedAmortizationScheduleNotFoundException; import org.apache.fineract.portfolio.workingcapitalloan.exception.WorkingCapitalLoanNotFoundException; import org.apache.fineract.portfolio.workingcapitalloan.mapper.ProjectedAmortizationScheduleMapper; @@ -36,23 +37,29 @@ @Transactional(readOnly = true) public class WorkingCapitalLoanAmortizationScheduleReadServiceImpl implements WorkingCapitalLoanAmortizationScheduleReadService { - // TODO: currency should come from loan product once WCL lifecycle is implemented - private static final MonetaryCurrency DEFAULT_CURRENCY = new MonetaryCurrency("USD", 2, null); - private final WorkingCapitalLoanRepository loanRepository; private final ProjectedAmortizationScheduleRepositoryWrapper scheduleRepositoryWrapper; private final ProjectedAmortizationScheduleMapper mapper; @Override public ProjectedAmortizationScheduleData retrieveAmortizationSchedule(final Long loanId) { - if (!loanRepository.existsById(loanId)) { - throw new WorkingCapitalLoanNotFoundException(loanId); - } + final WorkingCapitalLoan loan = loanRepository.findById(loanId).orElseThrow(() -> new WorkingCapitalLoanNotFoundException(loanId)); final MathContext mc = MoneyHelper.getMathContext(); - final ProjectedAmortizationScheduleModel model = scheduleRepositoryWrapper.readModel(loanId, mc, DEFAULT_CURRENCY) + final CurrencyData currency = resolveCurrency(loan); + final ProjectedAmortizationScheduleModel model = scheduleRepositoryWrapper.readModel(loanId, mc, currency) .orElseThrow(() -> new ProjectedAmortizationScheduleNotFoundException(loanId)); return mapper.toData(model); } + + private CurrencyData resolveCurrency(final WorkingCapitalLoan loan) { + if (loan.getLoanProductRelatedDetails() != null && loan.getLoanProductRelatedDetails().getCurrency() != null) { + return loan.getLoanProductRelatedDetails().getCurrency().toData(); + } + if (loan.getLoanProduct() != null && loan.getLoanProduct().getCurrency() != null) { + return loan.getLoanProduct().getCurrency().toData(); + } + throw new IllegalStateException("No currency found for loan " + loan.getId()); + } } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAmortizationScheduleWriteService.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAmortizationScheduleWriteService.java index 645b71c6c92..3d76e9e11f7 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAmortizationScheduleWriteService.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAmortizationScheduleWriteService.java @@ -35,4 +35,6 @@ public interface WorkingCapitalLoanAmortizationScheduleWriteService { void regenerateAmortizationScheduleOnUndoDisbursal(WorkingCapitalLoan loan); RepaymentAmortizationData applyRepayment(WorkingCapitalLoan loan, LocalDate transactionDate, BigDecimal repaymentAmount); + + void regenerateAmortizationScheduleOnRateChange(WorkingCapitalLoan loan, BigDecimal newRate); } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAmortizationScheduleWriteServiceImpl.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAmortizationScheduleWriteServiceImpl.java index fba56f36acd..94b6c895387 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAmortizationScheduleWriteServiceImpl.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAmortizationScheduleWriteServiceImpl.java @@ -21,10 +21,13 @@ import java.math.BigDecimal; import java.math.MathContext; import java.time.LocalDate; +import java.time.temporal.ChronoUnit; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.Validate; -import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.domain.MoneyHelper; +import org.apache.fineract.portfolio.workingcapitalloan.calc.ProjectedAmortizationScheduleCalculator; import org.apache.fineract.portfolio.workingcapitalloan.calc.ProjectedAmortizationScheduleModel; import org.apache.fineract.portfolio.workingcapitalloan.calc.ProjectedPayment; import org.apache.fineract.portfolio.workingcapitalloan.data.ProjectedAmortizationScheduleGenerateRequest; @@ -45,10 +48,10 @@ @Transactional public class WorkingCapitalLoanAmortizationScheduleWriteServiceImpl implements WorkingCapitalLoanAmortizationScheduleWriteService { - private static final MonetaryCurrency DEFAULT_CURRENCY = new MonetaryCurrency("USD", 2, null); - private final WorkingCapitalLoanRepository loanRepository; private final ProjectedAmortizationScheduleRepositoryWrapper scheduleRepositoryWrapper; + private final ProjectedAmortizationScheduleCalculator calculator; + private final ProjectedAmortizationScheduleModelParserService parserService; @Override public void generateAndSaveAmortizationSchedule(final Long loanId, final ProjectedAmortizationScheduleGenerateRequest request) { @@ -57,13 +60,13 @@ public void generateAndSaveAmortizationSchedule(final Long loanId, final Project final MathContext mc = MoneyHelper.getMathContext(); final ProjectedAmortizationScheduleModel model = ProjectedAmortizationScheduleModel.generate(// - request.getOriginationFeeAmount(), // + request.getDiscountFeeAmount(), // request.getNetDisbursementAmount(), // request.getTotalPaymentValue(), // request.getPeriodPaymentRate(), // request.getNpvDayCount(), // request.getExpectedDisbursementDate(), // - mc, DEFAULT_CURRENCY); + mc, resolveCurrency(loan)); scheduleRepositoryWrapper.writeModel(loan, model); } @@ -169,7 +172,7 @@ public RepaymentAmortizationData applyRepayment(final WorkingCapitalLoan loan, f private BigDecimal sumRunningNpv(final ProjectedAmortizationScheduleModel model) { final MathContext mc = MoneyHelper.getMathContext(); BigDecimal result = BigDecimal.ZERO; - for (ProjectedPayment payment : model.payments()) { + for (ProjectedPayment payment : model.projectedPayments()) { if (payment.paymentNo() > 0 && payment.npvValue() != null && payment.npvValue().getAmount() != null) { result = result.add(payment.npvValue().getAmount(), mc); } @@ -177,13 +180,50 @@ private BigDecimal sumRunningNpv(final ProjectedAmortizationScheduleModel model) return result; } - private MonetaryCurrency resolveCurrency(final WorkingCapitalLoan loan) { + @Override + public void regenerateAmortizationScheduleOnRateChange(final WorkingCapitalLoan loan, final BigDecimal newRate) { + Validate.notNull(loan, "loan must not be null"); + Validate.notNull(newRate, "newRate must not be null"); + + final MathContext mc = MoneyHelper.getMathContext(); + final CurrencyData currency = resolveCurrency(loan); + final ProjectedAmortizationScheduleModel model = scheduleRepositoryWrapper.readModel(loan.getId(), mc, currency) + .orElseThrow(() -> new IllegalStateException("Projected amortization schedule is not found for loan " + loan.getId())); + + final LocalDate businessDate = ThreadLocalContextUtil.getBusinessDate(); + final LocalDate loanDisbursementDate = resolveLoanDisbursementDate(loan); + final int splitDayIndex = (int) ChronoUnit.DAYS.between(loanDisbursementDate, businessDate); + final LocalDate modelRateChangeDate = model.expectedDisbursementDate().plusDays(splitDayIndex); + + // Clear previous segments — the service auto-reverses previous rate changes, + // so each rate change starts fresh from the base schedule. + model.removeLastRateChange(); + + calculator.applyRateChange(model, newRate, modelRateChangeDate); + + scheduleRepositoryWrapper.writeModel(loan, model); + } + + private LocalDate resolveLoanDisbursementDate(final WorkingCapitalLoan loan) { + if (loan.getDisbursementDetails() != null && !loan.getDisbursementDetails().isEmpty()) { + final WorkingCapitalLoanDisbursementDetails detail = loan.getDisbursementDetails().getFirst(); + if (detail.getActualDisbursementDate() != null) { + return detail.getActualDisbursementDate(); + } + if (detail.getExpectedDisbursementDate() != null) { + return detail.getExpectedDisbursementDate(); + } + } + throw new IllegalStateException("Cannot determine disbursement date for loan " + loan.getId()); + } + + private CurrencyData resolveCurrency(final WorkingCapitalLoan loan) { if (loan.getLoanProductRelatedDetails() != null && loan.getLoanProductRelatedDetails().getCurrency() != null) { - return loan.getLoanProductRelatedDetails().getCurrency(); + return loan.getLoanProductRelatedDetails().getCurrency().toData(); } if (loan.getLoanProduct() != null && loan.getLoanProduct().getCurrency() != null) { - return loan.getLoanProduct().getCurrency(); + return loan.getLoanProduct().getCurrency().toData(); } - return DEFAULT_CURRENCY; + throw new IllegalStateException("No currency found for loan " + loan.getId()); } } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanPeriodPaymentRateChangeReadService.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanPeriodPaymentRateChangeReadService.java new file mode 100644 index 00000000000..bd7cec07918 --- /dev/null +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanPeriodPaymentRateChangeReadService.java @@ -0,0 +1,27 @@ +/** + * 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.workingcapitalloan.service; + +import java.util.List; +import org.apache.fineract.portfolio.workingcapitalloan.data.WorkingCapitalLoanPeriodPaymentRateChangeData; + +public interface WorkingCapitalLoanPeriodPaymentRateChangeReadService { + + List retrieveRateChangeHistory(Long loanId); +} diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanPeriodPaymentRateChangeReadServiceImpl.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanPeriodPaymentRateChangeReadServiceImpl.java new file mode 100644 index 00000000000..22ee040037f --- /dev/null +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanPeriodPaymentRateChangeReadServiceImpl.java @@ -0,0 +1,46 @@ +/** + * 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.workingcapitalloan.service; + +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.portfolio.workingcapitalloan.data.WorkingCapitalLoanPeriodPaymentRateChangeData; +import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanPeriodPaymentRateChange; +import org.apache.fineract.portfolio.workingcapitalloan.repository.WorkingCapitalLoanPeriodPaymentRateChangeRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class WorkingCapitalLoanPeriodPaymentRateChangeReadServiceImpl implements WorkingCapitalLoanPeriodPaymentRateChangeReadService { + + private final WorkingCapitalLoanPeriodPaymentRateChangeRepository repository; + + @Override + public List retrieveRateChangeHistory(final Long loanId) { + return repository.findByWorkingCapitalLoanIdOrderByIdDesc(loanId).stream().map(this::toData).toList(); + } + + private WorkingCapitalLoanPeriodPaymentRateChangeData toData(final WorkingCapitalLoanPeriodPaymentRateChange entity) { + return new WorkingCapitalLoanPeriodPaymentRateChangeData(entity.getId(), entity.getWorkingCapitalLoan().getId(), + entity.getEffectiveDate(), entity.getPreviousRate(), entity.getNewRate(), entity.isReversed(), entity.getReversedOnDate(), + entity.getCreatedDate().orElse(null)); + } +} diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWritePlatformService.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWritePlatformService.java index b191a261f32..85594fc1416 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWritePlatformService.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWritePlatformService.java @@ -38,4 +38,6 @@ public interface WorkingCapitalLoanWritePlatformService { CommandProcessingResult makeDiscountFee(Long resourceId, JsonCommand command); CommandProcessingResult creditBalanceRefund(Long loanId, JsonCommand command); + + CommandProcessingResult updatePeriodPaymentRate(Long loanId, JsonCommand command); } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWritePlatformServiceImpl.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWritePlatformServiceImpl.java index 0bd1d58d562..9598acb720a 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWritePlatformServiceImpl.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWritePlatformServiceImpl.java @@ -60,6 +60,7 @@ import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanEvent; import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanLifecycleStateMachine; import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanNote; +import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanPeriodPaymentRateChange; import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanTransaction; import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanTransactionAllocation; import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanTransactionRelation; @@ -67,6 +68,7 @@ import org.apache.fineract.portfolio.workingcapitalloan.exception.WorkingCapitalLoanNotFoundException; import org.apache.fineract.portfolio.workingcapitalloan.repository.WorkingCapitalLoanBalanceRepository; import org.apache.fineract.portfolio.workingcapitalloan.repository.WorkingCapitalLoanNoteRepository; +import org.apache.fineract.portfolio.workingcapitalloan.repository.WorkingCapitalLoanPeriodPaymentRateChangeRepository; import org.apache.fineract.portfolio.workingcapitalloan.repository.WorkingCapitalLoanRepository; import org.apache.fineract.portfolio.workingcapitalloan.repository.WorkingCapitalLoanTransactionAllocationRepository; import org.apache.fineract.portfolio.workingcapitalloan.repository.WorkingCapitalLoanTransactionRepository; @@ -98,6 +100,7 @@ public class WorkingCapitalLoanWritePlatformServiceImpl implements WorkingCapita private final CodeValueRepository codeValueRepository; private final BusinessEventNotifierService businessEventNotifierService; private final WorkingCapitalLoanTransactionRelationRepository relationRepository; + private final WorkingCapitalLoanPeriodPaymentRateChangeRepository rateChangeRepository; @Override public CommandProcessingResult approveApplication(final Long loanId, final JsonCommand command) { @@ -671,6 +674,57 @@ public CommandProcessingResult creditBalanceRefund(final Long loanId, final Json .withClientId(loan.getClientId()).withLoanId(loanId).with(changes).build(); } + @Override + @Transactional + public CommandProcessingResult updatePeriodPaymentRate(final Long loanId, final JsonCommand command) { + final WorkingCapitalLoan loan = this.loanRepository.findById(loanId) + .orElseThrow(() -> new WorkingCapitalLoanNotFoundException(loanId)); + this.validator.validateUpdatePeriodPaymentRate(command.json(), loan); + + final BigDecimal newRate = this.fromApiJsonHelper.extractBigDecimalNamed(WorkingCapitalLoanConstants.periodPaymentRateParamName, + command.parsedJson(), new HashSet<>()); + final BigDecimal previousRate = loan.getLoanProductRelatedDetails().getPeriodPaymentRate(); + + final LocalDate businessDate = DateUtils.getBusinessLocalDate(); + + final List activeChanges = this.rateChangeRepository + .findByWorkingCapitalLoanIdAndReversedFalse(loanId); + for (final WorkingCapitalLoanPeriodPaymentRateChange active : activeChanges) { + active.reverse(businessDate); + } + if (!activeChanges.isEmpty()) { + this.rateChangeRepository.saveAll(activeChanges); + } + + loan.getLoanProductRelatedDetails().setPeriodPaymentRate(newRate); + + final WorkingCapitalLoanPeriodPaymentRateChange rateChange = WorkingCapitalLoanPeriodPaymentRateChange.create(loan, businessDate, + previousRate, newRate); + this.rateChangeRepository.save(rateChange); + + try { + this.amortizationScheduleWriteService.regenerateAmortizationScheduleOnRateChange(loan, newRate); + } catch (IllegalStateException | IllegalArgumentException e) { + throw new PlatformApiDataValidationException("validation.msg.wc.loan.rate.change.calculation.failed", + "Rate change calculation failed: " + e.getMessage(), WorkingCapitalLoanConstants.periodPaymentRateParamName, e); + } + + final String noteText = command.stringValueOfParameterNamed(WorkingCapitalLoanConstants.noteParamName); + createNote(noteText, loan); + this.loanRepository.saveAndFlush(loan); + + final Map changes = new LinkedHashMap<>(); + changes.put(WorkingCapitalLoanConstants.periodPaymentRateParamName, newRate); + changes.put("previousRate", previousRate); + if (StringUtils.isNotBlank(noteText)) { + changes.put(WorkingCapitalLoanConstants.noteParamName, noteText); + } + + return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(loanId) + .withEntityExternalId(loan.getExternalId()).withOfficeId(loan.getOfficeId()).withClientId(loan.getClientId()) + .withLoanId(loanId).with(changes).build(); + } + private PaymentDetail createAndPersistPaymentDetailFromCommand(final JsonCommand command, final Map changes) { final JsonElement paymentDetailsElement = command.jsonElement(WorkingCapitalLoanConstants.paymentDetailsParamName); if (paymentDetailsElement != null && paymentDetailsElement.isJsonNull()) { diff --git a/fineract-working-capital-loan/src/main/resources/db/changelog/tenant/module/workingcapitalloan/module-changelog-master.xml b/fineract-working-capital-loan/src/main/resources/db/changelog/tenant/module/workingcapitalloan/module-changelog-master.xml index 5f7971fa2f1..0d2d802446e 100644 --- a/fineract-working-capital-loan/src/main/resources/db/changelog/tenant/module/workingcapitalloan/module-changelog-master.xml +++ b/fineract-working-capital-loan/src/main/resources/db/changelog/tenant/module/workingcapitalloan/module-changelog-master.xml @@ -56,4 +56,5 @@ + diff --git a/fineract-working-capital-loan/src/main/resources/db/changelog/tenant/module/workingcapitalloan/parts/0035_wc_loan_period_payment_rate_change.xml b/fineract-working-capital-loan/src/main/resources/db/changelog/tenant/module/workingcapitalloan/parts/0035_wc_loan_period_payment_rate_change.xml new file mode 100644 index 00000000000..5ad12113e6c --- /dev/null +++ b/fineract-working-capital-loan/src/main/resources/db/changelog/tenant/module/workingcapitalloan/parts/0035_wc_loan_period_payment_rate_change.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SELECT COUNT(*) FROM m_permission WHERE code = 'UPDATERATE_WORKINGCAPITALLOAN'; + + + + + + + + + + + + + + + SELECT COUNT(*) FROM m_permission WHERE code = 'UNDORATECHANGE_WORKINGCAPITALLOAN'; + + + + + + + + + + + + diff --git a/fineract-working-capital-loan/src/main/resources/jpa/static-weaving/module/fineract-working-capital-loan/persistence.xml b/fineract-working-capital-loan/src/main/resources/jpa/static-weaving/module/fineract-working-capital-loan/persistence.xml index fe2cca0328b..f35952e4603 100644 --- a/fineract-working-capital-loan/src/main/resources/jpa/static-weaving/module/fineract-working-capital-loan/persistence.xml +++ b/fineract-working-capital-loan/src/main/resources/jpa/static-weaving/module/fineract-working-capital-loan/persistence.xml @@ -172,6 +172,7 @@ org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanTransaction org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanTransactionAllocation org.apache.fineract.portfolio.workingcapitalloannearbreach.domain.WorkingCapitalNearBreach + org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanPeriodPaymentRateChange org.apache.fineract.portfolio.workingcapitalloanproduct.domain.WorkingCapitalLoanProduct org.apache.fineract.portfolio.workingcapitalloanproduct.domain.WorkingCapitalLoanProductConfigurableAttributes org.apache.fineract.portfolio.workingcapitalloanproduct.domain.WorkingCapitalLoanProductPaymentAllocationRule diff --git a/fineract-working-capital-loan/src/test/java/org/apache/fineract/portfolio/workingcapitalloan/calc/ProjectedAmortizationScheduleCalculatorTest.java b/fineract-working-capital-loan/src/test/java/org/apache/fineract/portfolio/workingcapitalloan/calc/ProjectedAmortizationScheduleCalculatorTest.java index e09a2cf63cb..5e47ac8a6d3 100644 --- a/fineract-working-capital-loan/src/test/java/org/apache/fineract/portfolio/workingcapitalloan/calc/ProjectedAmortizationScheduleCalculatorTest.java +++ b/fineract-working-capital-loan/src/test/java/org/apache/fineract/portfolio/workingcapitalloan/calc/ProjectedAmortizationScheduleCalculatorTest.java @@ -19,22 +19,26 @@ package org.apache.fineract.portfolio.workingcapitalloan.calc; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; import java.time.LocalDate; -import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.domain.Money; import org.junit.jupiter.api.Test; class ProjectedAmortizationScheduleCalculatorTest { private static final MathContext MC = MathContext.DECIMAL128; - private static final MonetaryCurrency CURRENCY = new MonetaryCurrency("USD", 2, null); + private static final CurrencyData CURRENCY = new CurrencyData("USD", 2, null); - private static final BigDecimal ORIGINATION_FEE = new BigDecimal("1000"); + private static final BigDecimal DISCOUNT_FEE = new BigDecimal("1000"); private static final BigDecimal NET_DISBURSEMENT = new BigDecimal("9000"); private static final BigDecimal TPV = new BigDecimal("100000"); private static final BigDecimal RATE = new BigDecimal("0.18"); @@ -45,2240 +49,2287 @@ class ProjectedAmortizationScheduleCalculatorTest { private final ProjectedAmortizationScheduleCalculator calculator = new DefaultProjectedAmortizationScheduleCalculator(); @Test - void testAddDisbursement_term10_originationFee50_netDisbursement450_then430() { - final BigDecimal originationFee = new BigDecimal("50"); + void testAddDisbursement_term10_discountFee50_netDisbursement450_then430() { + final BigDecimal discountFee = new BigDecimal("50"); final BigDecimal initialNetDisbursement = new BigDecimal("450"); final LocalDate initialDisbursementDate = LocalDate.of(2019, 1, 1); - final ProjectedAmortizationScheduleModel initial = calculator.generateModel(originationFee, initialNetDisbursement, TPV, RATE, + final ProjectedAmortizationScheduleModel initial = calculator.generateModel(discountFee, initialNetDisbursement, TPV, RATE, DAY_COUNT, initialDisbursementDate, MC, CURRENCY); - final ProjectedAmortizationScheduleModel model1 = calculator.addDisbursement(initial, originationFee, initialNetDisbursement, + final ProjectedAmortizationScheduleModel model1 = calculator.addDisbursement(initial, discountFee, initialNetDisbursement, initialDisbursementDate); - assertEquals(10, model1.loanTerm()); - assertEquals(11, model1.payments().size()); + assertEquals(10, model1.originalPaymentNumber()); + assertEquals(11, model1.projectedPayments().size()); - checkInst(model1, 0, 0, LocalDate.of(2019, 1, 1), 10, 0, -450.00, null, null, 1.00000000, -450.00, 450.00, null, null, null, null, + checkInst(model1, 0, 0, LocalDate.of(2019, 1, 1), 0, -450.00, null, null, 1.00000000, -450.00, 450.00, null, null, null, null, 50.00); - checkInst(model1, 1, 1, LocalDate.of(2019, 1, 2), 9, 1, 50.00, 50.00, null, 0.98074794, 49.04, 408.83, 8.83, 0.00, null, -8.83, + checkInst(model1, 1, 1, LocalDate.of(2019, 1, 2), 1, 50.00, 50.00, null, 0.98074794, 49.04, 408.83, 8.83, 0.00, null, null, 50.00); + checkInst(model1, 2, 2, LocalDate.of(2019, 1, 3), 2, 50.00, 50.00, null, 0.96186652, 48.09, 366.86, 8.03, 0.00, null, null, 50.00); + checkInst(model1, 3, 3, LocalDate.of(2019, 1, 4), 3, 50.00, 50.00, null, 0.94334860, 47.17, 324.06, 7.20, 0.00, null, null, 50.00); + checkInst(model1, 4, 4, LocalDate.of(2019, 1, 5), 4, 50.00, 50.00, null, 0.92518720, 46.26, 280.42, 6.36, 0.00, null, null, 50.00); + checkInst(model1, 5, 5, LocalDate.of(2019, 1, 6), 5, 50.00, 50.00, null, 0.90737544, 45.37, 235.93, 5.50, 0.00, null, null, 50.00); + checkInst(model1, 6, 6, LocalDate.of(2019, 1, 7), 6, 50.00, 50.00, null, 0.88990659, 44.50, 190.56, 4.63, 0.00, null, null, 50.00); + checkInst(model1, 7, 7, LocalDate.of(2019, 1, 8), 7, 50.00, 50.00, null, 0.87277405, 43.64, 144.30, 3.74, 0.00, null, null, 50.00); + checkInst(model1, 8, 8, LocalDate.of(2019, 1, 9), 8, 50.00, 50.00, null, 0.85597135, 42.80, 97.13, 2.83, 0.00, null, null, 50.00); + checkInst(model1, 9, 9, LocalDate.of(2019, 1, 10), 9, 50.00, 50.00, null, 0.83949214, 41.97, 49.04, 1.91, 0.00, null, null, 50.00); + checkInst(model1, 10, 10, LocalDate.of(2019, 1, 11), 10, 50.00, 50.00, null, 0.82333018, 41.17, 0.00, 0.96, 0.00, null, null, 50.00); - checkInst(model1, 2, 2, LocalDate.of(2019, 1, 3), 8, 2, 50.00, 50.00, null, 0.96186652, 48.09, 366.86, 8.03, 0.00, null, -8.03, - 50.00); - checkInst(model1, 3, 3, LocalDate.of(2019, 1, 4), 7, 3, 50.00, 50.00, null, 0.94334860, 47.17, 324.06, 7.20, 0.00, null, -7.20, - 50.00); - checkInst(model1, 4, 4, LocalDate.of(2019, 1, 5), 6, 4, 50.00, 50.00, null, 0.92518720, 46.26, 280.42, 6.36, 0.00, null, -6.36, - 50.00); - checkInst(model1, 5, 5, LocalDate.of(2019, 1, 6), 5, 5, 50.00, 50.00, null, 0.90737544, 45.37, 235.93, 5.50, 0.00, null, -5.50, - 50.00); - checkInst(model1, 6, 6, LocalDate.of(2019, 1, 7), 4, 6, 50.00, 50.00, null, 0.88990659, 44.50, 190.56, 4.63, 0.00, null, -4.63, - 50.00); - checkInst(model1, 7, 7, LocalDate.of(2019, 1, 8), 3, 7, 50.00, 50.00, null, 0.87277405, 43.64, 144.30, 3.74, 0.00, null, -3.74, - 50.00); - checkInst(model1, 8, 8, LocalDate.of(2019, 1, 9), 2, 8, 50.00, 50.00, null, 0.85597135, 42.80, 97.13, 2.83, 0.00, null, -2.83, - 50.00); - checkInst(model1, 9, 9, LocalDate.of(2019, 1, 10), 1, 9, 50.00, 50.00, null, 0.83949214, 41.97, 49.04, 1.91, 0.00, null, -1.91, + + final BigDecimal newNetDisbursement = new BigDecimal("430"); + final LocalDate newDisbursementDate = LocalDate.of(2019, 1, 5); + + final ProjectedAmortizationScheduleModel model2 = calculator.addDisbursement(model1, discountFee, newNetDisbursement, + newDisbursementDate); + + assertEquals(10, model2.originalPaymentNumber()); + assertEquals(11, model2.projectedPayments().size()); + + checkInst(model2, 0, 0, LocalDate.of(2019, 1, 5), 0, -430.00, null, null, 1.00000000, -430.00, 430.00, null, null, null, null, 50.00); - checkInst(model1, 10, 10, LocalDate.of(2019, 1, 11), 0, 10, 50.00, 50.00, null, 0.82333018, 41.17, 0.00, 0.96, 0.00, null, -0.96, + checkInst(model2, 1, 1, LocalDate.of(2019, 1, 6), 1, 50.00, 50.00, null, 0.97237826, 48.62, 392.21, 12.21, 0.00, null, null, 50.00); + checkInst(model2, 2, 2, LocalDate.of(2019, 1, 7), 2, 50.00, 50.00, null, 0.94551948, 47.28, 353.36, 11.14, 0.00, null, null, 50.00); + checkInst(model2, 3, 3, LocalDate.of(2019, 1, 8), 3, 50.00, 50.00, null, 0.91940259, 45.97, 313.39, 10.04, 0.00, null, null, 50.00); + checkInst(model2, 4, 4, LocalDate.of(2019, 1, 9), 4, 50.00, 50.00, null, 0.89400709, 44.70, 272.30, 8.90, 0.00, null, null, 50.00); + checkInst(model2, 5, 5, LocalDate.of(2019, 1, 10), 5, 50.00, 50.00, null, 0.86931306, 43.47, 230.03, 7.73, 0.00, null, null, 50.00); + checkInst(model2, 6, 6, LocalDate.of(2019, 1, 11), 6, 50.00, 50.00, null, 0.84530113, 42.27, 186.57, 6.53, 0.00, null, null, 50.00); + checkInst(model2, 7, 7, LocalDate.of(2019, 1, 12), 7, 50.00, 50.00, null, 0.82195244, 41.10, 141.87, 5.30, 0.00, null, null, 50.00); + checkInst(model2, 8, 8, LocalDate.of(2019, 1, 13), 8, 50.00, 50.00, null, 0.79924869, 39.96, 95.89, 4.03, 0.00, null, null, 50.00); + checkInst(model2, 9, 9, LocalDate.of(2019, 1, 14), 9, 50.00, 50.00, null, 0.77717205, 38.86, 48.62, 2.72, 0.00, null, null, 50.00); + checkInst(model2, 10, 10, LocalDate.of(2019, 1, 15), 10, 50.00, 50.00, null, 0.75570520, 37.79, 0.00, 1.38, 0.00, null, null, 50.00); + } + + @Test + void testProjectedSchedule_term200_discountFee1000_netDisbursement9000() { + final ProjectedAmortizationScheduleModel model = generateModel(); + + assertEquals(TERM, model.originalPaymentNumber()); + + checkInst(model, 0, 0, EXPECTED_DISBURSEMENT_DATE, 0, -9000.00, null, null, 1.00000000, -9000.00, 9000.00, null, null, null, null, + 1000.00); + + checkInst(model, 1, 1, LocalDate.of(2019, 1, 2), 1, 50.00, 50.00, null, 0.99893332, 49.95, 8959.61, 9.61, 0.00, null, null, + 1000.00); + checkInst(model, 2, 2, LocalDate.of(2019, 1, 3), 2, 50.00, 50.00, null, 0.99786779, 49.89, 8919.18, 9.57, 0.00, null, null, + 1000.00); + checkInst(model, 3, 3, LocalDate.of(2019, 1, 4), 3, 50.00, 50.00, null, 0.99680339, 49.84, 8878.70, 9.52, 0.00, null, null, + 1000.00); + checkInst(model, 4, 4, LocalDate.of(2019, 1, 5), 4, 50.00, 50.00, null, 0.99574012, 49.79, 8838.18, 9.48, 0.00, null, null, + 1000.00); + checkInst(model, 5, 5, LocalDate.of(2019, 1, 6), 5, 50.00, 50.00, null, 0.99467799, 49.73, 8797.62, 9.44, 0.00, null, null, + 1000.00); + checkInst(model, 6, 6, LocalDate.of(2019, 1, 7), 6, 50.00, 50.00, null, 0.99361699, 49.68, 8757.01, 9.39, 0.00, null, null, + 1000.00); + checkInst(model, 7, 7, LocalDate.of(2019, 1, 8), 7, 50.00, 50.00, null, 0.99255712, 49.63, 8716.36, 9.35, 0.00, null, null, + 1000.00); + checkInst(model, 8, 8, LocalDate.of(2019, 1, 9), 8, 50.00, 50.00, null, 0.99149839, 49.57, 8675.67, 9.31, 0.00, null, null, + 1000.00); + checkInst(model, 9, 9, LocalDate.of(2019, 1, 10), 9, 50.00, 50.00, null, 0.99044078, 49.52, 8634.94, 9.26, 0.00, null, null, + 1000.00); + checkInst(model, 10, 10, LocalDate.of(2019, 1, 11), 10, 50.00, 50.00, null, 0.98938430, 49.47, 8594.16, 9.22, 0.00, null, null, + 1000.00); + checkInst(model, 11, 11, LocalDate.of(2019, 1, 12), 11, 50.00, 50.00, null, 0.98832895, 49.42, 8553.33, 9.18, 0.00, null, null, + 1000.00); + checkInst(model, 12, 12, LocalDate.of(2019, 1, 13), 12, 50.00, 50.00, null, 0.98727472, 49.36, 8512.47, 9.13, 0.00, null, null, + 1000.00); + checkInst(model, 13, 13, LocalDate.of(2019, 1, 14), 13, 50.00, 50.00, null, 0.98622162, 49.31, 8471.56, 9.09, 0.00, null, null, + 1000.00); + checkInst(model, 14, 14, LocalDate.of(2019, 1, 15), 14, 50.00, 50.00, null, 0.98516964, 49.26, 8430.60, 9.05, 0.00, null, null, + 1000.00); + checkInst(model, 15, 15, LocalDate.of(2019, 1, 16), 15, 50.00, 50.00, null, 0.98411879, 49.21, 8389.61, 9.00, 0.00, null, null, + 1000.00); + checkInst(model, 16, 16, LocalDate.of(2019, 1, 17), 16, 50.00, 50.00, null, 0.98306905, 49.15, 8348.56, 8.96, 0.00, null, null, + 1000.00); + checkInst(model, 17, 17, LocalDate.of(2019, 1, 18), 17, 50.00, 50.00, null, 0.98202044, 49.10, 8307.48, 8.91, 0.00, null, null, + 1000.00); + checkInst(model, 18, 18, LocalDate.of(2019, 1, 19), 18, 50.00, 50.00, null, 0.98097294, 49.05, 8266.35, 8.87, 0.00, null, null, + 1000.00); + checkInst(model, 19, 19, LocalDate.of(2019, 1, 20), 19, 50.00, 50.00, null, 0.97992656, 49.00, 8225.18, 8.83, 0.00, null, null, + 1000.00); + checkInst(model, 20, 20, LocalDate.of(2019, 1, 21), 20, 50.00, 50.00, null, 0.97888129, 48.94, 8183.96, 8.78, 0.00, null, null, + 1000.00); + checkInst(model, 21, 21, LocalDate.of(2019, 1, 22), 21, 50.00, 50.00, null, 0.97783715, 48.89, 8142.70, 8.74, 0.00, null, null, + 1000.00); + checkInst(model, 22, 22, LocalDate.of(2019, 1, 23), 22, 50.00, 50.00, null, 0.97679411, 48.84, 8101.39, 8.69, 0.00, null, null, + 1000.00); + checkInst(model, 23, 23, LocalDate.of(2019, 1, 24), 23, 50.00, 50.00, null, 0.97575219, 48.79, 8060.04, 8.65, 0.00, null, null, + 1000.00); + checkInst(model, 24, 24, LocalDate.of(2019, 1, 25), 24, 50.00, 50.00, null, 0.97471138, 48.74, 8018.65, 8.61, 0.00, null, null, + 1000.00); + checkInst(model, 25, 25, LocalDate.of(2019, 1, 26), 25, 50.00, 50.00, null, 0.97367168, 48.68, 7977.21, 8.56, 0.00, null, null, + 1000.00); + checkInst(model, 26, 26, LocalDate.of(2019, 1, 27), 26, 50.00, 50.00, null, 0.97263309, 48.63, 7935.73, 8.52, 0.00, null, null, + 1000.00); + checkInst(model, 27, 27, LocalDate.of(2019, 1, 28), 27, 50.00, 50.00, null, 0.97159560, 48.58, 7894.21, 8.47, 0.00, null, null, + 1000.00); + checkInst(model, 28, 28, LocalDate.of(2019, 1, 29), 28, 50.00, 50.00, null, 0.97055922, 48.53, 7852.63, 8.43, 0.00, null, null, + 1000.00); + checkInst(model, 29, 29, LocalDate.of(2019, 1, 30), 29, 50.00, 50.00, null, 0.96952395, 48.48, 7811.02, 8.39, 0.00, null, null, + 1000.00); + checkInst(model, 30, 30, LocalDate.of(2019, 1, 31), 30, 50.00, 50.00, null, 0.96848979, 48.42, 7769.36, 8.34, 0.00, null, null, + 1000.00); + checkInst(model, 31, 31, LocalDate.of(2019, 2, 1), 31, 50.00, 50.00, null, 0.96745672, 48.37, 7727.66, 8.30, 0.00, null, null, + 1000.00); + checkInst(model, 32, 32, LocalDate.of(2019, 2, 2), 32, 50.00, 50.00, null, 0.96642476, 48.32, 7685.91, 8.25, 0.00, null, null, + 1000.00); + checkInst(model, 33, 33, LocalDate.of(2019, 2, 3), 33, 50.00, 50.00, null, 0.96539390, 48.27, 7644.12, 8.21, 0.00, null, null, + 1000.00); + checkInst(model, 34, 34, LocalDate.of(2019, 2, 4), 34, 50.00, 50.00, null, 0.96436413, 48.22, 7602.28, 8.16, 0.00, null, null, + 1000.00); + checkInst(model, 35, 35, LocalDate.of(2019, 2, 5), 35, 50.00, 50.00, null, 0.96333547, 48.17, 7560.40, 8.12, 0.00, null, null, + 1000.00); + checkInst(model, 36, 36, LocalDate.of(2019, 2, 6), 36, 50.00, 50.00, null, 0.96230790, 48.12, 7518.47, 8.07, 0.00, null, null, + 1000.00); + checkInst(model, 37, 37, LocalDate.of(2019, 2, 7), 37, 50.00, 50.00, null, 0.96128143, 48.06, 7476.50, 8.03, 0.00, null, null, + 1000.00); + checkInst(model, 38, 38, LocalDate.of(2019, 2, 8), 38, 50.00, 50.00, null, 0.96025606, 48.01, 7434.48, 7.98, 0.00, null, null, + 1000.00); + checkInst(model, 39, 39, LocalDate.of(2019, 2, 9), 39, 50.00, 50.00, null, 0.95923178, 47.96, 7392.42, 7.94, 0.00, null, null, + 1000.00); + checkInst(model, 40, 40, LocalDate.of(2019, 2, 10), 40, 50.00, 50.00, null, 0.95820859, 47.91, 7350.31, 7.89, 0.00, null, null, + 1000.00); + checkInst(model, 41, 41, LocalDate.of(2019, 2, 11), 41, 50.00, 50.00, null, 0.95718649, 47.86, 7308.16, 7.85, 0.00, null, null, + 1000.00); + checkInst(model, 42, 42, LocalDate.of(2019, 2, 12), 42, 50.00, 50.00, null, 0.95616548, 47.81, 7265.97, 7.80, 0.00, null, null, + 1000.00); + checkInst(model, 43, 43, LocalDate.of(2019, 2, 13), 43, 50.00, 50.00, null, 0.95514557, 47.76, 7223.72, 7.76, 0.00, null, null, + 1000.00); + checkInst(model, 44, 44, LocalDate.of(2019, 2, 14), 44, 50.00, 50.00, null, 0.95412674, 47.71, 7181.44, 7.71, 0.00, null, null, + 1000.00); + checkInst(model, 45, 45, LocalDate.of(2019, 2, 15), 45, 50.00, 50.00, null, 0.95310899, 47.66, 7139.11, 7.67, 0.00, null, null, + 1000.00); + checkInst(model, 46, 46, LocalDate.of(2019, 2, 16), 46, 50.00, 50.00, null, 0.95209233, 47.60, 7096.73, 7.62, 0.00, null, null, + 1000.00); + checkInst(model, 47, 47, LocalDate.of(2019, 2, 17), 47, 50.00, 50.00, null, 0.95107676, 47.55, 7054.31, 7.58, 0.00, null, null, + 1000.00); + checkInst(model, 48, 48, LocalDate.of(2019, 2, 18), 48, 50.00, 50.00, null, 0.95006227, 47.50, 7011.84, 7.53, 0.00, null, null, + 1000.00); + checkInst(model, 49, 49, LocalDate.of(2019, 2, 19), 49, 50.00, 50.00, null, 0.94904886, 47.45, 6969.33, 7.49, 0.00, null, null, + 1000.00); + checkInst(model, 50, 50, LocalDate.of(2019, 2, 20), 50, 50.00, 50.00, null, 0.94803653, 47.40, 6926.77, 7.44, 0.00, null, null, + 1000.00); + checkInst(model, 51, 51, LocalDate.of(2019, 2, 21), 51, 50.00, 50.00, null, 0.94702529, 47.35, 6884.17, 7.40, 0.00, null, null, + 1000.00); + checkInst(model, 52, 52, LocalDate.of(2019, 2, 22), 52, 50.00, 50.00, null, 0.94601512, 47.30, 6841.52, 7.35, 0.00, null, null, + 1000.00); + checkInst(model, 53, 53, LocalDate.of(2019, 2, 23), 53, 50.00, 50.00, null, 0.94500603, 47.25, 6798.82, 7.31, 0.00, null, null, + 1000.00); + checkInst(model, 54, 54, LocalDate.of(2019, 2, 24), 54, 50.00, 50.00, null, 0.94399801, 47.20, 6756.08, 7.26, 0.00, null, null, + 1000.00); + checkInst(model, 55, 55, LocalDate.of(2019, 2, 25), 55, 50.00, 50.00, null, 0.94299107, 47.15, 6713.30, 7.21, 0.00, null, null, + 1000.00); + checkInst(model, 56, 56, LocalDate.of(2019, 2, 26), 56, 50.00, 50.00, null, 0.94198521, 47.10, 6670.47, 7.17, 0.00, null, null, + 1000.00); + checkInst(model, 57, 57, LocalDate.of(2019, 2, 27), 57, 50.00, 50.00, null, 0.94098042, 47.05, 6627.59, 7.12, 0.00, null, null, + 1000.00); + checkInst(model, 58, 58, LocalDate.of(2019, 2, 28), 58, 50.00, 50.00, null, 0.93997669, 47.00, 6584.67, 7.08, 0.00, null, null, + 1000.00); + checkInst(model, 59, 59, LocalDate.of(2019, 3, 1), 59, 50.00, 50.00, null, 0.93897404, 46.95, 6541.70, 7.03, 0.00, null, null, + 1000.00); + checkInst(model, 60, 60, LocalDate.of(2019, 3, 2), 60, 50.00, 50.00, null, 0.93797246, 46.90, 6498.68, 6.99, 0.00, null, null, + 1000.00); + checkInst(model, 61, 61, LocalDate.of(2019, 3, 3), 61, 50.00, 50.00, null, 0.93697195, 46.85, 6455.62, 6.94, 0.00, null, null, + 1000.00); + checkInst(model, 62, 62, LocalDate.of(2019, 3, 4), 62, 50.00, 50.00, null, 0.93597251, 46.80, 6412.51, 6.89, 0.00, null, null, + 1000.00); + checkInst(model, 63, 63, LocalDate.of(2019, 3, 5), 63, 50.00, 50.00, null, 0.93497413, 46.75, 6369.36, 6.85, 0.00, null, null, + 1000.00); + checkInst(model, 64, 64, LocalDate.of(2019, 3, 6), 64, 50.00, 50.00, null, 0.93397681, 46.70, 6326.16, 6.80, 0.00, null, null, + 1000.00); + checkInst(model, 65, 65, LocalDate.of(2019, 3, 7), 65, 50.00, 50.00, null, 0.93298056, 46.65, 6282.92, 6.76, 0.00, null, null, + 1000.00); + checkInst(model, 66, 66, LocalDate.of(2019, 3, 8), 66, 50.00, 50.00, null, 0.93198538, 46.60, 6239.63, 6.71, 0.00, null, null, + 1000.00); + checkInst(model, 67, 67, LocalDate.of(2019, 3, 9), 67, 50.00, 50.00, null, 0.93099125, 46.55, 6196.29, 6.66, 0.00, null, null, + 1000.00); + checkInst(model, 68, 68, LocalDate.of(2019, 3, 10), 68, 50.00, 50.00, null, 0.92999818, 46.50, 6152.91, 6.62, 0.00, null, null, + 1000.00); + checkInst(model, 69, 69, LocalDate.of(2019, 3, 11), 69, 50.00, 50.00, null, 0.92900618, 46.45, 6109.48, 6.57, 0.00, null, null, + 1000.00); + checkInst(model, 70, 70, LocalDate.of(2019, 3, 12), 70, 50.00, 50.00, null, 0.92801523, 46.40, 6066.00, 6.52, 0.00, null, null, + 1000.00); + checkInst(model, 71, 71, LocalDate.of(2019, 3, 13), 71, 50.00, 50.00, null, 0.92702534, 46.35, 6022.48, 6.48, 0.00, null, null, + 1000.00); + checkInst(model, 72, 72, LocalDate.of(2019, 3, 14), 72, 50.00, 50.00, null, 0.92603650, 46.30, 5978.91, 6.43, 0.00, null, null, + 1000.00); + checkInst(model, 73, 73, LocalDate.of(2019, 3, 15), 73, 50.00, 50.00, null, 0.92504872, 46.25, 5935.29, 6.38, 0.00, null, null, + 1000.00); + checkInst(model, 74, 74, LocalDate.of(2019, 3, 16), 74, 50.00, 50.00, null, 0.92406200, 46.20, 5891.63, 6.34, 0.00, null, null, + 1000.00); + checkInst(model, 75, 75, LocalDate.of(2019, 3, 17), 75, 50.00, 50.00, null, 0.92307632, 46.15, 5847.92, 6.29, 0.00, null, null, + 1000.00); + checkInst(model, 76, 76, LocalDate.of(2019, 3, 18), 76, 50.00, 50.00, null, 0.92209170, 46.10, 5804.17, 6.24, 0.00, null, null, + 1000.00); + checkInst(model, 77, 77, LocalDate.of(2019, 3, 19), 77, 50.00, 50.00, null, 0.92110813, 46.06, 5760.36, 6.20, 0.00, null, null, + 1000.00); + checkInst(model, 78, 78, LocalDate.of(2019, 3, 20), 78, 50.00, 50.00, null, 0.92012560, 46.01, 5716.52, 6.15, 0.00, null, null, + 1000.00); + checkInst(model, 79, 79, LocalDate.of(2019, 3, 21), 79, 50.00, 50.00, null, 0.91914413, 45.96, 5672.62, 6.10, 0.00, null, null, + 1000.00); + checkInst(model, 80, 80, LocalDate.of(2019, 3, 22), 80, 50.00, 50.00, null, 0.91816370, 45.91, 5628.68, 6.06, 0.00, null, null, + 1000.00); + checkInst(model, 81, 81, LocalDate.of(2019, 3, 23), 81, 50.00, 50.00, null, 0.91718432, 45.86, 5584.69, 6.01, 0.00, null, null, + 1000.00); + checkInst(model, 82, 82, LocalDate.of(2019, 3, 24), 82, 50.00, 50.00, null, 0.91620598, 45.81, 5540.65, 5.96, 0.00, null, null, + 1000.00); + checkInst(model, 83, 83, LocalDate.of(2019, 3, 25), 83, 50.00, 50.00, null, 0.91522868, 45.76, 5496.57, 5.92, 0.00, null, null, + 1000.00); + checkInst(model, 84, 84, LocalDate.of(2019, 3, 26), 84, 50.00, 50.00, null, 0.91425243, 45.71, 5452.44, 5.87, 0.00, null, null, + 1000.00); + checkInst(model, 85, 85, LocalDate.of(2019, 3, 27), 85, 50.00, 50.00, null, 0.91327722, 45.66, 5408.26, 5.82, 0.00, null, null, + 1000.00); + checkInst(model, 86, 86, LocalDate.of(2019, 3, 28), 86, 50.00, 50.00, null, 0.91230305, 45.62, 5364.03, 5.78, 0.00, null, null, + 1000.00); + checkInst(model, 87, 87, LocalDate.of(2019, 3, 29), 87, 50.00, 50.00, null, 0.91132992, 45.57, 5319.76, 5.73, 0.00, null, null, + 1000.00); + checkInst(model, 88, 88, LocalDate.of(2019, 3, 30), 88, 50.00, 50.00, null, 0.91035783, 45.52, 5275.44, 5.68, 0.00, null, null, + 1000.00); + checkInst(model, 89, 89, LocalDate.of(2019, 3, 31), 89, 50.00, 50.00, null, 0.90938677, 45.47, 5231.08, 5.63, 0.00, null, null, + 1000.00); + checkInst(model, 90, 90, LocalDate.of(2019, 4, 1), 90, 50.00, 50.00, null, 0.90841675, 45.42, 5186.66, 5.59, 0.00, null, null, + 1000.00); + checkInst(model, 91, 91, LocalDate.of(2019, 4, 2), 91, 50.00, 50.00, null, 0.90744776, 45.37, 5142.20, 5.54, 0.00, null, null, + 1000.00); + checkInst(model, 92, 92, LocalDate.of(2019, 4, 3), 92, 50.00, 50.00, null, 0.90647981, 45.32, 5097.69, 5.49, 0.00, null, null, + 1000.00); + checkInst(model, 93, 93, LocalDate.of(2019, 4, 4), 93, 50.00, 50.00, null, 0.90551289, 45.28, 5053.13, 5.44, 0.00, null, null, + 1000.00); + checkInst(model, 94, 94, LocalDate.of(2019, 4, 5), 94, 50.00, 50.00, null, 0.90454700, 45.23, 5008.53, 5.40, 0.00, null, null, + 1000.00); + checkInst(model, 95, 95, LocalDate.of(2019, 4, 6), 95, 50.00, 50.00, null, 0.90358215, 45.18, 4963.88, 5.35, 0.00, null, null, + 1000.00); + checkInst(model, 96, 96, LocalDate.of(2019, 4, 7), 96, 50.00, 50.00, null, 0.90261832, 45.13, 4919.18, 5.30, 0.00, null, null, + 1000.00); + checkInst(model, 97, 97, LocalDate.of(2019, 4, 8), 97, 50.00, 50.00, null, 0.90165552, 45.08, 4874.43, 5.25, 0.00, null, null, + 1000.00); + checkInst(model, 98, 98, LocalDate.of(2019, 4, 9), 98, 50.00, 50.00, null, 0.90069374, 45.03, 4829.64, 5.20, 0.00, null, null, + 1000.00); + checkInst(model, 99, 99, LocalDate.of(2019, 4, 10), 99, 50.00, 50.00, null, 0.89973299, 44.99, 4784.79, 5.16, 0.00, null, null, + 1000.00); + checkInst(model, 100, 100, LocalDate.of(2019, 4, 11), 100, 50.00, 50.00, null, 0.89877327, 44.94, 4739.90, 5.11, 0.00, null, null, + 1000.00); + checkInst(model, 101, 101, LocalDate.of(2019, 4, 12), 101, 50.00, 50.00, null, 0.89781457, 44.89, 4694.96, 5.06, 0.00, null, null, + 1000.00); + checkInst(model, 102, 102, LocalDate.of(2019, 4, 13), 102, 50.00, 50.00, null, 0.89685689, 44.84, 4649.98, 5.01, 0.00, null, null, + 1000.00); + checkInst(model, 103, 103, LocalDate.of(2019, 4, 14), 103, 50.00, 50.00, null, 0.89590024, 44.80, 4604.94, 4.97, 0.00, null, null, + 1000.00); + checkInst(model, 104, 104, LocalDate.of(2019, 4, 15), 104, 50.00, 50.00, null, 0.89494460, 44.75, 4559.86, 4.92, 0.00, null, null, + 1000.00); + checkInst(model, 105, 105, LocalDate.of(2019, 4, 16), 105, 50.00, 50.00, null, 0.89398999, 44.70, 4514.73, 4.87, 0.00, null, null, + 1000.00); + checkInst(model, 106, 106, LocalDate.of(2019, 4, 17), 106, 50.00, 50.00, null, 0.89303639, 44.65, 4469.55, 4.82, 0.00, null, null, + 1000.00); + checkInst(model, 107, 107, LocalDate.of(2019, 4, 18), 107, 50.00, 50.00, null, 0.89208381, 44.60, 4424.32, 4.77, 0.00, null, null, + 1000.00); + checkInst(model, 108, 108, LocalDate.of(2019, 4, 19), 108, 50.00, 50.00, null, 0.89113225, 44.56, 4379.05, 4.72, 0.00, null, null, + 1000.00); + checkInst(model, 109, 109, LocalDate.of(2019, 4, 20), 109, 50.00, 50.00, null, 0.89018170, 44.51, 4333.72, 4.68, 0.00, null, null, + 1000.00); + checkInst(model, 110, 110, LocalDate.of(2019, 4, 21), 110, 50.00, 50.00, null, 0.88923216, 44.46, 4288.35, 4.63, 0.00, null, null, + 1000.00); + checkInst(model, 111, 111, LocalDate.of(2019, 4, 22), 111, 50.00, 50.00, null, 0.88828364, 44.41, 4242.93, 4.58, 0.00, null, null, + 1000.00); + checkInst(model, 112, 112, LocalDate.of(2019, 4, 23), 112, 50.00, 50.00, null, 0.88733613, 44.37, 4197.46, 4.53, 0.00, null, null, + 1000.00); + checkInst(model, 113, 113, LocalDate.of(2019, 4, 24), 113, 50.00, 50.00, null, 0.88638963, 44.32, 4151.94, 4.48, 0.00, null, null, + 1000.00); + checkInst(model, 114, 114, LocalDate.of(2019, 4, 25), 114, 50.00, 50.00, null, 0.88544414, 44.27, 4106.38, 4.43, 0.00, null, null, + 1000.00); + checkInst(model, 115, 115, LocalDate.of(2019, 4, 26), 115, 50.00, 50.00, null, 0.88449966, 44.22, 4060.76, 4.38, 0.00, null, null, + 1000.00); + checkInst(model, 116, 116, LocalDate.of(2019, 4, 27), 116, 50.00, 50.00, null, 0.88355619, 44.18, 4015.10, 4.34, 0.00, null, null, + 1000.00); + checkInst(model, 117, 117, LocalDate.of(2019, 4, 28), 117, 50.00, 50.00, null, 0.88261372, 44.13, 3969.38, 4.29, 0.00, null, null, + 1000.00); + checkInst(model, 118, 118, LocalDate.of(2019, 4, 29), 118, 50.00, 50.00, null, 0.88167226, 44.08, 3923.62, 4.24, 0.00, null, null, + 1000.00); + checkInst(model, 119, 119, LocalDate.of(2019, 4, 30), 119, 50.00, 50.00, null, 0.88073180, 44.04, 3877.81, 4.19, 0.00, null, null, + 1000.00); + checkInst(model, 120, 120, LocalDate.of(2019, 5, 1), 120, 50.00, 50.00, null, 0.87979234, 43.99, 3831.95, 4.14, 0.00, null, null, + 1000.00); + checkInst(model, 121, 121, LocalDate.of(2019, 5, 2), 121, 50.00, 50.00, null, 0.87885389, 43.94, 3786.04, 4.09, 0.00, null, null, + 1000.00); + checkInst(model, 122, 122, LocalDate.of(2019, 5, 3), 122, 50.00, 50.00, null, 0.87791644, 43.90, 3740.09, 4.04, 0.00, null, null, + 1000.00); + checkInst(model, 123, 123, LocalDate.of(2019, 5, 4), 123, 50.00, 50.00, null, 0.87697999, 43.85, 3694.08, 3.99, 0.00, null, null, + 1000.00); + checkInst(model, 124, 124, LocalDate.of(2019, 5, 5), 124, 50.00, 50.00, null, 0.87604453, 43.80, 3648.03, 3.94, 0.00, null, null, + 1000.00); + checkInst(model, 125, 125, LocalDate.of(2019, 5, 6), 125, 50.00, 50.00, null, 0.87511008, 43.76, 3601.92, 3.90, 0.00, null, null, + 1000.00); + checkInst(model, 126, 126, LocalDate.of(2019, 5, 7), 126, 50.00, 50.00, null, 0.87417662, 43.71, 3555.77, 3.85, 0.00, null, null, + 1000.00); + checkInst(model, 127, 127, LocalDate.of(2019, 5, 8), 127, 50.00, 50.00, null, 0.87324416, 43.66, 3509.56, 3.80, 0.00, null, null, + 1000.00); + checkInst(model, 128, 128, LocalDate.of(2019, 5, 9), 128, 50.00, 50.00, null, 0.87231269, 43.62, 3463.31, 3.75, 0.00, null, null, + 1000.00); + checkInst(model, 129, 129, LocalDate.of(2019, 5, 10), 129, 50.00, 50.00, null, 0.87138221, 43.57, 3417.01, 3.70, 0.00, null, null, + 1000.00); + checkInst(model, 130, 130, LocalDate.of(2019, 5, 11), 130, 50.00, 50.00, null, 0.87045273, 43.52, 3370.66, 3.65, 0.00, null, null, + 1000.00); + checkInst(model, 131, 131, LocalDate.of(2019, 5, 12), 131, 50.00, 50.00, null, 0.86952424, 43.48, 3324.26, 3.60, 0.00, null, null, + 1000.00); + checkInst(model, 132, 132, LocalDate.of(2019, 5, 13), 132, 50.00, 50.00, null, 0.86859674, 43.43, 3277.81, 3.55, 0.00, null, null, + 1000.00); + checkInst(model, 133, 133, LocalDate.of(2019, 5, 14), 133, 50.00, 50.00, null, 0.86767023, 43.38, 3231.31, 3.50, 0.00, null, null, + 1000.00); + checkInst(model, 134, 134, LocalDate.of(2019, 5, 15), 134, 50.00, 50.00, null, 0.86674471, 43.34, 3184.76, 3.45, 0.00, null, null, + 1000.00); + checkInst(model, 135, 135, LocalDate.of(2019, 5, 16), 135, 50.00, 50.00, null, 0.86582017, 43.29, 3138.16, 3.40, 0.00, null, null, + 1000.00); + checkInst(model, 136, 136, LocalDate.of(2019, 5, 17), 136, 50.00, 50.00, null, 0.86489662, 43.24, 3091.51, 3.35, 0.00, null, null, + 1000.00); + checkInst(model, 137, 137, LocalDate.of(2019, 5, 18), 137, 50.00, 50.00, null, 0.86397406, 43.20, 3044.81, 3.30, 0.00, null, null, + 1000.00); + checkInst(model, 138, 138, LocalDate.of(2019, 5, 19), 138, 50.00, 50.00, null, 0.86305248, 43.15, 2998.06, 3.25, 0.00, null, null, + 1000.00); + checkInst(model, 139, 139, LocalDate.of(2019, 5, 20), 139, 50.00, 50.00, null, 0.86213188, 43.11, 2951.26, 3.20, 0.00, null, null, + 1000.00); + checkInst(model, 140, 140, LocalDate.of(2019, 5, 21), 140, 50.00, 50.00, null, 0.86121227, 43.06, 2904.42, 3.15, 0.00, null, null, + 1000.00); + checkInst(model, 141, 141, LocalDate.of(2019, 5, 22), 141, 50.00, 50.00, null, 0.86029363, 43.01, 2857.52, 3.10, 0.00, null, null, + 1000.00); + checkInst(model, 142, 142, LocalDate.of(2019, 5, 23), 142, 50.00, 50.00, null, 0.85937598, 42.97, 2810.57, 3.05, 0.00, null, null, + 1000.00); + checkInst(model, 143, 143, LocalDate.of(2019, 5, 24), 143, 50.00, 50.00, null, 0.85845930, 42.92, 2763.57, 3.00, 0.00, null, null, + 1000.00); + checkInst(model, 144, 144, LocalDate.of(2019, 5, 25), 144, 50.00, 50.00, null, 0.85754361, 42.88, 2716.52, 2.95, 0.00, null, null, + 1000.00); + checkInst(model, 145, 145, LocalDate.of(2019, 5, 26), 145, 50.00, 50.00, null, 0.85662889, 42.83, 2669.42, 2.90, 0.00, null, null, + 1000.00); + checkInst(model, 146, 146, LocalDate.of(2019, 5, 27), 146, 50.00, 50.00, null, 0.85571514, 42.79, 2622.27, 2.85, 0.00, null, null, + 1000.00); + checkInst(model, 147, 147, LocalDate.of(2019, 5, 28), 147, 50.00, 50.00, null, 0.85480237, 42.74, 2575.07, 2.80, 0.00, null, null, + 1000.00); + checkInst(model, 148, 148, LocalDate.of(2019, 5, 29), 148, 50.00, 50.00, null, 0.85389057, 42.69, 2527.82, 2.75, 0.00, null, null, + 1000.00); + checkInst(model, 149, 149, LocalDate.of(2019, 5, 30), 149, 50.00, 50.00, null, 0.85297975, 42.65, 2480.52, 2.70, 0.00, null, null, + 1000.00); + checkInst(model, 150, 150, LocalDate.of(2019, 5, 31), 150, 50.00, 50.00, null, 0.85206990, 42.60, 2433.17, 2.65, 0.00, null, null, + 1000.00); + checkInst(model, 151, 151, LocalDate.of(2019, 6, 1), 151, 50.00, 50.00, null, 0.85116101, 42.56, 2385.77, 2.60, 0.00, null, null, + 1000.00); + checkInst(model, 152, 152, LocalDate.of(2019, 6, 2), 152, 50.00, 50.00, null, 0.85025310, 42.51, 2338.31, 2.55, 0.00, null, null, + 1000.00); + checkInst(model, 153, 153, LocalDate.of(2019, 6, 3), 153, 50.00, 50.00, null, 0.84934616, 42.47, 2290.81, 2.50, 0.00, null, null, + 1000.00); + checkInst(model, 154, 154, LocalDate.of(2019, 6, 4), 154, 50.00, 50.00, null, 0.84844018, 42.42, 2243.26, 2.45, 0.00, null, null, + 1000.00); + checkInst(model, 155, 155, LocalDate.of(2019, 6, 5), 155, 50.00, 50.00, null, 0.84753517, 42.38, 2195.65, 2.40, 0.00, null, null, + 1000.00); + checkInst(model, 156, 156, LocalDate.of(2019, 6, 6), 156, 50.00, 50.00, null, 0.84663113, 42.33, 2148.00, 2.34, 0.00, null, null, + 1000.00); + checkInst(model, 157, 157, LocalDate.of(2019, 6, 7), 157, 50.00, 50.00, null, 0.84572805, 42.29, 2100.29, 2.29, 0.00, null, null, + 1000.00); + checkInst(model, 158, 158, LocalDate.of(2019, 6, 8), 158, 50.00, 50.00, null, 0.84482593, 42.24, 2052.53, 2.24, 0.00, null, null, + 1000.00); + checkInst(model, 159, 159, LocalDate.of(2019, 6, 9), 159, 50.00, 50.00, null, 0.84392477, 42.20, 2004.73, 2.19, 0.00, null, null, + 1000.00); + checkInst(model, 160, 160, LocalDate.of(2019, 6, 10), 160, 50.00, 50.00, null, 0.84302458, 42.15, 1956.87, 2.14, 0.00, null, null, + 1000.00); + checkInst(model, 161, 161, LocalDate.of(2019, 6, 11), 161, 50.00, 50.00, null, 0.84212535, 42.11, 1908.96, 2.09, 0.00, null, null, + 1000.00); + checkInst(model, 162, 162, LocalDate.of(2019, 6, 12), 162, 50.00, 50.00, null, 0.84122707, 42.06, 1860.99, 2.04, 0.00, null, null, + 1000.00); + checkInst(model, 163, 163, LocalDate.of(2019, 6, 13), 163, 50.00, 50.00, null, 0.84032975, 42.02, 1812.98, 1.99, 0.00, null, null, + 1000.00); + checkInst(model, 164, 164, LocalDate.of(2019, 6, 14), 164, 50.00, 50.00, null, 0.83943340, 41.97, 1764.92, 1.94, 0.00, null, null, + 1000.00); + checkInst(model, 165, 165, LocalDate.of(2019, 6, 15), 165, 50.00, 50.00, null, 0.83853799, 41.93, 1716.80, 1.88, 0.00, null, null, + 1000.00); + checkInst(model, 166, 166, LocalDate.of(2019, 6, 16), 166, 50.00, 50.00, null, 0.83764354, 41.88, 1668.64, 1.83, 0.00, null, null, + 1000.00); + checkInst(model, 167, 167, LocalDate.of(2019, 6, 17), 167, 50.00, 50.00, null, 0.83675005, 41.84, 1620.42, 1.78, 0.00, null, null, + 1000.00); + checkInst(model, 168, 168, LocalDate.of(2019, 6, 18), 168, 50.00, 50.00, null, 0.83585751, 41.79, 1572.15, 1.73, 0.00, null, null, + 1000.00); + checkInst(model, 169, 169, LocalDate.of(2019, 6, 19), 169, 50.00, 50.00, null, 0.83496592, 41.75, 1523.83, 1.68, 0.00, null, null, + 1000.00); + checkInst(model, 170, 170, LocalDate.of(2019, 6, 20), 170, 50.00, 50.00, null, 0.83407528, 41.70, 1475.45, 1.63, 0.00, null, null, + 1000.00); + checkInst(model, 171, 171, LocalDate.of(2019, 6, 21), 171, 50.00, 50.00, null, 0.83318560, 41.66, 1427.03, 1.58, 0.00, null, null, + 1000.00); + checkInst(model, 172, 172, LocalDate.of(2019, 6, 22), 172, 50.00, 50.00, null, 0.83229686, 41.61, 1378.55, 1.52, 0.00, null, null, + 1000.00); + checkInst(model, 173, 173, LocalDate.of(2019, 6, 23), 173, 50.00, 50.00, null, 0.83140907, 41.57, 1330.02, 1.47, 0.00, null, null, + 1000.00); + checkInst(model, 174, 174, LocalDate.of(2019, 6, 24), 174, 50.00, 50.00, null, 0.83052222, 41.53, 1281.45, 1.42, 0.00, null, null, + 1000.00); + checkInst(model, 175, 175, LocalDate.of(2019, 6, 25), 175, 50.00, 50.00, null, 0.82963633, 41.48, 1232.81, 1.37, 0.00, null, null, + 1000.00); + checkInst(model, 176, 176, LocalDate.of(2019, 6, 26), 176, 50.00, 50.00, null, 0.82875137, 41.44, 1184.13, 1.32, 0.00, null, null, + 1000.00); + checkInst(model, 177, 177, LocalDate.of(2019, 6, 27), 177, 50.00, 50.00, null, 0.82786736, 41.39, 1135.39, 1.26, 0.00, null, null, + 1000.00); + checkInst(model, 178, 178, LocalDate.of(2019, 6, 28), 178, 50.00, 50.00, null, 0.82698430, 41.35, 1086.61, 1.21, 0.00, null, null, + 1000.00); + checkInst(model, 179, 179, LocalDate.of(2019, 6, 29), 179, 50.00, 50.00, null, 0.82610217, 41.31, 1037.77, 1.16, 0.00, null, null, + 1000.00); + checkInst(model, 180, 180, LocalDate.of(2019, 6, 30), 180, 50.00, 50.00, null, 0.82522099, 41.26, 988.88, 1.11, 0.00, null, null, + 1000.00); + checkInst(model, 181, 181, LocalDate.of(2019, 7, 1), 181, 50.00, 50.00, null, 0.82434075, 41.22, 939.93, 1.06, 0.00, null, null, + 1000.00); + checkInst(model, 182, 182, LocalDate.of(2019, 7, 2), 182, 50.00, 50.00, null, 0.82346144, 41.17, 890.93, 1.00, 0.00, null, null, + 1000.00); + checkInst(model, 183, 183, LocalDate.of(2019, 7, 3), 183, 50.00, 50.00, null, 0.82258308, 41.13, 841.89, 0.95, 0.00, null, null, + 1000.00); + checkInst(model, 184, 184, LocalDate.of(2019, 7, 4), 184, 50.00, 50.00, null, 0.82170565, 41.09, 792.79, 0.90, 0.00, null, null, + 1000.00); + checkInst(model, 185, 185, LocalDate.of(2019, 7, 5), 185, 50.00, 50.00, null, 0.82082916, 41.04, 743.63, 0.85, 0.00, null, null, + 1000.00); + checkInst(model, 186, 186, LocalDate.of(2019, 7, 6), 186, 50.00, 50.00, null, 0.81995360, 41.00, 694.43, 0.79, 0.00, null, null, + 1000.00); + checkInst(model, 187, 187, LocalDate.of(2019, 7, 7), 187, 50.00, 50.00, null, 0.81907897, 40.95, 645.17, 0.74, 0.00, null, null, + 1000.00); + checkInst(model, 188, 188, LocalDate.of(2019, 7, 8), 188, 50.00, 50.00, null, 0.81820528, 40.91, 595.86, 0.69, 0.00, null, null, + 1000.00); + checkInst(model, 189, 189, LocalDate.of(2019, 7, 9), 189, 50.00, 50.00, null, 0.81733252, 40.87, 546.49, 0.64, 0.00, null, null, + 1000.00); + checkInst(model, 190, 190, LocalDate.of(2019, 7, 10), 190, 50.00, 50.00, null, 0.81646069, 40.82, 497.08, 0.58, 0.00, null, null, + 1000.00); + checkInst(model, 191, 191, LocalDate.of(2019, 7, 11), 191, 50.00, 50.00, null, 0.81558979, 40.78, 447.61, 0.53, 0.00, null, null, + 1000.00); + checkInst(model, 192, 192, LocalDate.of(2019, 7, 12), 192, 50.00, 50.00, null, 0.81471983, 40.74, 398.08, 0.48, 0.00, null, null, + 1000.00); + checkInst(model, 193, 193, LocalDate.of(2019, 7, 13), 193, 50.00, 50.00, null, 0.81385078, 40.69, 348.51, 0.43, 0.00, null, null, + 1000.00); + checkInst(model, 194, 194, LocalDate.of(2019, 7, 14), 194, 50.00, 50.00, null, 0.81298267, 40.65, 298.88, 0.37, 0.00, null, null, + 1000.00); + checkInst(model, 195, 195, LocalDate.of(2019, 7, 15), 195, 50.00, 50.00, null, 0.81211548, 40.61, 249.20, 0.32, 0.00, null, null, + 1000.00); + checkInst(model, 196, 196, LocalDate.of(2019, 7, 16), 196, 50.00, 50.00, null, 0.81124922, 40.56, 199.47, 0.27, 0.00, null, null, + 1000.00); + checkInst(model, 197, 197, LocalDate.of(2019, 7, 17), 197, 50.00, 50.00, null, 0.81038388, 40.52, 149.68, 0.21, 0.00, null, null, + 1000.00); + checkInst(model, 198, 198, LocalDate.of(2019, 7, 18), 198, 50.00, 50.00, null, 0.80951946, 40.48, 99.84, 0.16, 0.00, null, null, + 1000.00); + checkInst(model, 199, 199, LocalDate.of(2019, 7, 19), 199, 50.00, 50.00, null, 0.80865597, 40.43, 49.95, 0.11, 0.00, null, null, + 1000.00); + checkInst(model, 200, 200, LocalDate.of(2019, 7, 20), 200, 50.00, 50.00, null, 0.80779339, 40.39, 0.00, 0.05, 0.00, null, null, + 1000.00); + } + + @Test + void testNoDiscountLoan_term180_discountFee0_netDisbursement9000() { + final BigDecimal zeroDiscount = BigDecimal.ZERO; + final ProjectedAmortizationScheduleModel model = ProjectedAmortizationScheduleModel.generate(zeroDiscount, NET_DISBURSEMENT, TPV, + RATE, DAY_COUNT, EXPECTED_DISBURSEMENT_DATE, MC, CURRENCY); + + assertEquals(180, model.originalPaymentNumber(), "loanTerm = ceil(9000/50) = 180"); + assertEquals(BigDecimal.ZERO, model.effectiveInterestRate(), "EIR should be 0 when no discount fee"); + assertEquals(181, model.projectedPayments().size(), "disbursement + 180 periods"); + + // All discount factors should be 1.0 (EIR = 0) + for (int i = 1; i < model.projectedPayments().size(); i++) { + final ProjectedPayment p = model.projectedPayments().get(i); + assertEquals(0, BigDecimal.ONE.compareTo(p.discountFactor()), "period " + i + ": discount factor should be 1.0"); + assertMoneyValue(50.00, p.npvValue(), 2, "period " + i + ": NPV should equal payment (DF=1)"); + } + + // Deferred balance should be 0 throughout (no discount to defer) + for (int i = 0; i < model.projectedPayments().size(); i++) { + final ProjectedPayment p = model.projectedPayments().get(i); + if (p.deferredBalance() != null) { + assertMoneyValue(0.00, p.deferredBalance(), 2, "period " + i + ": deferred balance should be 0"); + } + } + + // Last period balance should be 0 + final ProjectedPayment last = model.projectedPayments().getLast(); + assertMoneyValue(0.00, last.balance(), 2, "last period balance should be 0"); + } + + @Test + void testOnTimePayment_term200_discountFee1000_netDisbursement9000_pay50_50() { + final ProjectedAmortizationScheduleModel model = generateModel(); + + calculator.applyPayment(model, EXPECTED_DISBURSEMENT_DATE.plusDays(1), new BigDecimal("50")); + calculator.applyPayment(model, EXPECTED_DISBURSEMENT_DATE.plusDays(2), new BigDecimal("50")); + + checkInst(model, 0, 0, EXPECTED_DISBURSEMENT_DATE, 0, -9000.00, null, null, 1.00000000, -9000.00, 9000.00, null, null, null, null, + 1000.00); + + checkInst(model, 1, 1, LocalDate.of(2019, 1, 2), 0, 50.00, 50.00, 50.00, 1.00000000, 50.00, 8959.61, 9.61, 19.18, 9.61, 0.00, + 990.39); + checkInst(model, 2, 2, LocalDate.of(2019, 1, 3), 0, 50.00, 50.00, 50.00, 1.00000000, 50.00, 8919.18, 9.57, 9.57, 9.57, 0.00, + 980.82); + checkInst(model, 3, 3, LocalDate.of(2019, 1, 4), 1, 50.00, 50.00, null, 0.99893332, 49.95, 8878.70, 9.52, 0.00, null, null, 980.82); + checkInst(model, 4, 4, LocalDate.of(2019, 1, 5), 2, 50.00, 50.00, null, 0.99786779, 49.89, 8838.18, 9.48, 0.00, null, null, 980.82); + checkInst(model, 5, 5, LocalDate.of(2019, 1, 6), 3, 50.00, 50.00, null, 0.99680339, 49.84, 8797.62, 9.44, 0.00, null, null, 980.82); + checkInst(model, 6, 6, LocalDate.of(2019, 1, 7), 4, 50.00, 50.00, null, 0.99574012, 49.79, 8757.01, 9.39, 0.00, null, null, 980.82); + checkInst(model, 7, 7, LocalDate.of(2019, 1, 8), 5, 50.00, 50.00, null, 0.99467799, 49.73, 8716.36, 9.35, 0.00, null, null, 980.82); + checkInst(model, 8, 8, LocalDate.of(2019, 1, 9), 6, 50.00, 50.00, null, 0.99361699, 49.68, 8675.67, 9.31, 0.00, null, null, 980.82); + checkInst(model, 9, 9, LocalDate.of(2019, 1, 10), 7, 50.00, 50.00, null, 0.99255712, 49.63, 8634.94, 9.26, 0.00, null, null, + 980.82); + checkInst(model, 10, 10, LocalDate.of(2019, 1, 11), 8, 50.00, 50.00, null, 0.99149839, 49.57, 8594.16, 9.22, 0.00, null, null, + 980.82); + checkInst(model, 11, 11, LocalDate.of(2019, 1, 12), 9, 50.00, 50.00, null, 0.99044078, 49.52, 8553.33, 9.18, 0.00, null, null, + 980.82); + checkInst(model, 12, 12, LocalDate.of(2019, 1, 13), 10, 50.00, 50.00, null, 0.98938430, 49.47, 8512.47, 9.13, 0.00, null, null, + 980.82); + checkInst(model, 13, 13, LocalDate.of(2019, 1, 14), 11, 50.00, 50.00, null, 0.98832895, 49.42, 8471.56, 9.09, 0.00, null, null, + 980.82); + checkInst(model, 14, 14, LocalDate.of(2019, 1, 15), 12, 50.00, 50.00, null, 0.98727472, 49.36, 8430.60, 9.05, 0.00, null, null, + 980.82); + checkInst(model, 15, 15, LocalDate.of(2019, 1, 16), 13, 50.00, 50.00, null, 0.98622162, 49.31, 8389.61, 9.00, 0.00, null, null, + 980.82); + checkInst(model, 16, 16, LocalDate.of(2019, 1, 17), 14, 50.00, 50.00, null, 0.98516964, 49.26, 8348.56, 8.96, 0.00, null, null, + 980.82); + checkInst(model, 17, 17, LocalDate.of(2019, 1, 18), 15, 50.00, 50.00, null, 0.98411879, 49.21, 8307.48, 8.91, 0.00, null, null, + 980.82); + checkInst(model, 18, 18, LocalDate.of(2019, 1, 19), 16, 50.00, 50.00, null, 0.98306905, 49.15, 8266.35, 8.87, 0.00, null, null, + 980.82); + checkInst(model, 19, 19, LocalDate.of(2019, 1, 20), 17, 50.00, 50.00, null, 0.98202044, 49.10, 8225.18, 8.83, 0.00, null, null, + 980.82); + checkInst(model, 20, 20, LocalDate.of(2019, 1, 21), 18, 50.00, 50.00, null, 0.98097294, 49.05, 8183.96, 8.78, 0.00, null, null, + 980.82); + checkInst(model, 21, 21, LocalDate.of(2019, 1, 22), 19, 50.00, 50.00, null, 0.97992656, 49.00, 8142.70, 8.74, 0.00, null, null, + 980.82); + checkInst(model, 22, 22, LocalDate.of(2019, 1, 23), 20, 50.00, 50.00, null, 0.97888129, 48.94, 8101.39, 8.69, 0.00, null, null, + 980.82); + checkInst(model, 23, 23, LocalDate.of(2019, 1, 24), 21, 50.00, 50.00, null, 0.97783715, 48.89, 8060.04, 8.65, 0.00, null, null, + 980.82); + checkInst(model, 24, 24, LocalDate.of(2019, 1, 25), 22, 50.00, 50.00, null, 0.97679411, 48.84, 8018.65, 8.61, 0.00, null, null, + 980.82); + checkInst(model, 25, 25, LocalDate.of(2019, 1, 26), 23, 50.00, 50.00, null, 0.97575219, 48.79, 7977.21, 8.56, 0.00, null, null, + 980.82); + checkInst(model, 26, 26, LocalDate.of(2019, 1, 27), 24, 50.00, 50.00, null, 0.97471138, 48.74, 7935.73, 8.52, 0.00, null, null, + 980.82); + checkInst(model, 27, 27, LocalDate.of(2019, 1, 28), 25, 50.00, 50.00, null, 0.97367168, 48.68, 7894.21, 8.47, 0.00, null, null, + 980.82); + checkInst(model, 28, 28, LocalDate.of(2019, 1, 29), 26, 50.00, 50.00, null, 0.97263309, 48.63, 7852.63, 8.43, 0.00, null, null, + 980.82); + checkInst(model, 29, 29, LocalDate.of(2019, 1, 30), 27, 50.00, 50.00, null, 0.97159560, 48.58, 7811.02, 8.39, 0.00, null, null, + 980.82); + checkInst(model, 30, 30, LocalDate.of(2019, 1, 31), 28, 50.00, 50.00, null, 0.97055922, 48.53, 7769.36, 8.34, 0.00, null, null, + 980.82); + checkInst(model, 31, 31, LocalDate.of(2019, 2, 1), 29, 50.00, 50.00, null, 0.96952395, 48.48, 7727.66, 8.30, 0.00, null, null, + 980.82); + checkInst(model, 32, 32, LocalDate.of(2019, 2, 2), 30, 50.00, 50.00, null, 0.96848979, 48.42, 7685.91, 8.25, 0.00, null, null, + 980.82); + checkInst(model, 33, 33, LocalDate.of(2019, 2, 3), 31, 50.00, 50.00, null, 0.96745672, 48.37, 7644.12, 8.21, 0.00, null, null, + 980.82); + checkInst(model, 34, 34, LocalDate.of(2019, 2, 4), 32, 50.00, 50.00, null, 0.96642476, 48.32, 7602.28, 8.16, 0.00, null, null, + 980.82); + checkInst(model, 35, 35, LocalDate.of(2019, 2, 5), 33, 50.00, 50.00, null, 0.96539390, 48.27, 7560.40, 8.12, 0.00, null, null, + 980.82); + checkInst(model, 36, 36, LocalDate.of(2019, 2, 6), 34, 50.00, 50.00, null, 0.96436413, 48.22, 7518.47, 8.07, 0.00, null, null, + 980.82); + checkInst(model, 37, 37, LocalDate.of(2019, 2, 7), 35, 50.00, 50.00, null, 0.96333547, 48.17, 7476.50, 8.03, 0.00, null, null, + 980.82); + checkInst(model, 38, 38, LocalDate.of(2019, 2, 8), 36, 50.00, 50.00, null, 0.96230790, 48.12, 7434.48, 7.98, 0.00, null, null, + 980.82); + checkInst(model, 39, 39, LocalDate.of(2019, 2, 9), 37, 50.00, 50.00, null, 0.96128143, 48.06, 7392.42, 7.94, 0.00, null, null, + 980.82); + checkInst(model, 40, 40, LocalDate.of(2019, 2, 10), 38, 50.00, 50.00, null, 0.96025606, 48.01, 7350.31, 7.89, 0.00, null, null, + 980.82); + checkInst(model, 41, 41, LocalDate.of(2019, 2, 11), 39, 50.00, 50.00, null, 0.95923178, 47.96, 7308.16, 7.85, 0.00, null, null, + 980.82); + checkInst(model, 42, 42, LocalDate.of(2019, 2, 12), 40, 50.00, 50.00, null, 0.95820859, 47.91, 7265.97, 7.80, 0.00, null, null, + 980.82); + checkInst(model, 43, 43, LocalDate.of(2019, 2, 13), 41, 50.00, 50.00, null, 0.95718649, 47.86, 7223.72, 7.76, 0.00, null, null, + 980.82); + checkInst(model, 44, 44, LocalDate.of(2019, 2, 14), 42, 50.00, 50.00, null, 0.95616548, 47.81, 7181.44, 7.71, 0.00, null, null, + 980.82); + checkInst(model, 45, 45, LocalDate.of(2019, 2, 15), 43, 50.00, 50.00, null, 0.95514557, 47.76, 7139.11, 7.67, 0.00, null, null, + 980.82); + checkInst(model, 46, 46, LocalDate.of(2019, 2, 16), 44, 50.00, 50.00, null, 0.95412674, 47.71, 7096.73, 7.62, 0.00, null, null, + 980.82); + checkInst(model, 47, 47, LocalDate.of(2019, 2, 17), 45, 50.00, 50.00, null, 0.95310899, 47.66, 7054.31, 7.58, 0.00, null, null, + 980.82); + checkInst(model, 48, 48, LocalDate.of(2019, 2, 18), 46, 50.00, 50.00, null, 0.95209233, 47.60, 7011.84, 7.53, 0.00, null, null, + 980.82); + checkInst(model, 49, 49, LocalDate.of(2019, 2, 19), 47, 50.00, 50.00, null, 0.95107676, 47.55, 6969.33, 7.49, 0.00, null, null, + 980.82); + checkInst(model, 50, 50, LocalDate.of(2019, 2, 20), 48, 50.00, 50.00, null, 0.95006227, 47.50, 6926.77, 7.44, 0.00, null, null, + 980.82); + checkInst(model, 51, 51, LocalDate.of(2019, 2, 21), 49, 50.00, 50.00, null, 0.94904886, 47.45, 6884.17, 7.40, 0.00, null, null, + 980.82); + checkInst(model, 52, 52, LocalDate.of(2019, 2, 22), 50, 50.00, 50.00, null, 0.94803653, 47.40, 6841.52, 7.35, 0.00, null, null, + 980.82); + checkInst(model, 53, 53, LocalDate.of(2019, 2, 23), 51, 50.00, 50.00, null, 0.94702529, 47.35, 6798.82, 7.31, 0.00, null, null, + 980.82); + checkInst(model, 54, 54, LocalDate.of(2019, 2, 24), 52, 50.00, 50.00, null, 0.94601512, 47.30, 6756.08, 7.26, 0.00, null, null, + 980.82); + checkInst(model, 55, 55, LocalDate.of(2019, 2, 25), 53, 50.00, 50.00, null, 0.94500603, 47.25, 6713.30, 7.21, 0.00, null, null, + 980.82); + checkInst(model, 56, 56, LocalDate.of(2019, 2, 26), 54, 50.00, 50.00, null, 0.94399801, 47.20, 6670.47, 7.17, 0.00, null, null, + 980.82); + checkInst(model, 57, 57, LocalDate.of(2019, 2, 27), 55, 50.00, 50.00, null, 0.94299107, 47.15, 6627.59, 7.12, 0.00, null, null, + 980.82); + checkInst(model, 58, 58, LocalDate.of(2019, 2, 28), 56, 50.00, 50.00, null, 0.94198521, 47.10, 6584.67, 7.08, 0.00, null, null, + 980.82); + checkInst(model, 59, 59, LocalDate.of(2019, 3, 1), 57, 50.00, 50.00, null, 0.94098042, 47.05, 6541.70, 7.03, 0.00, null, null, + 980.82); + checkInst(model, 60, 60, LocalDate.of(2019, 3, 2), 58, 50.00, 50.00, null, 0.93997669, 47.00, 6498.68, 6.99, 0.00, null, null, + 980.82); + checkInst(model, 61, 61, LocalDate.of(2019, 3, 3), 59, 50.00, 50.00, null, 0.93897404, 46.95, 6455.62, 6.94, 0.00, null, null, + 980.82); + checkInst(model, 62, 62, LocalDate.of(2019, 3, 4), 60, 50.00, 50.00, null, 0.93797246, 46.90, 6412.51, 6.89, 0.00, null, null, + 980.82); + checkInst(model, 63, 63, LocalDate.of(2019, 3, 5), 61, 50.00, 50.00, null, 0.93697195, 46.85, 6369.36, 6.85, 0.00, null, null, + 980.82); + checkInst(model, 64, 64, LocalDate.of(2019, 3, 6), 62, 50.00, 50.00, null, 0.93597251, 46.80, 6326.16, 6.80, 0.00, null, null, + 980.82); + checkInst(model, 65, 65, LocalDate.of(2019, 3, 7), 63, 50.00, 50.00, null, 0.93497413, 46.75, 6282.92, 6.76, 0.00, null, null, + 980.82); + checkInst(model, 66, 66, LocalDate.of(2019, 3, 8), 64, 50.00, 50.00, null, 0.93397681, 46.70, 6239.63, 6.71, 0.00, null, null, + 980.82); + checkInst(model, 67, 67, LocalDate.of(2019, 3, 9), 65, 50.00, 50.00, null, 0.93298056, 46.65, 6196.29, 6.66, 0.00, null, null, + 980.82); + checkInst(model, 68, 68, LocalDate.of(2019, 3, 10), 66, 50.00, 50.00, null, 0.93198538, 46.60, 6152.91, 6.62, 0.00, null, null, + 980.82); + checkInst(model, 69, 69, LocalDate.of(2019, 3, 11), 67, 50.00, 50.00, null, 0.93099125, 46.55, 6109.48, 6.57, 0.00, null, null, + 980.82); + checkInst(model, 70, 70, LocalDate.of(2019, 3, 12), 68, 50.00, 50.00, null, 0.92999818, 46.50, 6066.00, 6.52, 0.00, null, null, + 980.82); + checkInst(model, 71, 71, LocalDate.of(2019, 3, 13), 69, 50.00, 50.00, null, 0.92900618, 46.45, 6022.48, 6.48, 0.00, null, null, + 980.82); + checkInst(model, 72, 72, LocalDate.of(2019, 3, 14), 70, 50.00, 50.00, null, 0.92801523, 46.40, 5978.91, 6.43, 0.00, null, null, + 980.82); + checkInst(model, 73, 73, LocalDate.of(2019, 3, 15), 71, 50.00, 50.00, null, 0.92702534, 46.35, 5935.29, 6.38, 0.00, null, null, + 980.82); + checkInst(model, 74, 74, LocalDate.of(2019, 3, 16), 72, 50.00, 50.00, null, 0.92603650, 46.30, 5891.63, 6.34, 0.00, null, null, + 980.82); + checkInst(model, 75, 75, LocalDate.of(2019, 3, 17), 73, 50.00, 50.00, null, 0.92504872, 46.25, 5847.92, 6.29, 0.00, null, null, + 980.82); + checkInst(model, 76, 76, LocalDate.of(2019, 3, 18), 74, 50.00, 50.00, null, 0.92406200, 46.20, 5804.17, 6.24, 0.00, null, null, + 980.82); + checkInst(model, 77, 77, LocalDate.of(2019, 3, 19), 75, 50.00, 50.00, null, 0.92307632, 46.15, 5760.36, 6.20, 0.00, null, null, + 980.82); + checkInst(model, 78, 78, LocalDate.of(2019, 3, 20), 76, 50.00, 50.00, null, 0.92209170, 46.10, 5716.52, 6.15, 0.00, null, null, + 980.82); + checkInst(model, 79, 79, LocalDate.of(2019, 3, 21), 77, 50.00, 50.00, null, 0.92110813, 46.06, 5672.62, 6.10, 0.00, null, null, + 980.82); + checkInst(model, 80, 80, LocalDate.of(2019, 3, 22), 78, 50.00, 50.00, null, 0.92012560, 46.01, 5628.68, 6.06, 0.00, null, null, + 980.82); + checkInst(model, 81, 81, LocalDate.of(2019, 3, 23), 79, 50.00, 50.00, null, 0.91914413, 45.96, 5584.69, 6.01, 0.00, null, null, + 980.82); + checkInst(model, 82, 82, LocalDate.of(2019, 3, 24), 80, 50.00, 50.00, null, 0.91816370, 45.91, 5540.65, 5.96, 0.00, null, null, + 980.82); + checkInst(model, 83, 83, LocalDate.of(2019, 3, 25), 81, 50.00, 50.00, null, 0.91718432, 45.86, 5496.57, 5.92, 0.00, null, null, + 980.82); + checkInst(model, 84, 84, LocalDate.of(2019, 3, 26), 82, 50.00, 50.00, null, 0.91620598, 45.81, 5452.44, 5.87, 0.00, null, null, + 980.82); + checkInst(model, 85, 85, LocalDate.of(2019, 3, 27), 83, 50.00, 50.00, null, 0.91522868, 45.76, 5408.26, 5.82, 0.00, null, null, + 980.82); + checkInst(model, 86, 86, LocalDate.of(2019, 3, 28), 84, 50.00, 50.00, null, 0.91425243, 45.71, 5364.03, 5.78, 0.00, null, null, + 980.82); + checkInst(model, 87, 87, LocalDate.of(2019, 3, 29), 85, 50.00, 50.00, null, 0.91327722, 45.66, 5319.76, 5.73, 0.00, null, null, + 980.82); + checkInst(model, 88, 88, LocalDate.of(2019, 3, 30), 86, 50.00, 50.00, null, 0.91230305, 45.62, 5275.44, 5.68, 0.00, null, null, + 980.82); + checkInst(model, 89, 89, LocalDate.of(2019, 3, 31), 87, 50.00, 50.00, null, 0.91132992, 45.57, 5231.08, 5.63, 0.00, null, null, + 980.82); + checkInst(model, 90, 90, LocalDate.of(2019, 4, 1), 88, 50.00, 50.00, null, 0.91035783, 45.52, 5186.66, 5.59, 0.00, null, null, + 980.82); + checkInst(model, 91, 91, LocalDate.of(2019, 4, 2), 89, 50.00, 50.00, null, 0.90938677, 45.47, 5142.20, 5.54, 0.00, null, null, + 980.82); + checkInst(model, 92, 92, LocalDate.of(2019, 4, 3), 90, 50.00, 50.00, null, 0.90841675, 45.42, 5097.69, 5.49, 0.00, null, null, + 980.82); + checkInst(model, 93, 93, LocalDate.of(2019, 4, 4), 91, 50.00, 50.00, null, 0.90744776, 45.37, 5053.13, 5.44, 0.00, null, null, + 980.82); + checkInst(model, 94, 94, LocalDate.of(2019, 4, 5), 92, 50.00, 50.00, null, 0.90647981, 45.32, 5008.53, 5.40, 0.00, null, null, + 980.82); + checkInst(model, 95, 95, LocalDate.of(2019, 4, 6), 93, 50.00, 50.00, null, 0.90551289, 45.28, 4963.88, 5.35, 0.00, null, null, + 980.82); + checkInst(model, 96, 96, LocalDate.of(2019, 4, 7), 94, 50.00, 50.00, null, 0.90454700, 45.23, 4919.18, 5.30, 0.00, null, null, + 980.82); + checkInst(model, 97, 97, LocalDate.of(2019, 4, 8), 95, 50.00, 50.00, null, 0.90358215, 45.18, 4874.43, 5.25, 0.00, null, null, + 980.82); + checkInst(model, 98, 98, LocalDate.of(2019, 4, 9), 96, 50.00, 50.00, null, 0.90261832, 45.13, 4829.64, 5.20, 0.00, null, null, + 980.82); + checkInst(model, 99, 99, LocalDate.of(2019, 4, 10), 97, 50.00, 50.00, null, 0.90165552, 45.08, 4784.79, 5.16, 0.00, null, null, + 980.82); + checkInst(model, 100, 100, LocalDate.of(2019, 4, 11), 98, 50.00, 50.00, null, 0.90069374, 45.03, 4739.90, 5.11, 0.00, null, null, + 980.82); + checkInst(model, 101, 101, LocalDate.of(2019, 4, 12), 99, 50.00, 50.00, null, 0.89973299, 44.99, 4694.96, 5.06, 0.00, null, null, + 980.82); + checkInst(model, 102, 102, LocalDate.of(2019, 4, 13), 100, 50.00, 50.00, null, 0.89877327, 44.94, 4649.98, 5.01, 0.00, null, null, + 980.82); + checkInst(model, 103, 103, LocalDate.of(2019, 4, 14), 101, 50.00, 50.00, null, 0.89781457, 44.89, 4604.94, 4.97, 0.00, null, null, + 980.82); + checkInst(model, 104, 104, LocalDate.of(2019, 4, 15), 102, 50.00, 50.00, null, 0.89685689, 44.84, 4559.86, 4.92, 0.00, null, null, + 980.82); + checkInst(model, 105, 105, LocalDate.of(2019, 4, 16), 103, 50.00, 50.00, null, 0.89590024, 44.80, 4514.73, 4.87, 0.00, null, null, + 980.82); + checkInst(model, 106, 106, LocalDate.of(2019, 4, 17), 104, 50.00, 50.00, null, 0.89494460, 44.75, 4469.55, 4.82, 0.00, null, null, + 980.82); + checkInst(model, 107, 107, LocalDate.of(2019, 4, 18), 105, 50.00, 50.00, null, 0.89398999, 44.70, 4424.32, 4.77, 0.00, null, null, + 980.82); + checkInst(model, 108, 108, LocalDate.of(2019, 4, 19), 106, 50.00, 50.00, null, 0.89303639, 44.65, 4379.05, 4.72, 0.00, null, null, + 980.82); + checkInst(model, 109, 109, LocalDate.of(2019, 4, 20), 107, 50.00, 50.00, null, 0.89208381, 44.60, 4333.72, 4.68, 0.00, null, null, + 980.82); + checkInst(model, 110, 110, LocalDate.of(2019, 4, 21), 108, 50.00, 50.00, null, 0.89113225, 44.56, 4288.35, 4.63, 0.00, null, null, + 980.82); + checkInst(model, 111, 111, LocalDate.of(2019, 4, 22), 109, 50.00, 50.00, null, 0.89018170, 44.51, 4242.93, 4.58, 0.00, null, null, + 980.82); + checkInst(model, 112, 112, LocalDate.of(2019, 4, 23), 110, 50.00, 50.00, null, 0.88923216, 44.46, 4197.46, 4.53, 0.00, null, null, + 980.82); + checkInst(model, 113, 113, LocalDate.of(2019, 4, 24), 111, 50.00, 50.00, null, 0.88828364, 44.41, 4151.94, 4.48, 0.00, null, null, + 980.82); + checkInst(model, 114, 114, LocalDate.of(2019, 4, 25), 112, 50.00, 50.00, null, 0.88733613, 44.37, 4106.38, 4.43, 0.00, null, null, + 980.82); + checkInst(model, 115, 115, LocalDate.of(2019, 4, 26), 113, 50.00, 50.00, null, 0.88638963, 44.32, 4060.76, 4.38, 0.00, null, null, + 980.82); + checkInst(model, 116, 116, LocalDate.of(2019, 4, 27), 114, 50.00, 50.00, null, 0.88544414, 44.27, 4015.10, 4.34, 0.00, null, null, + 980.82); + checkInst(model, 117, 117, LocalDate.of(2019, 4, 28), 115, 50.00, 50.00, null, 0.88449966, 44.22, 3969.38, 4.29, 0.00, null, null, + 980.82); + checkInst(model, 118, 118, LocalDate.of(2019, 4, 29), 116, 50.00, 50.00, null, 0.88355619, 44.18, 3923.62, 4.24, 0.00, null, null, + 980.82); + checkInst(model, 119, 119, LocalDate.of(2019, 4, 30), 117, 50.00, 50.00, null, 0.88261372, 44.13, 3877.81, 4.19, 0.00, null, null, + 980.82); + checkInst(model, 120, 120, LocalDate.of(2019, 5, 1), 118, 50.00, 50.00, null, 0.88167226, 44.08, 3831.95, 4.14, 0.00, null, null, + 980.82); + checkInst(model, 121, 121, LocalDate.of(2019, 5, 2), 119, 50.00, 50.00, null, 0.88073180, 44.04, 3786.04, 4.09, 0.00, null, null, + 980.82); + checkInst(model, 122, 122, LocalDate.of(2019, 5, 3), 120, 50.00, 50.00, null, 0.87979234, 43.99, 3740.09, 4.04, 0.00, null, null, + 980.82); + checkInst(model, 123, 123, LocalDate.of(2019, 5, 4), 121, 50.00, 50.00, null, 0.87885389, 43.94, 3694.08, 3.99, 0.00, null, null, + 980.82); + checkInst(model, 124, 124, LocalDate.of(2019, 5, 5), 122, 50.00, 50.00, null, 0.87791644, 43.90, 3648.03, 3.94, 0.00, null, null, + 980.82); + checkInst(model, 125, 125, LocalDate.of(2019, 5, 6), 123, 50.00, 50.00, null, 0.87697999, 43.85, 3601.92, 3.90, 0.00, null, null, + 980.82); + checkInst(model, 126, 126, LocalDate.of(2019, 5, 7), 124, 50.00, 50.00, null, 0.87604453, 43.80, 3555.77, 3.85, 0.00, null, null, + 980.82); + checkInst(model, 127, 127, LocalDate.of(2019, 5, 8), 125, 50.00, 50.00, null, 0.87511008, 43.76, 3509.56, 3.80, 0.00, null, null, + 980.82); + checkInst(model, 128, 128, LocalDate.of(2019, 5, 9), 126, 50.00, 50.00, null, 0.87417662, 43.71, 3463.31, 3.75, 0.00, null, null, + 980.82); + checkInst(model, 129, 129, LocalDate.of(2019, 5, 10), 127, 50.00, 50.00, null, 0.87324416, 43.66, 3417.01, 3.70, 0.00, null, null, + 980.82); + checkInst(model, 130, 130, LocalDate.of(2019, 5, 11), 128, 50.00, 50.00, null, 0.87231269, 43.62, 3370.66, 3.65, 0.00, null, null, + 980.82); + checkInst(model, 131, 131, LocalDate.of(2019, 5, 12), 129, 50.00, 50.00, null, 0.87138221, 43.57, 3324.26, 3.60, 0.00, null, null, + 980.82); + checkInst(model, 132, 132, LocalDate.of(2019, 5, 13), 130, 50.00, 50.00, null, 0.87045273, 43.52, 3277.81, 3.55, 0.00, null, null, + 980.82); + checkInst(model, 133, 133, LocalDate.of(2019, 5, 14), 131, 50.00, 50.00, null, 0.86952424, 43.48, 3231.31, 3.50, 0.00, null, null, + 980.82); + checkInst(model, 134, 134, LocalDate.of(2019, 5, 15), 132, 50.00, 50.00, null, 0.86859674, 43.43, 3184.76, 3.45, 0.00, null, null, + 980.82); + checkInst(model, 135, 135, LocalDate.of(2019, 5, 16), 133, 50.00, 50.00, null, 0.86767023, 43.38, 3138.16, 3.40, 0.00, null, null, + 980.82); + checkInst(model, 136, 136, LocalDate.of(2019, 5, 17), 134, 50.00, 50.00, null, 0.86674471, 43.34, 3091.51, 3.35, 0.00, null, null, + 980.82); + checkInst(model, 137, 137, LocalDate.of(2019, 5, 18), 135, 50.00, 50.00, null, 0.86582017, 43.29, 3044.81, 3.30, 0.00, null, null, + 980.82); + checkInst(model, 138, 138, LocalDate.of(2019, 5, 19), 136, 50.00, 50.00, null, 0.86489662, 43.24, 2998.06, 3.25, 0.00, null, null, + 980.82); + checkInst(model, 139, 139, LocalDate.of(2019, 5, 20), 137, 50.00, 50.00, null, 0.86397406, 43.20, 2951.26, 3.20, 0.00, null, null, + 980.82); + checkInst(model, 140, 140, LocalDate.of(2019, 5, 21), 138, 50.00, 50.00, null, 0.86305248, 43.15, 2904.42, 3.15, 0.00, null, null, + 980.82); + checkInst(model, 141, 141, LocalDate.of(2019, 5, 22), 139, 50.00, 50.00, null, 0.86213188, 43.11, 2857.52, 3.10, 0.00, null, null, + 980.82); + checkInst(model, 142, 142, LocalDate.of(2019, 5, 23), 140, 50.00, 50.00, null, 0.86121227, 43.06, 2810.57, 3.05, 0.00, null, null, + 980.82); + checkInst(model, 143, 143, LocalDate.of(2019, 5, 24), 141, 50.00, 50.00, null, 0.86029363, 43.01, 2763.57, 3.00, 0.00, null, null, + 980.82); + checkInst(model, 144, 144, LocalDate.of(2019, 5, 25), 142, 50.00, 50.00, null, 0.85937598, 42.97, 2716.52, 2.95, 0.00, null, null, + 980.82); + checkInst(model, 145, 145, LocalDate.of(2019, 5, 26), 143, 50.00, 50.00, null, 0.85845930, 42.92, 2669.42, 2.90, 0.00, null, null, + 980.82); + checkInst(model, 146, 146, LocalDate.of(2019, 5, 27), 144, 50.00, 50.00, null, 0.85754361, 42.88, 2622.27, 2.85, 0.00, null, null, + 980.82); + checkInst(model, 147, 147, LocalDate.of(2019, 5, 28), 145, 50.00, 50.00, null, 0.85662889, 42.83, 2575.07, 2.80, 0.00, null, null, + 980.82); + checkInst(model, 148, 148, LocalDate.of(2019, 5, 29), 146, 50.00, 50.00, null, 0.85571514, 42.79, 2527.82, 2.75, 0.00, null, null, + 980.82); + checkInst(model, 149, 149, LocalDate.of(2019, 5, 30), 147, 50.00, 50.00, null, 0.85480237, 42.74, 2480.52, 2.70, 0.00, null, null, + 980.82); + checkInst(model, 150, 150, LocalDate.of(2019, 5, 31), 148, 50.00, 50.00, null, 0.85389057, 42.69, 2433.17, 2.65, 0.00, null, null, + 980.82); + checkInst(model, 151, 151, LocalDate.of(2019, 6, 1), 149, 50.00, 50.00, null, 0.85297975, 42.65, 2385.77, 2.60, 0.00, null, null, + 980.82); + checkInst(model, 152, 152, LocalDate.of(2019, 6, 2), 150, 50.00, 50.00, null, 0.85206990, 42.60, 2338.31, 2.55, 0.00, null, null, + 980.82); + checkInst(model, 153, 153, LocalDate.of(2019, 6, 3), 151, 50.00, 50.00, null, 0.85116101, 42.56, 2290.81, 2.50, 0.00, null, null, + 980.82); + checkInst(model, 154, 154, LocalDate.of(2019, 6, 4), 152, 50.00, 50.00, null, 0.85025310, 42.51, 2243.26, 2.45, 0.00, null, null, + 980.82); + checkInst(model, 155, 155, LocalDate.of(2019, 6, 5), 153, 50.00, 50.00, null, 0.84934616, 42.47, 2195.65, 2.40, 0.00, null, null, + 980.82); + checkInst(model, 156, 156, LocalDate.of(2019, 6, 6), 154, 50.00, 50.00, null, 0.84844018, 42.42, 2148.00, 2.34, 0.00, null, null, + 980.82); + checkInst(model, 157, 157, LocalDate.of(2019, 6, 7), 155, 50.00, 50.00, null, 0.84753517, 42.38, 2100.29, 2.29, 0.00, null, null, + 980.82); + checkInst(model, 158, 158, LocalDate.of(2019, 6, 8), 156, 50.00, 50.00, null, 0.84663113, 42.33, 2052.53, 2.24, 0.00, null, null, + 980.82); + checkInst(model, 159, 159, LocalDate.of(2019, 6, 9), 157, 50.00, 50.00, null, 0.84572805, 42.29, 2004.73, 2.19, 0.00, null, null, + 980.82); + checkInst(model, 160, 160, LocalDate.of(2019, 6, 10), 158, 50.00, 50.00, null, 0.84482593, 42.24, 1956.87, 2.14, 0.00, null, null, + 980.82); + checkInst(model, 161, 161, LocalDate.of(2019, 6, 11), 159, 50.00, 50.00, null, 0.84392477, 42.20, 1908.96, 2.09, 0.00, null, null, + 980.82); + checkInst(model, 162, 162, LocalDate.of(2019, 6, 12), 160, 50.00, 50.00, null, 0.84302458, 42.15, 1860.99, 2.04, 0.00, null, null, + 980.82); + checkInst(model, 163, 163, LocalDate.of(2019, 6, 13), 161, 50.00, 50.00, null, 0.84212535, 42.11, 1812.98, 1.99, 0.00, null, null, + 980.82); + checkInst(model, 164, 164, LocalDate.of(2019, 6, 14), 162, 50.00, 50.00, null, 0.84122707, 42.06, 1764.92, 1.94, 0.00, null, null, + 980.82); + checkInst(model, 165, 165, LocalDate.of(2019, 6, 15), 163, 50.00, 50.00, null, 0.84032975, 42.02, 1716.80, 1.88, 0.00, null, null, + 980.82); + checkInst(model, 166, 166, LocalDate.of(2019, 6, 16), 164, 50.00, 50.00, null, 0.83943340, 41.97, 1668.64, 1.83, 0.00, null, null, + 980.82); + checkInst(model, 167, 167, LocalDate.of(2019, 6, 17), 165, 50.00, 50.00, null, 0.83853799, 41.93, 1620.42, 1.78, 0.00, null, null, + 980.82); + checkInst(model, 168, 168, LocalDate.of(2019, 6, 18), 166, 50.00, 50.00, null, 0.83764354, 41.88, 1572.15, 1.73, 0.00, null, null, + 980.82); + checkInst(model, 169, 169, LocalDate.of(2019, 6, 19), 167, 50.00, 50.00, null, 0.83675005, 41.84, 1523.83, 1.68, 0.00, null, null, + 980.82); + checkInst(model, 170, 170, LocalDate.of(2019, 6, 20), 168, 50.00, 50.00, null, 0.83585751, 41.79, 1475.45, 1.63, 0.00, null, null, + 980.82); + checkInst(model, 171, 171, LocalDate.of(2019, 6, 21), 169, 50.00, 50.00, null, 0.83496592, 41.75, 1427.03, 1.58, 0.00, null, null, + 980.82); + checkInst(model, 172, 172, LocalDate.of(2019, 6, 22), 170, 50.00, 50.00, null, 0.83407528, 41.70, 1378.55, 1.52, 0.00, null, null, + 980.82); + checkInst(model, 173, 173, LocalDate.of(2019, 6, 23), 171, 50.00, 50.00, null, 0.83318560, 41.66, 1330.02, 1.47, 0.00, null, null, + 980.82); + checkInst(model, 174, 174, LocalDate.of(2019, 6, 24), 172, 50.00, 50.00, null, 0.83229686, 41.61, 1281.45, 1.42, 0.00, null, null, + 980.82); + checkInst(model, 175, 175, LocalDate.of(2019, 6, 25), 173, 50.00, 50.00, null, 0.83140907, 41.57, 1232.81, 1.37, 0.00, null, null, + 980.82); + checkInst(model, 176, 176, LocalDate.of(2019, 6, 26), 174, 50.00, 50.00, null, 0.83052222, 41.53, 1184.13, 1.32, 0.00, null, null, + 980.82); + checkInst(model, 177, 177, LocalDate.of(2019, 6, 27), 175, 50.00, 50.00, null, 0.82963633, 41.48, 1135.39, 1.26, 0.00, null, null, + 980.82); + checkInst(model, 178, 178, LocalDate.of(2019, 6, 28), 176, 50.00, 50.00, null, 0.82875137, 41.44, 1086.61, 1.21, 0.00, null, null, + 980.82); + checkInst(model, 179, 179, LocalDate.of(2019, 6, 29), 177, 50.00, 50.00, null, 0.82786736, 41.39, 1037.77, 1.16, 0.00, null, null, + 980.82); + checkInst(model, 180, 180, LocalDate.of(2019, 6, 30), 178, 50.00, 50.00, null, 0.82698430, 41.35, 988.88, 1.11, 0.00, null, null, + 980.82); + checkInst(model, 181, 181, LocalDate.of(2019, 7, 1), 179, 50.00, 50.00, null, 0.82610217, 41.31, 939.93, 1.06, 0.00, null, null, + 980.82); + checkInst(model, 182, 182, LocalDate.of(2019, 7, 2), 180, 50.00, 50.00, null, 0.82522099, 41.26, 890.93, 1.00, 0.00, null, null, + 980.82); + checkInst(model, 183, 183, LocalDate.of(2019, 7, 3), 181, 50.00, 50.00, null, 0.82434075, 41.22, 841.89, 0.95, 0.00, null, null, + 980.82); + checkInst(model, 184, 184, LocalDate.of(2019, 7, 4), 182, 50.00, 50.00, null, 0.82346144, 41.17, 792.79, 0.90, 0.00, null, null, + 980.82); + checkInst(model, 185, 185, LocalDate.of(2019, 7, 5), 183, 50.00, 50.00, null, 0.82258308, 41.13, 743.63, 0.85, 0.00, null, null, + 980.82); + checkInst(model, 186, 186, LocalDate.of(2019, 7, 6), 184, 50.00, 50.00, null, 0.82170565, 41.09, 694.43, 0.79, 0.00, null, null, + 980.82); + checkInst(model, 187, 187, LocalDate.of(2019, 7, 7), 185, 50.00, 50.00, null, 0.82082916, 41.04, 645.17, 0.74, 0.00, null, null, + 980.82); + checkInst(model, 188, 188, LocalDate.of(2019, 7, 8), 186, 50.00, 50.00, null, 0.81995360, 41.00, 595.86, 0.69, 0.00, null, null, + 980.82); + checkInst(model, 189, 189, LocalDate.of(2019, 7, 9), 187, 50.00, 50.00, null, 0.81907897, 40.95, 546.49, 0.64, 0.00, null, null, + 980.82); + checkInst(model, 190, 190, LocalDate.of(2019, 7, 10), 188, 50.00, 50.00, null, 0.81820528, 40.91, 497.08, 0.58, 0.00, null, null, + 980.82); + checkInst(model, 191, 191, LocalDate.of(2019, 7, 11), 189, 50.00, 50.00, null, 0.81733252, 40.87, 447.61, 0.53, 0.00, null, null, + 980.82); + checkInst(model, 192, 192, LocalDate.of(2019, 7, 12), 190, 50.00, 50.00, null, 0.81646069, 40.82, 398.08, 0.48, 0.00, null, null, + 980.82); + checkInst(model, 193, 193, LocalDate.of(2019, 7, 13), 191, 50.00, 50.00, null, 0.81558979, 40.78, 348.51, 0.43, 0.00, null, null, + 980.82); + checkInst(model, 194, 194, LocalDate.of(2019, 7, 14), 192, 50.00, 50.00, null, 0.81471983, 40.74, 298.88, 0.37, 0.00, null, null, + 980.82); + checkInst(model, 195, 195, LocalDate.of(2019, 7, 15), 193, 50.00, 50.00, null, 0.81385078, 40.69, 249.20, 0.32, 0.00, null, null, + 980.82); + checkInst(model, 196, 196, LocalDate.of(2019, 7, 16), 194, 50.00, 50.00, null, 0.81298267, 40.65, 199.47, 0.27, 0.00, null, null, + 980.82); + checkInst(model, 197, 197, LocalDate.of(2019, 7, 17), 195, 50.00, 50.00, null, 0.81211548, 40.61, 149.68, 0.21, 0.00, null, null, + 980.82); + checkInst(model, 198, 198, LocalDate.of(2019, 7, 18), 196, 50.00, 50.00, null, 0.81124922, 40.56, 99.84, 0.16, 0.00, null, null, + 980.82); + checkInst(model, 199, 199, LocalDate.of(2019, 7, 19), 197, 50.00, 50.00, null, 0.81038388, 40.52, 49.95, 0.11, 0.00, null, null, + 980.82); + checkInst(model, 200, 200, LocalDate.of(2019, 7, 20), 198, 50.00, 50.00, null, 0.80951946, 40.48, 0.00, 0.05, 0.00, null, null, + 980.82); + + } + + @Test + void testExcessPayment_term200_discountFee1000_netDisbursement9000_pay70_80() { + final ProjectedAmortizationScheduleModel model = generateModel(); + + calculator.applyPayment(model, EXPECTED_DISBURSEMENT_DATE.plusDays(1), new BigDecimal("70")); + calculator.applyPayment(model, EXPECTED_DISBURSEMENT_DATE.plusDays(2), new BigDecimal("80")); + + checkInst(model, 0, 0, EXPECTED_DISBURSEMENT_DATE, 0, -9000.00, null, null, 1.00000000, -9000.00, 9000.00, null, null, null, null, + 1000.00); + + checkInst(model, 1, 1, LocalDate.of(2019, 1, 2), 0, 50.00, 50.00, 70.00, 1.00000000, 70.00, 8959.61, 9.61, 28.70, 13.44, 3.83, + 986.56); + checkInst(model, 2, 2, LocalDate.of(2019, 1, 3), 0, 50.00, 50.00, 80.00, 1.00000000, 80.00, 8919.18, 9.57, 15.26, 15.26, 5.69, + 971.30); + + checkInst(model, 3, 3, LocalDate.of(2019, 1, 4), 1, 50.00, 50.00, null, 0.99893332, 49.95, 8878.70, 9.52, 0.00, null, null, 971.30); + checkInst(model, 4, 4, LocalDate.of(2019, 1, 5), 2, 50.00, 50.00, null, 0.99786779, 49.89, 8838.18, 9.48, 0.00, null, null, 971.30); + checkInst(model, 5, 5, LocalDate.of(2019, 1, 6), 3, 50.00, 50.00, null, 0.99680339, 49.84, 8797.62, 9.44, 0.00, null, null, 971.30); + checkInst(model, 6, 6, LocalDate.of(2019, 1, 7), 4, 50.00, 50.00, null, 0.99574012, 49.79, 8757.01, 9.39, 0.00, null, null, 971.30); + checkInst(model, 7, 7, LocalDate.of(2019, 1, 8), 5, 50.00, 50.00, null, 0.99467799, 49.73, 8716.36, 9.35, 0.00, null, null, 971.30); + checkInst(model, 8, 8, LocalDate.of(2019, 1, 9), 6, 50.00, 50.00, null, 0.99361699, 49.68, 8675.67, 9.31, 0.00, null, null, 971.30); + checkInst(model, 9, 9, LocalDate.of(2019, 1, 10), 7, 50.00, 50.00, null, 0.99255712, 49.63, 8634.94, 9.26, 0.00, null, null, + 971.30); + checkInst(model, 10, 10, LocalDate.of(2019, 1, 11), 8, 50.00, 50.00, null, 0.99149839, 49.57, 8594.16, 9.22, 0.00, null, null, + 971.30); + checkInst(model, 11, 11, LocalDate.of(2019, 1, 12), 9, 50.00, 50.00, null, 0.99044078, 49.52, 8553.33, 9.18, 0.00, null, null, + 971.30); + checkInst(model, 12, 12, LocalDate.of(2019, 1, 13), 10, 50.00, 50.00, null, 0.98938430, 49.47, 8512.47, 9.13, 0.00, null, null, + 971.30); + checkInst(model, 13, 13, LocalDate.of(2019, 1, 14), 11, 50.00, 50.00, null, 0.98832895, 49.42, 8471.56, 9.09, 0.00, null, null, + 971.30); + checkInst(model, 14, 14, LocalDate.of(2019, 1, 15), 12, 50.00, 50.00, null, 0.98727472, 49.36, 8430.60, 9.05, 0.00, null, null, + 971.30); + checkInst(model, 15, 15, LocalDate.of(2019, 1, 16), 13, 50.00, 50.00, null, 0.98622162, 49.31, 8389.61, 9.00, 0.00, null, null, + 971.30); + checkInst(model, 16, 16, LocalDate.of(2019, 1, 17), 14, 50.00, 50.00, null, 0.98516964, 49.26, 8348.56, 8.96, 0.00, null, null, + 971.30); + checkInst(model, 17, 17, LocalDate.of(2019, 1, 18), 15, 50.00, 50.00, null, 0.98411879, 49.21, 8307.48, 8.91, 0.00, null, null, + 971.30); + checkInst(model, 18, 18, LocalDate.of(2019, 1, 19), 16, 50.00, 50.00, null, 0.98306905, 49.15, 8266.35, 8.87, 0.00, null, null, + 971.30); + checkInst(model, 19, 19, LocalDate.of(2019, 1, 20), 17, 50.00, 50.00, null, 0.98202044, 49.10, 8225.18, 8.83, 0.00, null, null, + 971.30); + checkInst(model, 20, 20, LocalDate.of(2019, 1, 21), 18, 50.00, 50.00, null, 0.98097294, 49.05, 8183.96, 8.78, 0.00, null, null, + 971.30); + checkInst(model, 21, 21, LocalDate.of(2019, 1, 22), 19, 50.00, 50.00, null, 0.97992656, 49.00, 8142.70, 8.74, 0.00, null, null, + 971.30); + checkInst(model, 22, 22, LocalDate.of(2019, 1, 23), 20, 50.00, 50.00, null, 0.97888129, 48.94, 8101.39, 8.69, 0.00, null, null, + 971.30); + checkInst(model, 23, 23, LocalDate.of(2019, 1, 24), 21, 50.00, 50.00, null, 0.97783715, 48.89, 8060.04, 8.65, 0.00, null, null, + 971.30); + checkInst(model, 24, 24, LocalDate.of(2019, 1, 25), 22, 50.00, 50.00, null, 0.97679411, 48.84, 8018.65, 8.61, 0.00, null, null, + 971.30); + checkInst(model, 25, 25, LocalDate.of(2019, 1, 26), 23, 50.00, 50.00, null, 0.97575219, 48.79, 7977.21, 8.56, 0.00, null, null, + 971.30); + checkInst(model, 26, 26, LocalDate.of(2019, 1, 27), 24, 50.00, 50.00, null, 0.97471138, 48.74, 7935.73, 8.52, 0.00, null, null, + 971.30); + checkInst(model, 27, 27, LocalDate.of(2019, 1, 28), 25, 50.00, 50.00, null, 0.97367168, 48.68, 7894.21, 8.47, 0.00, null, null, + 971.30); + checkInst(model, 28, 28, LocalDate.of(2019, 1, 29), 26, 50.00, 50.00, null, 0.97263309, 48.63, 7852.63, 8.43, 0.00, null, null, + 971.30); + checkInst(model, 29, 29, LocalDate.of(2019, 1, 30), 27, 50.00, 50.00, null, 0.97159560, 48.58, 7811.02, 8.39, 0.00, null, null, + 971.30); + checkInst(model, 30, 30, LocalDate.of(2019, 1, 31), 28, 50.00, 50.00, null, 0.97055922, 48.53, 7769.36, 8.34, 0.00, null, null, + 971.30); + checkInst(model, 31, 31, LocalDate.of(2019, 2, 1), 29, 50.00, 50.00, null, 0.96952395, 48.48, 7727.66, 8.30, 0.00, null, null, + 971.30); + checkInst(model, 32, 32, LocalDate.of(2019, 2, 2), 30, 50.00, 50.00, null, 0.96848979, 48.42, 7685.91, 8.25, 0.00, null, null, + 971.30); + checkInst(model, 33, 33, LocalDate.of(2019, 2, 3), 31, 50.00, 50.00, null, 0.96745672, 48.37, 7644.12, 8.21, 0.00, null, null, + 971.30); + checkInst(model, 34, 34, LocalDate.of(2019, 2, 4), 32, 50.00, 50.00, null, 0.96642476, 48.32, 7602.28, 8.16, 0.00, null, null, + 971.30); + checkInst(model, 35, 35, LocalDate.of(2019, 2, 5), 33, 50.00, 50.00, null, 0.96539390, 48.27, 7560.40, 8.12, 0.00, null, null, + 971.30); + checkInst(model, 36, 36, LocalDate.of(2019, 2, 6), 34, 50.00, 50.00, null, 0.96436413, 48.22, 7518.47, 8.07, 0.00, null, null, + 971.30); + checkInst(model, 37, 37, LocalDate.of(2019, 2, 7), 35, 50.00, 50.00, null, 0.96333547, 48.17, 7476.50, 8.03, 0.00, null, null, + 971.30); + checkInst(model, 38, 38, LocalDate.of(2019, 2, 8), 36, 50.00, 50.00, null, 0.96230790, 48.12, 7434.48, 7.98, 0.00, null, null, + 971.30); + checkInst(model, 39, 39, LocalDate.of(2019, 2, 9), 37, 50.00, 50.00, null, 0.96128143, 48.06, 7392.42, 7.94, 0.00, null, null, + 971.30); + checkInst(model, 40, 40, LocalDate.of(2019, 2, 10), 38, 50.00, 50.00, null, 0.96025606, 48.01, 7350.31, 7.89, 0.00, null, null, + 971.30); + checkInst(model, 41, 41, LocalDate.of(2019, 2, 11), 39, 50.00, 50.00, null, 0.95923178, 47.96, 7308.16, 7.85, 0.00, null, null, + 971.30); + checkInst(model, 42, 42, LocalDate.of(2019, 2, 12), 40, 50.00, 50.00, null, 0.95820859, 47.91, 7265.97, 7.80, 0.00, null, null, + 971.30); + checkInst(model, 43, 43, LocalDate.of(2019, 2, 13), 41, 50.00, 50.00, null, 0.95718649, 47.86, 7223.72, 7.76, 0.00, null, null, + 971.30); + checkInst(model, 44, 44, LocalDate.of(2019, 2, 14), 42, 50.00, 50.00, null, 0.95616548, 47.81, 7181.44, 7.71, 0.00, null, null, + 971.30); + checkInst(model, 45, 45, LocalDate.of(2019, 2, 15), 43, 50.00, 50.00, null, 0.95514557, 47.76, 7139.11, 7.67, 0.00, null, null, + 971.30); + checkInst(model, 46, 46, LocalDate.of(2019, 2, 16), 44, 50.00, 50.00, null, 0.95412674, 47.71, 7096.73, 7.62, 0.00, null, null, + 971.30); + checkInst(model, 47, 47, LocalDate.of(2019, 2, 17), 45, 50.00, 50.00, null, 0.95310899, 47.66, 7054.31, 7.58, 0.00, null, null, + 971.30); + checkInst(model, 48, 48, LocalDate.of(2019, 2, 18), 46, 50.00, 50.00, null, 0.95209233, 47.60, 7011.84, 7.53, 0.00, null, null, + 971.30); + checkInst(model, 49, 49, LocalDate.of(2019, 2, 19), 47, 50.00, 50.00, null, 0.95107676, 47.55, 6969.33, 7.49, 0.00, null, null, + 971.30); + checkInst(model, 50, 50, LocalDate.of(2019, 2, 20), 48, 50.00, 50.00, null, 0.95006227, 47.50, 6926.77, 7.44, 0.00, null, null, + 971.30); + checkInst(model, 51, 51, LocalDate.of(2019, 2, 21), 49, 50.00, 50.00, null, 0.94904886, 47.45, 6884.17, 7.40, 0.00, null, null, + 971.30); + checkInst(model, 52, 52, LocalDate.of(2019, 2, 22), 50, 50.00, 50.00, null, 0.94803653, 47.40, 6841.52, 7.35, 0.00, null, null, + 971.30); + checkInst(model, 53, 53, LocalDate.of(2019, 2, 23), 51, 50.00, 50.00, null, 0.94702529, 47.35, 6798.82, 7.31, 0.00, null, null, + 971.30); + checkInst(model, 54, 54, LocalDate.of(2019, 2, 24), 52, 50.00, 50.00, null, 0.94601512, 47.30, 6756.08, 7.26, 0.00, null, null, + 971.30); + checkInst(model, 55, 55, LocalDate.of(2019, 2, 25), 53, 50.00, 50.00, null, 0.94500603, 47.25, 6713.30, 7.21, 0.00, null, null, + 971.30); + checkInst(model, 56, 56, LocalDate.of(2019, 2, 26), 54, 50.00, 50.00, null, 0.94399801, 47.20, 6670.47, 7.17, 0.00, null, null, + 971.30); + checkInst(model, 57, 57, LocalDate.of(2019, 2, 27), 55, 50.00, 50.00, null, 0.94299107, 47.15, 6627.59, 7.12, 0.00, null, null, + 971.30); + checkInst(model, 58, 58, LocalDate.of(2019, 2, 28), 56, 50.00, 50.00, null, 0.94198521, 47.10, 6584.67, 7.08, 0.00, null, null, + 971.30); + checkInst(model, 59, 59, LocalDate.of(2019, 3, 1), 57, 50.00, 50.00, null, 0.94098042, 47.05, 6541.70, 7.03, 0.00, null, null, + 971.30); + checkInst(model, 60, 60, LocalDate.of(2019, 3, 2), 58, 50.00, 50.00, null, 0.93997669, 47.00, 6498.68, 6.99, 0.00, null, null, + 971.30); + checkInst(model, 61, 61, LocalDate.of(2019, 3, 3), 59, 50.00, 50.00, null, 0.93897404, 46.95, 6455.62, 6.94, 0.00, null, null, + 971.30); + checkInst(model, 62, 62, LocalDate.of(2019, 3, 4), 60, 50.00, 50.00, null, 0.93797246, 46.90, 6412.51, 6.89, 0.00, null, null, + 971.30); + checkInst(model, 63, 63, LocalDate.of(2019, 3, 5), 61, 50.00, 50.00, null, 0.93697195, 46.85, 6369.36, 6.85, 0.00, null, null, + 971.30); + checkInst(model, 64, 64, LocalDate.of(2019, 3, 6), 62, 50.00, 50.00, null, 0.93597251, 46.80, 6326.16, 6.80, 0.00, null, null, + 971.30); + checkInst(model, 65, 65, LocalDate.of(2019, 3, 7), 63, 50.00, 50.00, null, 0.93497413, 46.75, 6282.92, 6.76, 0.00, null, null, + 971.30); + checkInst(model, 66, 66, LocalDate.of(2019, 3, 8), 64, 50.00, 50.00, null, 0.93397681, 46.70, 6239.63, 6.71, 0.00, null, null, + 971.30); + checkInst(model, 67, 67, LocalDate.of(2019, 3, 9), 65, 50.00, 50.00, null, 0.93298056, 46.65, 6196.29, 6.66, 0.00, null, null, + 971.30); + checkInst(model, 68, 68, LocalDate.of(2019, 3, 10), 66, 50.00, 50.00, null, 0.93198538, 46.60, 6152.91, 6.62, 0.00, null, null, + 971.30); + checkInst(model, 69, 69, LocalDate.of(2019, 3, 11), 67, 50.00, 50.00, null, 0.93099125, 46.55, 6109.48, 6.57, 0.00, null, null, + 971.30); + checkInst(model, 70, 70, LocalDate.of(2019, 3, 12), 68, 50.00, 50.00, null, 0.92999818, 46.50, 6066.00, 6.52, 0.00, null, null, + 971.30); + checkInst(model, 71, 71, LocalDate.of(2019, 3, 13), 69, 50.00, 50.00, null, 0.92900618, 46.45, 6022.48, 6.48, 0.00, null, null, + 971.30); + checkInst(model, 72, 72, LocalDate.of(2019, 3, 14), 70, 50.00, 50.00, null, 0.92801523, 46.40, 5978.91, 6.43, 0.00, null, null, + 971.30); + checkInst(model, 73, 73, LocalDate.of(2019, 3, 15), 71, 50.00, 50.00, null, 0.92702534, 46.35, 5935.29, 6.38, 0.00, null, null, + 971.30); + checkInst(model, 74, 74, LocalDate.of(2019, 3, 16), 72, 50.00, 50.00, null, 0.92603650, 46.30, 5891.63, 6.34, 0.00, null, null, + 971.30); + checkInst(model, 75, 75, LocalDate.of(2019, 3, 17), 73, 50.00, 50.00, null, 0.92504872, 46.25, 5847.92, 6.29, 0.00, null, null, + 971.30); + checkInst(model, 76, 76, LocalDate.of(2019, 3, 18), 74, 50.00, 50.00, null, 0.92406200, 46.20, 5804.17, 6.24, 0.00, null, null, + 971.30); + checkInst(model, 77, 77, LocalDate.of(2019, 3, 19), 75, 50.00, 50.00, null, 0.92307632, 46.15, 5760.36, 6.20, 0.00, null, null, + 971.30); + checkInst(model, 78, 78, LocalDate.of(2019, 3, 20), 76, 50.00, 50.00, null, 0.92209170, 46.10, 5716.52, 6.15, 0.00, null, null, + 971.30); + checkInst(model, 79, 79, LocalDate.of(2019, 3, 21), 77, 50.00, 50.00, null, 0.92110813, 46.06, 5672.62, 6.10, 0.00, null, null, + 971.30); + checkInst(model, 80, 80, LocalDate.of(2019, 3, 22), 78, 50.00, 50.00, null, 0.92012560, 46.01, 5628.68, 6.06, 0.00, null, null, + 971.30); + checkInst(model, 81, 81, LocalDate.of(2019, 3, 23), 79, 50.00, 50.00, null, 0.91914413, 45.96, 5584.69, 6.01, 0.00, null, null, + 971.30); + checkInst(model, 82, 82, LocalDate.of(2019, 3, 24), 80, 50.00, 50.00, null, 0.91816370, 45.91, 5540.65, 5.96, 0.00, null, null, + 971.30); + checkInst(model, 83, 83, LocalDate.of(2019, 3, 25), 81, 50.00, 50.00, null, 0.91718432, 45.86, 5496.57, 5.92, 0.00, null, null, + 971.30); + checkInst(model, 84, 84, LocalDate.of(2019, 3, 26), 82, 50.00, 50.00, null, 0.91620598, 45.81, 5452.44, 5.87, 0.00, null, null, + 971.30); + checkInst(model, 85, 85, LocalDate.of(2019, 3, 27), 83, 50.00, 50.00, null, 0.91522868, 45.76, 5408.26, 5.82, 0.00, null, null, + 971.30); + checkInst(model, 86, 86, LocalDate.of(2019, 3, 28), 84, 50.00, 50.00, null, 0.91425243, 45.71, 5364.03, 5.78, 0.00, null, null, + 971.30); + checkInst(model, 87, 87, LocalDate.of(2019, 3, 29), 85, 50.00, 50.00, null, 0.91327722, 45.66, 5319.76, 5.73, 0.00, null, null, + 971.30); + checkInst(model, 88, 88, LocalDate.of(2019, 3, 30), 86, 50.00, 50.00, null, 0.91230305, 45.62, 5275.44, 5.68, 0.00, null, null, + 971.30); + checkInst(model, 89, 89, LocalDate.of(2019, 3, 31), 87, 50.00, 50.00, null, 0.91132992, 45.57, 5231.08, 5.63, 0.00, null, null, + 971.30); + checkInst(model, 90, 90, LocalDate.of(2019, 4, 1), 88, 50.00, 50.00, null, 0.91035783, 45.52, 5186.66, 5.59, 0.00, null, null, + 971.30); + checkInst(model, 91, 91, LocalDate.of(2019, 4, 2), 89, 50.00, 50.00, null, 0.90938677, 45.47, 5142.20, 5.54, 0.00, null, null, + 971.30); + checkInst(model, 92, 92, LocalDate.of(2019, 4, 3), 90, 50.00, 50.00, null, 0.90841675, 45.42, 5097.69, 5.49, 0.00, null, null, + 971.30); + checkInst(model, 93, 93, LocalDate.of(2019, 4, 4), 91, 50.00, 50.00, null, 0.90744776, 45.37, 5053.13, 5.44, 0.00, null, null, + 971.30); + checkInst(model, 94, 94, LocalDate.of(2019, 4, 5), 92, 50.00, 50.00, null, 0.90647981, 45.32, 5008.53, 5.40, 0.00, null, null, + 971.30); + checkInst(model, 95, 95, LocalDate.of(2019, 4, 6), 93, 50.00, 50.00, null, 0.90551289, 45.28, 4963.88, 5.35, 0.00, null, null, + 971.30); + checkInst(model, 96, 96, LocalDate.of(2019, 4, 7), 94, 50.00, 50.00, null, 0.90454700, 45.23, 4919.18, 5.30, 0.00, null, null, + 971.30); + checkInst(model, 97, 97, LocalDate.of(2019, 4, 8), 95, 50.00, 50.00, null, 0.90358215, 45.18, 4874.43, 5.25, 0.00, null, null, + 971.30); + checkInst(model, 98, 98, LocalDate.of(2019, 4, 9), 96, 50.00, 50.00, null, 0.90261832, 45.13, 4829.64, 5.20, 0.00, null, null, + 971.30); + checkInst(model, 99, 99, LocalDate.of(2019, 4, 10), 97, 50.00, 50.00, null, 0.90165552, 45.08, 4784.79, 5.16, 0.00, null, null, + 971.30); + checkInst(model, 100, 100, LocalDate.of(2019, 4, 11), 98, 50.00, 50.00, null, 0.90069374, 45.03, 4739.90, 5.11, 0.00, null, null, + 971.30); + checkInst(model, 101, 101, LocalDate.of(2019, 4, 12), 99, 50.00, 50.00, null, 0.89973299, 44.99, 4694.96, 5.06, 0.00, null, null, + 971.30); + checkInst(model, 102, 102, LocalDate.of(2019, 4, 13), 100, 50.00, 50.00, null, 0.89877327, 44.94, 4649.98, 5.01, 0.00, null, null, + 971.30); + checkInst(model, 103, 103, LocalDate.of(2019, 4, 14), 101, 50.00, 50.00, null, 0.89781457, 44.89, 4604.94, 4.97, 0.00, null, null, + 971.30); + checkInst(model, 104, 104, LocalDate.of(2019, 4, 15), 102, 50.00, 50.00, null, 0.89685689, 44.84, 4559.86, 4.92, 0.00, null, null, + 971.30); + checkInst(model, 105, 105, LocalDate.of(2019, 4, 16), 103, 50.00, 50.00, null, 0.89590024, 44.80, 4514.73, 4.87, 0.00, null, null, + 971.30); + checkInst(model, 106, 106, LocalDate.of(2019, 4, 17), 104, 50.00, 50.00, null, 0.89494460, 44.75, 4469.55, 4.82, 0.00, null, null, + 971.30); + checkInst(model, 107, 107, LocalDate.of(2019, 4, 18), 105, 50.00, 50.00, null, 0.89398999, 44.70, 4424.32, 4.77, 0.00, null, null, + 971.30); + checkInst(model, 108, 108, LocalDate.of(2019, 4, 19), 106, 50.00, 50.00, null, 0.89303639, 44.65, 4379.05, 4.72, 0.00, null, null, + 971.30); + checkInst(model, 109, 109, LocalDate.of(2019, 4, 20), 107, 50.00, 50.00, null, 0.89208381, 44.60, 4333.72, 4.68, 0.00, null, null, + 971.30); + checkInst(model, 110, 110, LocalDate.of(2019, 4, 21), 108, 50.00, 50.00, null, 0.89113225, 44.56, 4288.35, 4.63, 0.00, null, null, + 971.30); + checkInst(model, 111, 111, LocalDate.of(2019, 4, 22), 109, 50.00, 50.00, null, 0.89018170, 44.51, 4242.93, 4.58, 0.00, null, null, + 971.30); + checkInst(model, 112, 112, LocalDate.of(2019, 4, 23), 110, 50.00, 50.00, null, 0.88923216, 44.46, 4197.46, 4.53, 0.00, null, null, + 971.30); + checkInst(model, 113, 113, LocalDate.of(2019, 4, 24), 111, 50.00, 50.00, null, 0.88828364, 44.41, 4151.94, 4.48, 0.00, null, null, + 971.30); + checkInst(model, 114, 114, LocalDate.of(2019, 4, 25), 112, 50.00, 50.00, null, 0.88733613, 44.37, 4106.38, 4.43, 0.00, null, null, + 971.30); + checkInst(model, 115, 115, LocalDate.of(2019, 4, 26), 113, 50.00, 50.00, null, 0.88638963, 44.32, 4060.76, 4.38, 0.00, null, null, + 971.30); + checkInst(model, 116, 116, LocalDate.of(2019, 4, 27), 114, 50.00, 50.00, null, 0.88544414, 44.27, 4015.10, 4.34, 0.00, null, null, + 971.30); + checkInst(model, 117, 117, LocalDate.of(2019, 4, 28), 115, 50.00, 50.00, null, 0.88449966, 44.22, 3969.38, 4.29, 0.00, null, null, + 971.30); + checkInst(model, 118, 118, LocalDate.of(2019, 4, 29), 116, 50.00, 50.00, null, 0.88355619, 44.18, 3923.62, 4.24, 0.00, null, null, + 971.30); + checkInst(model, 119, 119, LocalDate.of(2019, 4, 30), 117, 50.00, 50.00, null, 0.88261372, 44.13, 3877.81, 4.19, 0.00, null, null, + 971.30); + checkInst(model, 120, 120, LocalDate.of(2019, 5, 1), 118, 50.00, 50.00, null, 0.88167226, 44.08, 3831.95, 4.14, 0.00, null, null, + 971.30); + checkInst(model, 121, 121, LocalDate.of(2019, 5, 2), 119, 50.00, 50.00, null, 0.88073180, 44.04, 3786.04, 4.09, 0.00, null, null, + 971.30); + checkInst(model, 122, 122, LocalDate.of(2019, 5, 3), 120, 50.00, 50.00, null, 0.87979234, 43.99, 3740.09, 4.04, 0.00, null, null, + 971.30); + checkInst(model, 123, 123, LocalDate.of(2019, 5, 4), 121, 50.00, 50.00, null, 0.87885389, 43.94, 3694.08, 3.99, 0.00, null, null, + 971.30); + checkInst(model, 124, 124, LocalDate.of(2019, 5, 5), 122, 50.00, 50.00, null, 0.87791644, 43.90, 3648.03, 3.94, 0.00, null, null, + 971.30); + checkInst(model, 125, 125, LocalDate.of(2019, 5, 6), 123, 50.00, 50.00, null, 0.87697999, 43.85, 3601.92, 3.90, 0.00, null, null, + 971.30); + checkInst(model, 126, 126, LocalDate.of(2019, 5, 7), 124, 50.00, 50.00, null, 0.87604453, 43.80, 3555.77, 3.85, 0.00, null, null, + 971.30); + checkInst(model, 127, 127, LocalDate.of(2019, 5, 8), 125, 50.00, 50.00, null, 0.87511008, 43.76, 3509.56, 3.80, 0.00, null, null, + 971.30); + checkInst(model, 128, 128, LocalDate.of(2019, 5, 9), 126, 50.00, 50.00, null, 0.87417662, 43.71, 3463.31, 3.75, 0.00, null, null, + 971.30); + checkInst(model, 129, 129, LocalDate.of(2019, 5, 10), 127, 50.00, 50.00, null, 0.87324416, 43.66, 3417.01, 3.70, 0.00, null, null, + 971.30); + checkInst(model, 130, 130, LocalDate.of(2019, 5, 11), 128, 50.00, 50.00, null, 0.87231269, 43.62, 3370.66, 3.65, 0.00, null, null, + 971.30); + checkInst(model, 131, 131, LocalDate.of(2019, 5, 12), 129, 50.00, 50.00, null, 0.87138221, 43.57, 3324.26, 3.60, 0.00, null, null, + 971.30); + checkInst(model, 132, 132, LocalDate.of(2019, 5, 13), 130, 50.00, 50.00, null, 0.87045273, 43.52, 3277.81, 3.55, 0.00, null, null, + 971.30); + checkInst(model, 133, 133, LocalDate.of(2019, 5, 14), 131, 50.00, 50.00, null, 0.86952424, 43.48, 3231.31, 3.50, 0.00, null, null, + 971.30); + checkInst(model, 134, 134, LocalDate.of(2019, 5, 15), 132, 50.00, 50.00, null, 0.86859674, 43.43, 3184.76, 3.45, 0.00, null, null, + 971.30); + checkInst(model, 135, 135, LocalDate.of(2019, 5, 16), 133, 50.00, 50.00, null, 0.86767023, 43.38, 3138.16, 3.40, 0.00, null, null, + 971.30); + checkInst(model, 136, 136, LocalDate.of(2019, 5, 17), 134, 50.00, 50.00, null, 0.86674471, 43.34, 3091.51, 3.35, 0.00, null, null, + 971.30); + checkInst(model, 137, 137, LocalDate.of(2019, 5, 18), 135, 50.00, 50.00, null, 0.86582017, 43.29, 3044.81, 3.30, 0.00, null, null, + 971.30); + checkInst(model, 138, 138, LocalDate.of(2019, 5, 19), 136, 50.00, 50.00, null, 0.86489662, 43.24, 2998.06, 3.25, 0.00, null, null, + 971.30); + checkInst(model, 139, 139, LocalDate.of(2019, 5, 20), 137, 50.00, 50.00, null, 0.86397406, 43.20, 2951.26, 3.20, 0.00, null, null, + 971.30); + checkInst(model, 140, 140, LocalDate.of(2019, 5, 21), 138, 50.00, 50.00, null, 0.86305248, 43.15, 2904.42, 3.15, 0.00, null, null, + 971.30); + checkInst(model, 141, 141, LocalDate.of(2019, 5, 22), 139, 50.00, 50.00, null, 0.86213188, 43.11, 2857.52, 3.10, 0.00, null, null, + 971.30); + checkInst(model, 142, 142, LocalDate.of(2019, 5, 23), 140, 50.00, 50.00, null, 0.86121227, 43.06, 2810.57, 3.05, 0.00, null, null, + 971.30); + checkInst(model, 143, 143, LocalDate.of(2019, 5, 24), 141, 50.00, 50.00, null, 0.86029363, 43.01, 2763.57, 3.00, 0.00, null, null, + 971.30); + checkInst(model, 144, 144, LocalDate.of(2019, 5, 25), 142, 50.00, 50.00, null, 0.85937598, 42.97, 2716.52, 2.95, 0.00, null, null, + 971.30); + checkInst(model, 145, 145, LocalDate.of(2019, 5, 26), 143, 50.00, 50.00, null, 0.85845930, 42.92, 2669.42, 2.90, 0.00, null, null, + 971.30); + checkInst(model, 146, 146, LocalDate.of(2019, 5, 27), 144, 50.00, 50.00, null, 0.85754361, 42.88, 2622.27, 2.85, 0.00, null, null, + 971.30); + checkInst(model, 147, 147, LocalDate.of(2019, 5, 28), 145, 50.00, 50.00, null, 0.85662889, 42.83, 2575.07, 2.80, 0.00, null, null, + 971.30); + checkInst(model, 148, 148, LocalDate.of(2019, 5, 29), 146, 50.00, 50.00, null, 0.85571514, 42.79, 2527.82, 2.75, 0.00, null, null, + 971.30); + checkInst(model, 149, 149, LocalDate.of(2019, 5, 30), 147, 50.00, 50.00, null, 0.85480237, 42.74, 2480.52, 2.70, 0.00, null, null, + 971.30); + checkInst(model, 150, 150, LocalDate.of(2019, 5, 31), 148, 50.00, 50.00, null, 0.85389057, 42.69, 2433.17, 2.65, 0.00, null, null, + 971.30); + checkInst(model, 151, 151, LocalDate.of(2019, 6, 1), 149, 50.00, 50.00, null, 0.85297975, 42.65, 2385.77, 2.60, 0.00, null, null, + 971.30); + checkInst(model, 152, 152, LocalDate.of(2019, 6, 2), 150, 50.00, 50.00, null, 0.85206990, 42.60, 2338.31, 2.55, 0.00, null, null, + 971.30); + checkInst(model, 153, 153, LocalDate.of(2019, 6, 3), 151, 50.00, 50.00, null, 0.85116101, 42.56, 2290.81, 2.50, 0.00, null, null, + 971.30); + checkInst(model, 154, 154, LocalDate.of(2019, 6, 4), 152, 50.00, 50.00, null, 0.85025310, 42.51, 2243.26, 2.45, 0.00, null, null, + 971.30); + checkInst(model, 155, 155, LocalDate.of(2019, 6, 5), 153, 50.00, 50.00, null, 0.84934616, 42.47, 2195.65, 2.40, 0.00, null, null, + 971.30); + checkInst(model, 156, 156, LocalDate.of(2019, 6, 6), 154, 50.00, 50.00, null, 0.84844018, 42.42, 2148.00, 2.34, 0.00, null, null, + 971.30); + checkInst(model, 157, 157, LocalDate.of(2019, 6, 7), 155, 50.00, 50.00, null, 0.84753517, 42.38, 2100.29, 2.29, 0.00, null, null, + 971.30); + checkInst(model, 158, 158, LocalDate.of(2019, 6, 8), 156, 50.00, 50.00, null, 0.84663113, 42.33, 2052.53, 2.24, 0.00, null, null, + 971.30); + checkInst(model, 159, 159, LocalDate.of(2019, 6, 9), 157, 50.00, 50.00, null, 0.84572805, 42.29, 2004.73, 2.19, 0.00, null, null, + 971.30); + checkInst(model, 160, 160, LocalDate.of(2019, 6, 10), 158, 50.00, 50.00, null, 0.84482593, 42.24, 1956.87, 2.14, 0.00, null, null, + 971.30); + checkInst(model, 161, 161, LocalDate.of(2019, 6, 11), 159, 50.00, 50.00, null, 0.84392477, 42.20, 1908.96, 2.09, 0.00, null, null, + 971.30); + checkInst(model, 162, 162, LocalDate.of(2019, 6, 12), 160, 50.00, 50.00, null, 0.84302458, 42.15, 1860.99, 2.04, 0.00, null, null, + 971.30); + checkInst(model, 163, 163, LocalDate.of(2019, 6, 13), 161, 50.00, 50.00, null, 0.84212535, 42.11, 1812.98, 1.99, 0.00, null, null, + 971.30); + checkInst(model, 164, 164, LocalDate.of(2019, 6, 14), 162, 50.00, 50.00, null, 0.84122707, 42.06, 1764.92, 1.94, 0.00, null, null, + 971.30); + checkInst(model, 165, 165, LocalDate.of(2019, 6, 15), 163, 50.00, 50.00, null, 0.84032975, 42.02, 1716.80, 1.88, 0.00, null, null, + 971.30); + checkInst(model, 166, 166, LocalDate.of(2019, 6, 16), 164, 50.00, 50.00, null, 0.83943340, 41.97, 1668.64, 1.83, 0.00, null, null, + 971.30); + checkInst(model, 167, 167, LocalDate.of(2019, 6, 17), 165, 50.00, 50.00, null, 0.83853799, 41.93, 1620.42, 1.78, 0.00, null, null, + 971.30); + checkInst(model, 168, 168, LocalDate.of(2019, 6, 18), 166, 50.00, 50.00, null, 0.83764354, 41.88, 1572.15, 1.73, 0.00, null, null, + 971.30); + checkInst(model, 169, 169, LocalDate.of(2019, 6, 19), 167, 50.00, 50.00, null, 0.83675005, 41.84, 1523.83, 1.68, 0.00, null, null, + 971.30); + checkInst(model, 170, 170, LocalDate.of(2019, 6, 20), 168, 50.00, 50.00, null, 0.83585751, 41.79, 1475.45, 1.63, 0.00, null, null, + 971.30); + checkInst(model, 171, 171, LocalDate.of(2019, 6, 21), 169, 50.00, 50.00, null, 0.83496592, 41.75, 1427.03, 1.58, 0.00, null, null, + 971.30); + checkInst(model, 172, 172, LocalDate.of(2019, 6, 22), 170, 50.00, 50.00, null, 0.83407528, 41.70, 1378.55, 1.52, 0.00, null, null, + 971.30); + checkInst(model, 173, 173, LocalDate.of(2019, 6, 23), 171, 50.00, 50.00, null, 0.83318560, 41.66, 1330.02, 1.47, 0.00, null, null, + 971.30); + checkInst(model, 174, 174, LocalDate.of(2019, 6, 24), 172, 50.00, 50.00, null, 0.83229686, 41.61, 1281.45, 1.42, 0.00, null, null, + 971.30); + checkInst(model, 175, 175, LocalDate.of(2019, 6, 25), 173, 50.00, 50.00, null, 0.83140907, 41.57, 1232.81, 1.37, 0.00, null, null, + 971.30); + checkInst(model, 176, 176, LocalDate.of(2019, 6, 26), 174, 50.00, 50.00, null, 0.83052222, 41.53, 1184.13, 1.32, 0.00, null, null, + 971.30); + checkInst(model, 177, 177, LocalDate.of(2019, 6, 27), 175, 50.00, 50.00, null, 0.82963633, 41.48, 1135.39, 1.26, 0.00, null, null, + 971.30); + checkInst(model, 178, 178, LocalDate.of(2019, 6, 28), 176, 50.00, 50.00, null, 0.82875137, 41.44, 1086.61, 1.21, 0.00, null, null, + 971.30); + checkInst(model, 179, 179, LocalDate.of(2019, 6, 29), 177, 50.00, 50.00, null, 0.82786736, 41.39, 1037.77, 1.16, 0.00, null, null, + 971.30); + checkInst(model, 180, 180, LocalDate.of(2019, 6, 30), 178, 50.00, 50.00, null, 0.82698430, 41.35, 988.88, 1.11, 0.00, null, null, + 971.30); + checkInst(model, 181, 181, LocalDate.of(2019, 7, 1), 179, 50.00, 50.00, null, 0.82610217, 41.31, 939.93, 1.06, 0.00, null, null, + 971.30); + checkInst(model, 182, 182, LocalDate.of(2019, 7, 2), 180, 50.00, 50.00, null, 0.82522099, 41.26, 890.93, 1.00, 0.00, null, null, + 971.30); + checkInst(model, 183, 183, LocalDate.of(2019, 7, 3), 181, 50.00, 50.00, null, 0.82434075, 41.22, 841.89, 0.95, 0.00, null, null, + 971.30); + checkInst(model, 184, 184, LocalDate.of(2019, 7, 4), 182, 50.00, 50.00, null, 0.82346144, 41.17, 792.79, 0.90, 0.00, null, null, + 971.30); + checkInst(model, 185, 185, LocalDate.of(2019, 7, 5), 183, 50.00, 50.00, null, 0.82258308, 41.13, 743.63, 0.85, 0.00, null, null, + 971.30); + checkInst(model, 186, 186, LocalDate.of(2019, 7, 6), 184, 50.00, 50.00, null, 0.82170565, 41.09, 694.43, 0.79, 0.00, null, null, + 971.30); + checkInst(model, 187, 187, LocalDate.of(2019, 7, 7), 185, 50.00, 50.00, null, 0.82082916, 41.04, 645.17, 0.74, 0.00, null, null, + 971.30); + checkInst(model, 188, 188, LocalDate.of(2019, 7, 8), 186, 50.00, 50.00, null, 0.81995360, 41.00, 595.86, 0.69, 0.00, null, null, + 971.30); + checkInst(model, 189, 189, LocalDate.of(2019, 7, 9), 187, 50.00, 50.00, null, 0.81907897, 40.95, 546.49, 0.64, 0.00, null, null, + 971.30); + checkInst(model, 190, 190, LocalDate.of(2019, 7, 10), 188, 50.00, 50.00, null, 0.81820528, 40.91, 497.08, 0.58, 0.00, null, null, + 971.30); + checkInst(model, 191, 191, LocalDate.of(2019, 7, 11), 189, 50.00, 50.00, null, 0.81733252, 40.87, 447.61, 0.53, 0.00, null, null, + 971.30); + checkInst(model, 192, 192, LocalDate.of(2019, 7, 12), 190, 50.00, 50.00, null, 0.81646069, 40.82, 398.08, 0.48, 0.00, null, null, + 971.30); + checkInst(model, 193, 193, LocalDate.of(2019, 7, 13), 191, 50.00, 50.00, null, 0.81558979, 40.78, 348.51, 0.43, 0.00, null, null, + 971.30); + checkInst(model, 194, 194, LocalDate.of(2019, 7, 14), 192, 50.00, 50.00, null, 0.81471983, 40.74, 298.88, 0.37, 0.00, null, null, + 971.30); + checkInst(model, 195, 195, LocalDate.of(2019, 7, 15), 193, 50.00, 50.00, null, 0.81385078, 40.69, 249.20, 0.32, 0.00, null, null, + 971.30); + checkInst(model, 196, 196, LocalDate.of(2019, 7, 16), 194, 50.00, 50.00, null, 0.81298267, 40.65, 199.47, 0.27, 0.00, null, null, + 971.30); + checkInst(model, 197, 197, LocalDate.of(2019, 7, 17), 195, 50.00, 50.00, null, 0.81211548, 40.61, 149.68, 0.21, 0.00, null, null, + 971.30); + checkInst(model, 198, 198, LocalDate.of(2019, 7, 18), 196, 50.00, 50.00, null, 0.81124922, 40.56, 99.84, 0.16, 0.00, null, null, + 971.30); + checkInst(model, 199, 199, LocalDate.of(2019, 7, 19), 197, 50.00, 50.00, null, 0.81038388, 40.52, 49.95, 0.11, 0.00, null, null, + 971.30); + + assertEquals(200, model.projectedPayments().size(), "disbursement + 199 regular (period 200 removed, forecast was 0)"); + } + + @Test + void testLessPayment_term200_discountFee1000_netDisbursement9000_pay40() { + final ProjectedAmortizationScheduleModel model = generateModel(); + calculator.applyPayment(model, EXPECTED_DISBURSEMENT_DATE.plusDays(1), new BigDecimal("40")); + + checkInst(model, 0, 0, EXPECTED_DISBURSEMENT_DATE, 0, -9000.00, null, null, 1.00000000, -9000.00, 9000.00, null, null, null, null, + 1000.00); + + checkInst(model, 1, 1, LocalDate.of(2019, 1, 2), 0, 50.00, 50.00, 40.00, 1.00000000, 40.00, 8959.61, 9.61, 7.69, 7.69, -1.92, + 992.31); + + checkInst(model, 2, 2, LocalDate.of(2019, 1, 3), 1, 50.00, 50.00, null, 0.99893332, 49.95, 8919.18, 9.57, 0.00, null, null, 992.31); + checkInst(model, 3, 3, LocalDate.of(2019, 1, 4), 2, 50.00, 50.00, null, 0.99786779, 49.89, 8878.70, 9.52, 0.00, null, null, 992.31); + checkInst(model, 4, 4, LocalDate.of(2019, 1, 5), 3, 50.00, 50.00, null, 0.99680339, 49.84, 8838.18, 9.48, 0.00, null, null, 992.31); + checkInst(model, 5, 5, LocalDate.of(2019, 1, 6), 4, 50.00, 50.00, null, 0.99574012, 49.79, 8797.62, 9.44, 0.00, null, null, 992.31); + checkInst(model, 6, 6, LocalDate.of(2019, 1, 7), 5, 50.00, 50.00, null, 0.99467799, 49.73, 8757.01, 9.39, 0.00, null, null, 992.31); + checkInst(model, 7, 7, LocalDate.of(2019, 1, 8), 6, 50.00, 50.00, null, 0.99361699, 49.68, 8716.36, 9.35, 0.00, null, null, 992.31); + checkInst(model, 8, 8, LocalDate.of(2019, 1, 9), 7, 50.00, 50.00, null, 0.99255712, 49.63, 8675.67, 9.31, 0.00, null, null, 992.31); + checkInst(model, 9, 9, LocalDate.of(2019, 1, 10), 8, 50.00, 50.00, null, 0.99149839, 49.57, 8634.94, 9.26, 0.00, null, null, + 992.31); + checkInst(model, 10, 10, LocalDate.of(2019, 1, 11), 9, 50.00, 50.00, null, 0.99044078, 49.52, 8594.16, 9.22, 0.00, null, null, + 992.31); + checkInst(model, 11, 11, LocalDate.of(2019, 1, 12), 10, 50.00, 50.00, null, 0.98938430, 49.47, 8553.33, 9.18, 0.00, null, null, + 992.31); + checkInst(model, 12, 12, LocalDate.of(2019, 1, 13), 11, 50.00, 50.00, null, 0.98832895, 49.42, 8512.47, 9.13, 0.00, null, null, + 992.31); + checkInst(model, 13, 13, LocalDate.of(2019, 1, 14), 12, 50.00, 50.00, null, 0.98727472, 49.36, 8471.56, 9.09, 0.00, null, null, + 992.31); + checkInst(model, 14, 14, LocalDate.of(2019, 1, 15), 13, 50.00, 50.00, null, 0.98622162, 49.31, 8430.60, 9.05, 0.00, null, null, + 992.31); + checkInst(model, 15, 15, LocalDate.of(2019, 1, 16), 14, 50.00, 50.00, null, 0.98516964, 49.26, 8389.61, 9.00, 0.00, null, null, + 992.31); + checkInst(model, 16, 16, LocalDate.of(2019, 1, 17), 15, 50.00, 50.00, null, 0.98411879, 49.21, 8348.56, 8.96, 0.00, null, null, + 992.31); + checkInst(model, 17, 17, LocalDate.of(2019, 1, 18), 16, 50.00, 50.00, null, 0.98306905, 49.15, 8307.48, 8.91, 0.00, null, null, + 992.31); + checkInst(model, 18, 18, LocalDate.of(2019, 1, 19), 17, 50.00, 50.00, null, 0.98202044, 49.10, 8266.35, 8.87, 0.00, null, null, + 992.31); + checkInst(model, 19, 19, LocalDate.of(2019, 1, 20), 18, 50.00, 50.00, null, 0.98097294, 49.05, 8225.18, 8.83, 0.00, null, null, + 992.31); + checkInst(model, 20, 20, LocalDate.of(2019, 1, 21), 19, 50.00, 50.00, null, 0.97992656, 49.00, 8183.96, 8.78, 0.00, null, null, + 992.31); + checkInst(model, 21, 21, LocalDate.of(2019, 1, 22), 20, 50.00, 50.00, null, 0.97888129, 48.94, 8142.70, 8.74, 0.00, null, null, + 992.31); + checkInst(model, 22, 22, LocalDate.of(2019, 1, 23), 21, 50.00, 50.00, null, 0.97783715, 48.89, 8101.39, 8.69, 0.00, null, null, + 992.31); + checkInst(model, 23, 23, LocalDate.of(2019, 1, 24), 22, 50.00, 50.00, null, 0.97679411, 48.84, 8060.04, 8.65, 0.00, null, null, + 992.31); + checkInst(model, 24, 24, LocalDate.of(2019, 1, 25), 23, 50.00, 50.00, null, 0.97575219, 48.79, 8018.65, 8.61, 0.00, null, null, + 992.31); + checkInst(model, 25, 25, LocalDate.of(2019, 1, 26), 24, 50.00, 50.00, null, 0.97471138, 48.74, 7977.21, 8.56, 0.00, null, null, + 992.31); + checkInst(model, 26, 26, LocalDate.of(2019, 1, 27), 25, 50.00, 50.00, null, 0.97367168, 48.68, 7935.73, 8.52, 0.00, null, null, + 992.31); + checkInst(model, 27, 27, LocalDate.of(2019, 1, 28), 26, 50.00, 50.00, null, 0.97263309, 48.63, 7894.21, 8.47, 0.00, null, null, + 992.31); + checkInst(model, 28, 28, LocalDate.of(2019, 1, 29), 27, 50.00, 50.00, null, 0.97159560, 48.58, 7852.63, 8.43, 0.00, null, null, + 992.31); + checkInst(model, 29, 29, LocalDate.of(2019, 1, 30), 28, 50.00, 50.00, null, 0.97055922, 48.53, 7811.02, 8.39, 0.00, null, null, + 992.31); + checkInst(model, 30, 30, LocalDate.of(2019, 1, 31), 29, 50.00, 50.00, null, 0.96952395, 48.48, 7769.36, 8.34, 0.00, null, null, + 992.31); + checkInst(model, 31, 31, LocalDate.of(2019, 2, 1), 30, 50.00, 50.00, null, 0.96848979, 48.42, 7727.66, 8.30, 0.00, null, null, + 992.31); + checkInst(model, 32, 32, LocalDate.of(2019, 2, 2), 31, 50.00, 50.00, null, 0.96745672, 48.37, 7685.91, 8.25, 0.00, null, null, + 992.31); + checkInst(model, 33, 33, LocalDate.of(2019, 2, 3), 32, 50.00, 50.00, null, 0.96642476, 48.32, 7644.12, 8.21, 0.00, null, null, + 992.31); + checkInst(model, 34, 34, LocalDate.of(2019, 2, 4), 33, 50.00, 50.00, null, 0.96539390, 48.27, 7602.28, 8.16, 0.00, null, null, + 992.31); + checkInst(model, 35, 35, LocalDate.of(2019, 2, 5), 34, 50.00, 50.00, null, 0.96436413, 48.22, 7560.40, 8.12, 0.00, null, null, + 992.31); + checkInst(model, 36, 36, LocalDate.of(2019, 2, 6), 35, 50.00, 50.00, null, 0.96333547, 48.17, 7518.47, 8.07, 0.00, null, null, + 992.31); + checkInst(model, 37, 37, LocalDate.of(2019, 2, 7), 36, 50.00, 50.00, null, 0.96230790, 48.12, 7476.50, 8.03, 0.00, null, null, + 992.31); + checkInst(model, 38, 38, LocalDate.of(2019, 2, 8), 37, 50.00, 50.00, null, 0.96128143, 48.06, 7434.48, 7.98, 0.00, null, null, + 992.31); + checkInst(model, 39, 39, LocalDate.of(2019, 2, 9), 38, 50.00, 50.00, null, 0.96025606, 48.01, 7392.42, 7.94, 0.00, null, null, + 992.31); + checkInst(model, 40, 40, LocalDate.of(2019, 2, 10), 39, 50.00, 50.00, null, 0.95923178, 47.96, 7350.31, 7.89, 0.00, null, null, + 992.31); + checkInst(model, 41, 41, LocalDate.of(2019, 2, 11), 40, 50.00, 50.00, null, 0.95820859, 47.91, 7308.16, 7.85, 0.00, null, null, + 992.31); + checkInst(model, 42, 42, LocalDate.of(2019, 2, 12), 41, 50.00, 50.00, null, 0.95718649, 47.86, 7265.97, 7.80, 0.00, null, null, + 992.31); + checkInst(model, 43, 43, LocalDate.of(2019, 2, 13), 42, 50.00, 50.00, null, 0.95616548, 47.81, 7223.72, 7.76, 0.00, null, null, + 992.31); + checkInst(model, 44, 44, LocalDate.of(2019, 2, 14), 43, 50.00, 50.00, null, 0.95514557, 47.76, 7181.44, 7.71, 0.00, null, null, + 992.31); + checkInst(model, 45, 45, LocalDate.of(2019, 2, 15), 44, 50.00, 50.00, null, 0.95412674, 47.71, 7139.11, 7.67, 0.00, null, null, + 992.31); + checkInst(model, 46, 46, LocalDate.of(2019, 2, 16), 45, 50.00, 50.00, null, 0.95310899, 47.66, 7096.73, 7.62, 0.00, null, null, + 992.31); + checkInst(model, 47, 47, LocalDate.of(2019, 2, 17), 46, 50.00, 50.00, null, 0.95209233, 47.60, 7054.31, 7.58, 0.00, null, null, + 992.31); + checkInst(model, 48, 48, LocalDate.of(2019, 2, 18), 47, 50.00, 50.00, null, 0.95107676, 47.55, 7011.84, 7.53, 0.00, null, null, + 992.31); + checkInst(model, 49, 49, LocalDate.of(2019, 2, 19), 48, 50.00, 50.00, null, 0.95006227, 47.50, 6969.33, 7.49, 0.00, null, null, + 992.31); + checkInst(model, 50, 50, LocalDate.of(2019, 2, 20), 49, 50.00, 50.00, null, 0.94904886, 47.45, 6926.77, 7.44, 0.00, null, null, + 992.31); + checkInst(model, 51, 51, LocalDate.of(2019, 2, 21), 50, 50.00, 50.00, null, 0.94803653, 47.40, 6884.17, 7.40, 0.00, null, null, + 992.31); + checkInst(model, 52, 52, LocalDate.of(2019, 2, 22), 51, 50.00, 50.00, null, 0.94702529, 47.35, 6841.52, 7.35, 0.00, null, null, + 992.31); + checkInst(model, 53, 53, LocalDate.of(2019, 2, 23), 52, 50.00, 50.00, null, 0.94601512, 47.30, 6798.82, 7.31, 0.00, null, null, + 992.31); + checkInst(model, 54, 54, LocalDate.of(2019, 2, 24), 53, 50.00, 50.00, null, 0.94500603, 47.25, 6756.08, 7.26, 0.00, null, null, + 992.31); + checkInst(model, 55, 55, LocalDate.of(2019, 2, 25), 54, 50.00, 50.00, null, 0.94399801, 47.20, 6713.30, 7.21, 0.00, null, null, + 992.31); + checkInst(model, 56, 56, LocalDate.of(2019, 2, 26), 55, 50.00, 50.00, null, 0.94299107, 47.15, 6670.47, 7.17, 0.00, null, null, + 992.31); + checkInst(model, 57, 57, LocalDate.of(2019, 2, 27), 56, 50.00, 50.00, null, 0.94198521, 47.10, 6627.59, 7.12, 0.00, null, null, + 992.31); + checkInst(model, 58, 58, LocalDate.of(2019, 2, 28), 57, 50.00, 50.00, null, 0.94098042, 47.05, 6584.67, 7.08, 0.00, null, null, + 992.31); + checkInst(model, 59, 59, LocalDate.of(2019, 3, 1), 58, 50.00, 50.00, null, 0.93997669, 47.00, 6541.70, 7.03, 0.00, null, null, + 992.31); + checkInst(model, 60, 60, LocalDate.of(2019, 3, 2), 59, 50.00, 50.00, null, 0.93897404, 46.95, 6498.68, 6.99, 0.00, null, null, + 992.31); + checkInst(model, 61, 61, LocalDate.of(2019, 3, 3), 60, 50.00, 50.00, null, 0.93797246, 46.90, 6455.62, 6.94, 0.00, null, null, + 992.31); + checkInst(model, 62, 62, LocalDate.of(2019, 3, 4), 61, 50.00, 50.00, null, 0.93697195, 46.85, 6412.51, 6.89, 0.00, null, null, + 992.31); + checkInst(model, 63, 63, LocalDate.of(2019, 3, 5), 62, 50.00, 50.00, null, 0.93597251, 46.80, 6369.36, 6.85, 0.00, null, null, + 992.31); + checkInst(model, 64, 64, LocalDate.of(2019, 3, 6), 63, 50.00, 50.00, null, 0.93497413, 46.75, 6326.16, 6.80, 0.00, null, null, + 992.31); + checkInst(model, 65, 65, LocalDate.of(2019, 3, 7), 64, 50.00, 50.00, null, 0.93397681, 46.70, 6282.92, 6.76, 0.00, null, null, + 992.31); + checkInst(model, 66, 66, LocalDate.of(2019, 3, 8), 65, 50.00, 50.00, null, 0.93298056, 46.65, 6239.63, 6.71, 0.00, null, null, + 992.31); + checkInst(model, 67, 67, LocalDate.of(2019, 3, 9), 66, 50.00, 50.00, null, 0.93198538, 46.60, 6196.29, 6.66, 0.00, null, null, + 992.31); + checkInst(model, 68, 68, LocalDate.of(2019, 3, 10), 67, 50.00, 50.00, null, 0.93099125, 46.55, 6152.91, 6.62, 0.00, null, null, + 992.31); + checkInst(model, 69, 69, LocalDate.of(2019, 3, 11), 68, 50.00, 50.00, null, 0.92999818, 46.50, 6109.48, 6.57, 0.00, null, null, + 992.31); + checkInst(model, 70, 70, LocalDate.of(2019, 3, 12), 69, 50.00, 50.00, null, 0.92900618, 46.45, 6066.00, 6.52, 0.00, null, null, + 992.31); + checkInst(model, 71, 71, LocalDate.of(2019, 3, 13), 70, 50.00, 50.00, null, 0.92801523, 46.40, 6022.48, 6.48, 0.00, null, null, + 992.31); + checkInst(model, 72, 72, LocalDate.of(2019, 3, 14), 71, 50.00, 50.00, null, 0.92702534, 46.35, 5978.91, 6.43, 0.00, null, null, + 992.31); + checkInst(model, 73, 73, LocalDate.of(2019, 3, 15), 72, 50.00, 50.00, null, 0.92603650, 46.30, 5935.29, 6.38, 0.00, null, null, + 992.31); + checkInst(model, 74, 74, LocalDate.of(2019, 3, 16), 73, 50.00, 50.00, null, 0.92504872, 46.25, 5891.63, 6.34, 0.00, null, null, + 992.31); + checkInst(model, 75, 75, LocalDate.of(2019, 3, 17), 74, 50.00, 50.00, null, 0.92406200, 46.20, 5847.92, 6.29, 0.00, null, null, + 992.31); + checkInst(model, 76, 76, LocalDate.of(2019, 3, 18), 75, 50.00, 50.00, null, 0.92307632, 46.15, 5804.17, 6.24, 0.00, null, null, + 992.31); + checkInst(model, 77, 77, LocalDate.of(2019, 3, 19), 76, 50.00, 50.00, null, 0.92209170, 46.10, 5760.36, 6.20, 0.00, null, null, + 992.31); + checkInst(model, 78, 78, LocalDate.of(2019, 3, 20), 77, 50.00, 50.00, null, 0.92110813, 46.06, 5716.52, 6.15, 0.00, null, null, + 992.31); + checkInst(model, 79, 79, LocalDate.of(2019, 3, 21), 78, 50.00, 50.00, null, 0.92012560, 46.01, 5672.62, 6.10, 0.00, null, null, + 992.31); + checkInst(model, 80, 80, LocalDate.of(2019, 3, 22), 79, 50.00, 50.00, null, 0.91914413, 45.96, 5628.68, 6.06, 0.00, null, null, + 992.31); + checkInst(model, 81, 81, LocalDate.of(2019, 3, 23), 80, 50.00, 50.00, null, 0.91816370, 45.91, 5584.69, 6.01, 0.00, null, null, + 992.31); + checkInst(model, 82, 82, LocalDate.of(2019, 3, 24), 81, 50.00, 50.00, null, 0.91718432, 45.86, 5540.65, 5.96, 0.00, null, null, + 992.31); + checkInst(model, 83, 83, LocalDate.of(2019, 3, 25), 82, 50.00, 50.00, null, 0.91620598, 45.81, 5496.57, 5.92, 0.00, null, null, + 992.31); + checkInst(model, 84, 84, LocalDate.of(2019, 3, 26), 83, 50.00, 50.00, null, 0.91522868, 45.76, 5452.44, 5.87, 0.00, null, null, + 992.31); + checkInst(model, 85, 85, LocalDate.of(2019, 3, 27), 84, 50.00, 50.00, null, 0.91425243, 45.71, 5408.26, 5.82, 0.00, null, null, + 992.31); + checkInst(model, 86, 86, LocalDate.of(2019, 3, 28), 85, 50.00, 50.00, null, 0.91327722, 45.66, 5364.03, 5.78, 0.00, null, null, + 992.31); + checkInst(model, 87, 87, LocalDate.of(2019, 3, 29), 86, 50.00, 50.00, null, 0.91230305, 45.62, 5319.76, 5.73, 0.00, null, null, + 992.31); + checkInst(model, 88, 88, LocalDate.of(2019, 3, 30), 87, 50.00, 50.00, null, 0.91132992, 45.57, 5275.44, 5.68, 0.00, null, null, + 992.31); + checkInst(model, 89, 89, LocalDate.of(2019, 3, 31), 88, 50.00, 50.00, null, 0.91035783, 45.52, 5231.08, 5.63, 0.00, null, null, + 992.31); + checkInst(model, 90, 90, LocalDate.of(2019, 4, 1), 89, 50.00, 50.00, null, 0.90938677, 45.47, 5186.66, 5.59, 0.00, null, null, + 992.31); + checkInst(model, 91, 91, LocalDate.of(2019, 4, 2), 90, 50.00, 50.00, null, 0.90841675, 45.42, 5142.20, 5.54, 0.00, null, null, + 992.31); + checkInst(model, 92, 92, LocalDate.of(2019, 4, 3), 91, 50.00, 50.00, null, 0.90744776, 45.37, 5097.69, 5.49, 0.00, null, null, + 992.31); + checkInst(model, 93, 93, LocalDate.of(2019, 4, 4), 92, 50.00, 50.00, null, 0.90647981, 45.32, 5053.13, 5.44, 0.00, null, null, + 992.31); + checkInst(model, 94, 94, LocalDate.of(2019, 4, 5), 93, 50.00, 50.00, null, 0.90551289, 45.28, 5008.53, 5.40, 0.00, null, null, + 992.31); + checkInst(model, 95, 95, LocalDate.of(2019, 4, 6), 94, 50.00, 50.00, null, 0.90454700, 45.23, 4963.88, 5.35, 0.00, null, null, + 992.31); + checkInst(model, 96, 96, LocalDate.of(2019, 4, 7), 95, 50.00, 50.00, null, 0.90358215, 45.18, 4919.18, 5.30, 0.00, null, null, + 992.31); + checkInst(model, 97, 97, LocalDate.of(2019, 4, 8), 96, 50.00, 50.00, null, 0.90261832, 45.13, 4874.43, 5.25, 0.00, null, null, + 992.31); + checkInst(model, 98, 98, LocalDate.of(2019, 4, 9), 97, 50.00, 50.00, null, 0.90165552, 45.08, 4829.64, 5.20, 0.00, null, null, + 992.31); + checkInst(model, 99, 99, LocalDate.of(2019, 4, 10), 98, 50.00, 50.00, null, 0.90069374, 45.03, 4784.79, 5.16, 0.00, null, null, + 992.31); + checkInst(model, 100, 100, LocalDate.of(2019, 4, 11), 99, 50.00, 50.00, null, 0.89973299, 44.99, 4739.90, 5.11, 0.00, null, null, + 992.31); + checkInst(model, 101, 101, LocalDate.of(2019, 4, 12), 100, 50.00, 50.00, null, 0.89877327, 44.94, 4694.96, 5.06, 0.00, null, null, + 992.31); + checkInst(model, 102, 102, LocalDate.of(2019, 4, 13), 101, 50.00, 50.00, null, 0.89781457, 44.89, 4649.98, 5.01, 0.00, null, null, + 992.31); + checkInst(model, 103, 103, LocalDate.of(2019, 4, 14), 102, 50.00, 50.00, null, 0.89685689, 44.84, 4604.94, 4.97, 0.00, null, null, + 992.31); + checkInst(model, 104, 104, LocalDate.of(2019, 4, 15), 103, 50.00, 50.00, null, 0.89590024, 44.80, 4559.86, 4.92, 0.00, null, null, + 992.31); + checkInst(model, 105, 105, LocalDate.of(2019, 4, 16), 104, 50.00, 50.00, null, 0.89494460, 44.75, 4514.73, 4.87, 0.00, null, null, + 992.31); + checkInst(model, 106, 106, LocalDate.of(2019, 4, 17), 105, 50.00, 50.00, null, 0.89398999, 44.70, 4469.55, 4.82, 0.00, null, null, + 992.31); + checkInst(model, 107, 107, LocalDate.of(2019, 4, 18), 106, 50.00, 50.00, null, 0.89303639, 44.65, 4424.32, 4.77, 0.00, null, null, + 992.31); + checkInst(model, 108, 108, LocalDate.of(2019, 4, 19), 107, 50.00, 50.00, null, 0.89208381, 44.60, 4379.05, 4.72, 0.00, null, null, + 992.31); + checkInst(model, 109, 109, LocalDate.of(2019, 4, 20), 108, 50.00, 50.00, null, 0.89113225, 44.56, 4333.72, 4.68, 0.00, null, null, + 992.31); + checkInst(model, 110, 110, LocalDate.of(2019, 4, 21), 109, 50.00, 50.00, null, 0.89018170, 44.51, 4288.35, 4.63, 0.00, null, null, + 992.31); + checkInst(model, 111, 111, LocalDate.of(2019, 4, 22), 110, 50.00, 50.00, null, 0.88923216, 44.46, 4242.93, 4.58, 0.00, null, null, + 992.31); + checkInst(model, 112, 112, LocalDate.of(2019, 4, 23), 111, 50.00, 50.00, null, 0.88828364, 44.41, 4197.46, 4.53, 0.00, null, null, + 992.31); + checkInst(model, 113, 113, LocalDate.of(2019, 4, 24), 112, 50.00, 50.00, null, 0.88733613, 44.37, 4151.94, 4.48, 0.00, null, null, + 992.31); + checkInst(model, 114, 114, LocalDate.of(2019, 4, 25), 113, 50.00, 50.00, null, 0.88638963, 44.32, 4106.38, 4.43, 0.00, null, null, + 992.31); + checkInst(model, 115, 115, LocalDate.of(2019, 4, 26), 114, 50.00, 50.00, null, 0.88544414, 44.27, 4060.76, 4.38, 0.00, null, null, + 992.31); + checkInst(model, 116, 116, LocalDate.of(2019, 4, 27), 115, 50.00, 50.00, null, 0.88449966, 44.22, 4015.10, 4.34, 0.00, null, null, + 992.31); + checkInst(model, 117, 117, LocalDate.of(2019, 4, 28), 116, 50.00, 50.00, null, 0.88355619, 44.18, 3969.38, 4.29, 0.00, null, null, + 992.31); + checkInst(model, 118, 118, LocalDate.of(2019, 4, 29), 117, 50.00, 50.00, null, 0.88261372, 44.13, 3923.62, 4.24, 0.00, null, null, + 992.31); + checkInst(model, 119, 119, LocalDate.of(2019, 4, 30), 118, 50.00, 50.00, null, 0.88167226, 44.08, 3877.81, 4.19, 0.00, null, null, + 992.31); + checkInst(model, 120, 120, LocalDate.of(2019, 5, 1), 119, 50.00, 50.00, null, 0.88073180, 44.04, 3831.95, 4.14, 0.00, null, null, + 992.31); + checkInst(model, 121, 121, LocalDate.of(2019, 5, 2), 120, 50.00, 50.00, null, 0.87979234, 43.99, 3786.04, 4.09, 0.00, null, null, + 992.31); + checkInst(model, 122, 122, LocalDate.of(2019, 5, 3), 121, 50.00, 50.00, null, 0.87885389, 43.94, 3740.09, 4.04, 0.00, null, null, + 992.31); + checkInst(model, 123, 123, LocalDate.of(2019, 5, 4), 122, 50.00, 50.00, null, 0.87791644, 43.90, 3694.08, 3.99, 0.00, null, null, + 992.31); + checkInst(model, 124, 124, LocalDate.of(2019, 5, 5), 123, 50.00, 50.00, null, 0.87697999, 43.85, 3648.03, 3.94, 0.00, null, null, + 992.31); + checkInst(model, 125, 125, LocalDate.of(2019, 5, 6), 124, 50.00, 50.00, null, 0.87604453, 43.80, 3601.92, 3.90, 0.00, null, null, + 992.31); + checkInst(model, 126, 126, LocalDate.of(2019, 5, 7), 125, 50.00, 50.00, null, 0.87511008, 43.76, 3555.77, 3.85, 0.00, null, null, + 992.31); + checkInst(model, 127, 127, LocalDate.of(2019, 5, 8), 126, 50.00, 50.00, null, 0.87417662, 43.71, 3509.56, 3.80, 0.00, null, null, + 992.31); + checkInst(model, 128, 128, LocalDate.of(2019, 5, 9), 127, 50.00, 50.00, null, 0.87324416, 43.66, 3463.31, 3.75, 0.00, null, null, + 992.31); + checkInst(model, 129, 129, LocalDate.of(2019, 5, 10), 128, 50.00, 50.00, null, 0.87231269, 43.62, 3417.01, 3.70, 0.00, null, null, + 992.31); + checkInst(model, 130, 130, LocalDate.of(2019, 5, 11), 129, 50.00, 50.00, null, 0.87138221, 43.57, 3370.66, 3.65, 0.00, null, null, + 992.31); + checkInst(model, 131, 131, LocalDate.of(2019, 5, 12), 130, 50.00, 50.00, null, 0.87045273, 43.52, 3324.26, 3.60, 0.00, null, null, + 992.31); + checkInst(model, 132, 132, LocalDate.of(2019, 5, 13), 131, 50.00, 50.00, null, 0.86952424, 43.48, 3277.81, 3.55, 0.00, null, null, + 992.31); + checkInst(model, 133, 133, LocalDate.of(2019, 5, 14), 132, 50.00, 50.00, null, 0.86859674, 43.43, 3231.31, 3.50, 0.00, null, null, + 992.31); + checkInst(model, 134, 134, LocalDate.of(2019, 5, 15), 133, 50.00, 50.00, null, 0.86767023, 43.38, 3184.76, 3.45, 0.00, null, null, + 992.31); + checkInst(model, 135, 135, LocalDate.of(2019, 5, 16), 134, 50.00, 50.00, null, 0.86674471, 43.34, 3138.16, 3.40, 0.00, null, null, + 992.31); + checkInst(model, 136, 136, LocalDate.of(2019, 5, 17), 135, 50.00, 50.00, null, 0.86582017, 43.29, 3091.51, 3.35, 0.00, null, null, + 992.31); + checkInst(model, 137, 137, LocalDate.of(2019, 5, 18), 136, 50.00, 50.00, null, 0.86489662, 43.24, 3044.81, 3.30, 0.00, null, null, + 992.31); + checkInst(model, 138, 138, LocalDate.of(2019, 5, 19), 137, 50.00, 50.00, null, 0.86397406, 43.20, 2998.06, 3.25, 0.00, null, null, + 992.31); + checkInst(model, 139, 139, LocalDate.of(2019, 5, 20), 138, 50.00, 50.00, null, 0.86305248, 43.15, 2951.26, 3.20, 0.00, null, null, + 992.31); + checkInst(model, 140, 140, LocalDate.of(2019, 5, 21), 139, 50.00, 50.00, null, 0.86213188, 43.11, 2904.42, 3.15, 0.00, null, null, + 992.31); + checkInst(model, 141, 141, LocalDate.of(2019, 5, 22), 140, 50.00, 50.00, null, 0.86121227, 43.06, 2857.52, 3.10, 0.00, null, null, + 992.31); + checkInst(model, 142, 142, LocalDate.of(2019, 5, 23), 141, 50.00, 50.00, null, 0.86029363, 43.01, 2810.57, 3.05, 0.00, null, null, + 992.31); + checkInst(model, 143, 143, LocalDate.of(2019, 5, 24), 142, 50.00, 50.00, null, 0.85937598, 42.97, 2763.57, 3.00, 0.00, null, null, + 992.31); + checkInst(model, 144, 144, LocalDate.of(2019, 5, 25), 143, 50.00, 50.00, null, 0.85845930, 42.92, 2716.52, 2.95, 0.00, null, null, + 992.31); + checkInst(model, 145, 145, LocalDate.of(2019, 5, 26), 144, 50.00, 50.00, null, 0.85754361, 42.88, 2669.42, 2.90, 0.00, null, null, + 992.31); + checkInst(model, 146, 146, LocalDate.of(2019, 5, 27), 145, 50.00, 50.00, null, 0.85662889, 42.83, 2622.27, 2.85, 0.00, null, null, + 992.31); + checkInst(model, 147, 147, LocalDate.of(2019, 5, 28), 146, 50.00, 50.00, null, 0.85571514, 42.79, 2575.07, 2.80, 0.00, null, null, + 992.31); + checkInst(model, 148, 148, LocalDate.of(2019, 5, 29), 147, 50.00, 50.00, null, 0.85480237, 42.74, 2527.82, 2.75, 0.00, null, null, + 992.31); + checkInst(model, 149, 149, LocalDate.of(2019, 5, 30), 148, 50.00, 50.00, null, 0.85389057, 42.69, 2480.52, 2.70, 0.00, null, null, + 992.31); + checkInst(model, 150, 150, LocalDate.of(2019, 5, 31), 149, 50.00, 50.00, null, 0.85297975, 42.65, 2433.17, 2.65, 0.00, null, null, + 992.31); + checkInst(model, 151, 151, LocalDate.of(2019, 6, 1), 150, 50.00, 50.00, null, 0.85206990, 42.60, 2385.77, 2.60, 0.00, null, null, + 992.31); + checkInst(model, 152, 152, LocalDate.of(2019, 6, 2), 151, 50.00, 50.00, null, 0.85116101, 42.56, 2338.31, 2.55, 0.00, null, null, + 992.31); + checkInst(model, 153, 153, LocalDate.of(2019, 6, 3), 152, 50.00, 50.00, null, 0.85025310, 42.51, 2290.81, 2.50, 0.00, null, null, + 992.31); + checkInst(model, 154, 154, LocalDate.of(2019, 6, 4), 153, 50.00, 50.00, null, 0.84934616, 42.47, 2243.26, 2.45, 0.00, null, null, + 992.31); + checkInst(model, 155, 155, LocalDate.of(2019, 6, 5), 154, 50.00, 50.00, null, 0.84844018, 42.42, 2195.65, 2.40, 0.00, null, null, + 992.31); + checkInst(model, 156, 156, LocalDate.of(2019, 6, 6), 155, 50.00, 50.00, null, 0.84753517, 42.38, 2148.00, 2.34, 0.00, null, null, + 992.31); + checkInst(model, 157, 157, LocalDate.of(2019, 6, 7), 156, 50.00, 50.00, null, 0.84663113, 42.33, 2100.29, 2.29, 0.00, null, null, + 992.31); + checkInst(model, 158, 158, LocalDate.of(2019, 6, 8), 157, 50.00, 50.00, null, 0.84572805, 42.29, 2052.53, 2.24, 0.00, null, null, + 992.31); + checkInst(model, 159, 159, LocalDate.of(2019, 6, 9), 158, 50.00, 50.00, null, 0.84482593, 42.24, 2004.73, 2.19, 0.00, null, null, + 992.31); + checkInst(model, 160, 160, LocalDate.of(2019, 6, 10), 159, 50.00, 50.00, null, 0.84392477, 42.20, 1956.87, 2.14, 0.00, null, null, + 992.31); + checkInst(model, 161, 161, LocalDate.of(2019, 6, 11), 160, 50.00, 50.00, null, 0.84302458, 42.15, 1908.96, 2.09, 0.00, null, null, + 992.31); + checkInst(model, 162, 162, LocalDate.of(2019, 6, 12), 161, 50.00, 50.00, null, 0.84212535, 42.11, 1860.99, 2.04, 0.00, null, null, + 992.31); + checkInst(model, 163, 163, LocalDate.of(2019, 6, 13), 162, 50.00, 50.00, null, 0.84122707, 42.06, 1812.98, 1.99, 0.00, null, null, + 992.31); + checkInst(model, 164, 164, LocalDate.of(2019, 6, 14), 163, 50.00, 50.00, null, 0.84032975, 42.02, 1764.92, 1.94, 0.00, null, null, + 992.31); + checkInst(model, 165, 165, LocalDate.of(2019, 6, 15), 164, 50.00, 50.00, null, 0.83943340, 41.97, 1716.80, 1.88, 0.00, null, null, + 992.31); + checkInst(model, 166, 166, LocalDate.of(2019, 6, 16), 165, 50.00, 50.00, null, 0.83853799, 41.93, 1668.64, 1.83, 0.00, null, null, + 992.31); + checkInst(model, 167, 167, LocalDate.of(2019, 6, 17), 166, 50.00, 50.00, null, 0.83764354, 41.88, 1620.42, 1.78, 0.00, null, null, + 992.31); + checkInst(model, 168, 168, LocalDate.of(2019, 6, 18), 167, 50.00, 50.00, null, 0.83675005, 41.84, 1572.15, 1.73, 0.00, null, null, + 992.31); + checkInst(model, 169, 169, LocalDate.of(2019, 6, 19), 168, 50.00, 50.00, null, 0.83585751, 41.79, 1523.83, 1.68, 0.00, null, null, + 992.31); + checkInst(model, 170, 170, LocalDate.of(2019, 6, 20), 169, 50.00, 50.00, null, 0.83496592, 41.75, 1475.45, 1.63, 0.00, null, null, + 992.31); + checkInst(model, 171, 171, LocalDate.of(2019, 6, 21), 170, 50.00, 50.00, null, 0.83407528, 41.70, 1427.03, 1.58, 0.00, null, null, + 992.31); + checkInst(model, 172, 172, LocalDate.of(2019, 6, 22), 171, 50.00, 50.00, null, 0.83318560, 41.66, 1378.55, 1.52, 0.00, null, null, + 992.31); + checkInst(model, 173, 173, LocalDate.of(2019, 6, 23), 172, 50.00, 50.00, null, 0.83229686, 41.61, 1330.02, 1.47, 0.00, null, null, + 992.31); + checkInst(model, 174, 174, LocalDate.of(2019, 6, 24), 173, 50.00, 50.00, null, 0.83140907, 41.57, 1281.45, 1.42, 0.00, null, null, + 992.31); + checkInst(model, 175, 175, LocalDate.of(2019, 6, 25), 174, 50.00, 50.00, null, 0.83052222, 41.53, 1232.81, 1.37, 0.00, null, null, + 992.31); + checkInst(model, 176, 176, LocalDate.of(2019, 6, 26), 175, 50.00, 50.00, null, 0.82963633, 41.48, 1184.13, 1.32, 0.00, null, null, + 992.31); + checkInst(model, 177, 177, LocalDate.of(2019, 6, 27), 176, 50.00, 50.00, null, 0.82875137, 41.44, 1135.39, 1.26, 0.00, null, null, + 992.31); + checkInst(model, 178, 178, LocalDate.of(2019, 6, 28), 177, 50.00, 50.00, null, 0.82786736, 41.39, 1086.61, 1.21, 0.00, null, null, + 992.31); + checkInst(model, 179, 179, LocalDate.of(2019, 6, 29), 178, 50.00, 50.00, null, 0.82698430, 41.35, 1037.77, 1.16, 0.00, null, null, + 992.31); + checkInst(model, 180, 180, LocalDate.of(2019, 6, 30), 179, 50.00, 50.00, null, 0.82610217, 41.31, 988.88, 1.11, 0.00, null, null, + 992.31); + checkInst(model, 181, 181, LocalDate.of(2019, 7, 1), 180, 50.00, 50.00, null, 0.82522099, 41.26, 939.93, 1.06, 0.00, null, null, + 992.31); + checkInst(model, 182, 182, LocalDate.of(2019, 7, 2), 181, 50.00, 50.00, null, 0.82434075, 41.22, 890.93, 1.00, 0.00, null, null, + 992.31); + checkInst(model, 183, 183, LocalDate.of(2019, 7, 3), 182, 50.00, 50.00, null, 0.82346144, 41.17, 841.89, 0.95, 0.00, null, null, + 992.31); + checkInst(model, 184, 184, LocalDate.of(2019, 7, 4), 183, 50.00, 50.00, null, 0.82258308, 41.13, 792.79, 0.90, 0.00, null, null, + 992.31); + checkInst(model, 185, 185, LocalDate.of(2019, 7, 5), 184, 50.00, 50.00, null, 0.82170565, 41.09, 743.63, 0.85, 0.00, null, null, + 992.31); + checkInst(model, 186, 186, LocalDate.of(2019, 7, 6), 185, 50.00, 50.00, null, 0.82082916, 41.04, 694.43, 0.79, 0.00, null, null, + 992.31); + checkInst(model, 187, 187, LocalDate.of(2019, 7, 7), 186, 50.00, 50.00, null, 0.81995360, 41.00, 645.17, 0.74, 0.00, null, null, + 992.31); + checkInst(model, 188, 188, LocalDate.of(2019, 7, 8), 187, 50.00, 50.00, null, 0.81907897, 40.95, 595.86, 0.69, 0.00, null, null, + 992.31); + checkInst(model, 189, 189, LocalDate.of(2019, 7, 9), 188, 50.00, 50.00, null, 0.81820528, 40.91, 546.49, 0.64, 0.00, null, null, + 992.31); + checkInst(model, 190, 190, LocalDate.of(2019, 7, 10), 189, 50.00, 50.00, null, 0.81733252, 40.87, 497.08, 0.58, 0.00, null, null, + 992.31); + checkInst(model, 191, 191, LocalDate.of(2019, 7, 11), 190, 50.00, 50.00, null, 0.81646069, 40.82, 447.61, 0.53, 0.00, null, null, + 992.31); + checkInst(model, 192, 192, LocalDate.of(2019, 7, 12), 191, 50.00, 50.00, null, 0.81558979, 40.78, 398.08, 0.48, 0.00, null, null, + 992.31); + checkInst(model, 193, 193, LocalDate.of(2019, 7, 13), 192, 50.00, 50.00, null, 0.81471983, 40.74, 348.51, 0.43, 0.00, null, null, + 992.31); + checkInst(model, 194, 194, LocalDate.of(2019, 7, 14), 193, 50.00, 50.00, null, 0.81385078, 40.69, 298.88, 0.37, 0.00, null, null, + 992.31); + checkInst(model, 195, 195, LocalDate.of(2019, 7, 15), 194, 50.00, 50.00, null, 0.81298267, 40.65, 249.20, 0.32, 0.00, null, null, + 992.31); + checkInst(model, 196, 196, LocalDate.of(2019, 7, 16), 195, 50.00, 50.00, null, 0.81211548, 40.61, 199.47, 0.27, 0.00, null, null, + 992.31); + checkInst(model, 197, 197, LocalDate.of(2019, 7, 17), 196, 50.00, 50.00, null, 0.81124922, 40.56, 149.68, 0.21, 0.00, null, null, + 992.31); + checkInst(model, 198, 198, LocalDate.of(2019, 7, 18), 197, 50.00, 50.00, null, 0.81038388, 40.52, 99.84, 0.16, 0.00, null, null, + 992.31); + checkInst(model, 199, 199, LocalDate.of(2019, 7, 19), 198, 50.00, 50.00, null, 0.80951946, 40.48, 49.95, 0.11, 0.00, null, null, + 992.31); + checkInst(model, 200, 200, LocalDate.of(2019, 7, 20), 199, 50.00, 50.00, null, 0.80865597, 40.43, 0.00, 0.05, 0.00, null, null, + 992.31); + + assertEquals(202, model.projectedPayments().size(), "disbursement + 200 regular + 1 additional"); + checkInst(model, 201, 201, LocalDate.of(2019, 7, 21), 200, null, 10.00, null, 0.80779339, 8.08, null, null, 0.00, null, null, null); + } + + @Test + void testNoPayment_term200_discountFee1000_netDisbursement9000_pay0_0_50() { + final ProjectedAmortizationScheduleModel model = generateModel(); + calculator.applyPayment(model, EXPECTED_DISBURSEMENT_DATE.plusDays(1), BigDecimal.ZERO); + calculator.applyPayment(model, EXPECTED_DISBURSEMENT_DATE.plusDays(2), BigDecimal.ZERO); + calculator.applyPayment(model, EXPECTED_DISBURSEMENT_DATE.plusDays(3), new BigDecimal("50")); + + checkInst(model, 0, 0, EXPECTED_DISBURSEMENT_DATE, 0, -9000.00, null, null, 1.00000000, -9000.00, 9000.00, null, null, null, null, + 1000.00); + + checkInst(model, 1, 1, LocalDate.of(2019, 1, 2), 0, 50.00, 50.00, 0.00, 1.00000000, 0.00, 8959.61, 9.61, 9.61, 0.00, null, 1000.00); + checkInst(model, 2, 2, LocalDate.of(2019, 1, 3), 0, 50.00, 50.00, 0.00, 1.00000000, 0.00, 8919.18, 9.57, 9.61, 0.00, null, 1000.00); + checkInst(model, 3, 3, LocalDate.of(2019, 1, 4), 0, 50.00, 50.00, 50.00, 1.00000000, 50.00, 8878.70, 9.52, 9.61, 9.61, 0.09, + 990.39); + + checkInst(model, 4, 4, LocalDate.of(2019, 1, 5), 1, 50.00, 50.00, null, 0.99893332, 49.95, 8838.18, 9.48, 0.00, null, null, 990.39); + checkInst(model, 5, 5, LocalDate.of(2019, 1, 6), 2, 50.00, 50.00, null, 0.99786779, 49.89, 8797.62, 9.44, 0.00, null, null, 990.39); + checkInst(model, 6, 6, LocalDate.of(2019, 1, 7), 3, 50.00, 50.00, null, 0.99680339, 49.84, 8757.01, 9.39, 0.00, null, null, 990.39); + checkInst(model, 7, 7, LocalDate.of(2019, 1, 8), 4, 50.00, 50.00, null, 0.99574012, 49.79, 8716.36, 9.35, 0.00, null, null, 990.39); + checkInst(model, 8, 8, LocalDate.of(2019, 1, 9), 5, 50.00, 50.00, null, 0.99467799, 49.73, 8675.67, 9.31, 0.00, null, null, 990.39); + checkInst(model, 9, 9, LocalDate.of(2019, 1, 10), 6, 50.00, 50.00, null, 0.99361699, 49.68, 8634.94, 9.26, 0.00, null, null, + 990.39); + checkInst(model, 10, 10, LocalDate.of(2019, 1, 11), 7, 50.00, 50.00, null, 0.99255712, 49.63, 8594.16, 9.22, 0.00, null, null, + 990.39); + checkInst(model, 11, 11, LocalDate.of(2019, 1, 12), 8, 50.00, 50.00, null, 0.99149839, 49.57, 8553.33, 9.18, 0.00, null, null, + 990.39); + checkInst(model, 12, 12, LocalDate.of(2019, 1, 13), 9, 50.00, 50.00, null, 0.99044078, 49.52, 8512.47, 9.13, 0.00, null, null, + 990.39); + checkInst(model, 13, 13, LocalDate.of(2019, 1, 14), 10, 50.00, 50.00, null, 0.98938430, 49.47, 8471.56, 9.09, 0.00, null, null, + 990.39); + checkInst(model, 14, 14, LocalDate.of(2019, 1, 15), 11, 50.00, 50.00, null, 0.98832895, 49.42, 8430.60, 9.05, 0.00, null, null, + 990.39); + checkInst(model, 15, 15, LocalDate.of(2019, 1, 16), 12, 50.00, 50.00, null, 0.98727472, 49.36, 8389.61, 9.00, 0.00, null, null, + 990.39); + checkInst(model, 16, 16, LocalDate.of(2019, 1, 17), 13, 50.00, 50.00, null, 0.98622162, 49.31, 8348.56, 8.96, 0.00, null, null, + 990.39); + checkInst(model, 17, 17, LocalDate.of(2019, 1, 18), 14, 50.00, 50.00, null, 0.98516964, 49.26, 8307.48, 8.91, 0.00, null, null, + 990.39); + checkInst(model, 18, 18, LocalDate.of(2019, 1, 19), 15, 50.00, 50.00, null, 0.98411879, 49.21, 8266.35, 8.87, 0.00, null, null, + 990.39); + checkInst(model, 19, 19, LocalDate.of(2019, 1, 20), 16, 50.00, 50.00, null, 0.98306905, 49.15, 8225.18, 8.83, 0.00, null, null, + 990.39); + checkInst(model, 20, 20, LocalDate.of(2019, 1, 21), 17, 50.00, 50.00, null, 0.98202044, 49.10, 8183.96, 8.78, 0.00, null, null, + 990.39); + checkInst(model, 21, 21, LocalDate.of(2019, 1, 22), 18, 50.00, 50.00, null, 0.98097294, 49.05, 8142.70, 8.74, 0.00, null, null, + 990.39); + checkInst(model, 22, 22, LocalDate.of(2019, 1, 23), 19, 50.00, 50.00, null, 0.97992656, 49.00, 8101.39, 8.69, 0.00, null, null, + 990.39); + checkInst(model, 23, 23, LocalDate.of(2019, 1, 24), 20, 50.00, 50.00, null, 0.97888129, 48.94, 8060.04, 8.65, 0.00, null, null, + 990.39); + checkInst(model, 24, 24, LocalDate.of(2019, 1, 25), 21, 50.00, 50.00, null, 0.97783715, 48.89, 8018.65, 8.61, 0.00, null, null, + 990.39); + checkInst(model, 25, 25, LocalDate.of(2019, 1, 26), 22, 50.00, 50.00, null, 0.97679411, 48.84, 7977.21, 8.56, 0.00, null, null, + 990.39); + checkInst(model, 26, 26, LocalDate.of(2019, 1, 27), 23, 50.00, 50.00, null, 0.97575219, 48.79, 7935.73, 8.52, 0.00, null, null, + 990.39); + checkInst(model, 27, 27, LocalDate.of(2019, 1, 28), 24, 50.00, 50.00, null, 0.97471138, 48.74, 7894.21, 8.47, 0.00, null, null, + 990.39); + checkInst(model, 28, 28, LocalDate.of(2019, 1, 29), 25, 50.00, 50.00, null, 0.97367168, 48.68, 7852.63, 8.43, 0.00, null, null, + 990.39); + checkInst(model, 29, 29, LocalDate.of(2019, 1, 30), 26, 50.00, 50.00, null, 0.97263309, 48.63, 7811.02, 8.39, 0.00, null, null, + 990.39); + checkInst(model, 30, 30, LocalDate.of(2019, 1, 31), 27, 50.00, 50.00, null, 0.97159560, 48.58, 7769.36, 8.34, 0.00, null, null, + 990.39); + checkInst(model, 31, 31, LocalDate.of(2019, 2, 1), 28, 50.00, 50.00, null, 0.97055922, 48.53, 7727.66, 8.30, 0.00, null, null, + 990.39); + checkInst(model, 32, 32, LocalDate.of(2019, 2, 2), 29, 50.00, 50.00, null, 0.96952395, 48.48, 7685.91, 8.25, 0.00, null, null, + 990.39); + checkInst(model, 33, 33, LocalDate.of(2019, 2, 3), 30, 50.00, 50.00, null, 0.96848979, 48.42, 7644.12, 8.21, 0.00, null, null, + 990.39); + checkInst(model, 34, 34, LocalDate.of(2019, 2, 4), 31, 50.00, 50.00, null, 0.96745672, 48.37, 7602.28, 8.16, 0.00, null, null, + 990.39); + checkInst(model, 35, 35, LocalDate.of(2019, 2, 5), 32, 50.00, 50.00, null, 0.96642476, 48.32, 7560.40, 8.12, 0.00, null, null, + 990.39); + checkInst(model, 36, 36, LocalDate.of(2019, 2, 6), 33, 50.00, 50.00, null, 0.96539390, 48.27, 7518.47, 8.07, 0.00, null, null, + 990.39); + checkInst(model, 37, 37, LocalDate.of(2019, 2, 7), 34, 50.00, 50.00, null, 0.96436413, 48.22, 7476.50, 8.03, 0.00, null, null, + 990.39); + checkInst(model, 38, 38, LocalDate.of(2019, 2, 8), 35, 50.00, 50.00, null, 0.96333547, 48.17, 7434.48, 7.98, 0.00, null, null, + 990.39); + checkInst(model, 39, 39, LocalDate.of(2019, 2, 9), 36, 50.00, 50.00, null, 0.96230790, 48.12, 7392.42, 7.94, 0.00, null, null, + 990.39); + checkInst(model, 40, 40, LocalDate.of(2019, 2, 10), 37, 50.00, 50.00, null, 0.96128143, 48.06, 7350.31, 7.89, 0.00, null, null, + 990.39); + checkInst(model, 41, 41, LocalDate.of(2019, 2, 11), 38, 50.00, 50.00, null, 0.96025606, 48.01, 7308.16, 7.85, 0.00, null, null, + 990.39); + checkInst(model, 42, 42, LocalDate.of(2019, 2, 12), 39, 50.00, 50.00, null, 0.95923178, 47.96, 7265.97, 7.80, 0.00, null, null, + 990.39); + checkInst(model, 43, 43, LocalDate.of(2019, 2, 13), 40, 50.00, 50.00, null, 0.95820859, 47.91, 7223.72, 7.76, 0.00, null, null, + 990.39); + checkInst(model, 44, 44, LocalDate.of(2019, 2, 14), 41, 50.00, 50.00, null, 0.95718649, 47.86, 7181.44, 7.71, 0.00, null, null, + 990.39); + checkInst(model, 45, 45, LocalDate.of(2019, 2, 15), 42, 50.00, 50.00, null, 0.95616548, 47.81, 7139.11, 7.67, 0.00, null, null, + 990.39); + checkInst(model, 46, 46, LocalDate.of(2019, 2, 16), 43, 50.00, 50.00, null, 0.95514557, 47.76, 7096.73, 7.62, 0.00, null, null, + 990.39); + checkInst(model, 47, 47, LocalDate.of(2019, 2, 17), 44, 50.00, 50.00, null, 0.95412674, 47.71, 7054.31, 7.58, 0.00, null, null, + 990.39); + checkInst(model, 48, 48, LocalDate.of(2019, 2, 18), 45, 50.00, 50.00, null, 0.95310899, 47.66, 7011.84, 7.53, 0.00, null, null, + 990.39); + checkInst(model, 49, 49, LocalDate.of(2019, 2, 19), 46, 50.00, 50.00, null, 0.95209233, 47.60, 6969.33, 7.49, 0.00, null, null, + 990.39); + checkInst(model, 50, 50, LocalDate.of(2019, 2, 20), 47, 50.00, 50.00, null, 0.95107676, 47.55, 6926.77, 7.44, 0.00, null, null, + 990.39); + checkInst(model, 51, 51, LocalDate.of(2019, 2, 21), 48, 50.00, 50.00, null, 0.95006227, 47.50, 6884.17, 7.40, 0.00, null, null, + 990.39); + checkInst(model, 52, 52, LocalDate.of(2019, 2, 22), 49, 50.00, 50.00, null, 0.94904886, 47.45, 6841.52, 7.35, 0.00, null, null, + 990.39); + checkInst(model, 53, 53, LocalDate.of(2019, 2, 23), 50, 50.00, 50.00, null, 0.94803653, 47.40, 6798.82, 7.31, 0.00, null, null, + 990.39); + checkInst(model, 54, 54, LocalDate.of(2019, 2, 24), 51, 50.00, 50.00, null, 0.94702529, 47.35, 6756.08, 7.26, 0.00, null, null, + 990.39); + checkInst(model, 55, 55, LocalDate.of(2019, 2, 25), 52, 50.00, 50.00, null, 0.94601512, 47.30, 6713.30, 7.21, 0.00, null, null, + 990.39); + checkInst(model, 56, 56, LocalDate.of(2019, 2, 26), 53, 50.00, 50.00, null, 0.94500603, 47.25, 6670.47, 7.17, 0.00, null, null, + 990.39); + checkInst(model, 57, 57, LocalDate.of(2019, 2, 27), 54, 50.00, 50.00, null, 0.94399801, 47.20, 6627.59, 7.12, 0.00, null, null, + 990.39); + checkInst(model, 58, 58, LocalDate.of(2019, 2, 28), 55, 50.00, 50.00, null, 0.94299107, 47.15, 6584.67, 7.08, 0.00, null, null, + 990.39); + checkInst(model, 59, 59, LocalDate.of(2019, 3, 1), 56, 50.00, 50.00, null, 0.94198521, 47.10, 6541.70, 7.03, 0.00, null, null, + 990.39); + checkInst(model, 60, 60, LocalDate.of(2019, 3, 2), 57, 50.00, 50.00, null, 0.94098042, 47.05, 6498.68, 6.99, 0.00, null, null, + 990.39); + checkInst(model, 61, 61, LocalDate.of(2019, 3, 3), 58, 50.00, 50.00, null, 0.93997669, 47.00, 6455.62, 6.94, 0.00, null, null, + 990.39); + checkInst(model, 62, 62, LocalDate.of(2019, 3, 4), 59, 50.00, 50.00, null, 0.93897404, 46.95, 6412.51, 6.89, 0.00, null, null, + 990.39); + checkInst(model, 63, 63, LocalDate.of(2019, 3, 5), 60, 50.00, 50.00, null, 0.93797246, 46.90, 6369.36, 6.85, 0.00, null, null, + 990.39); + checkInst(model, 64, 64, LocalDate.of(2019, 3, 6), 61, 50.00, 50.00, null, 0.93697195, 46.85, 6326.16, 6.80, 0.00, null, null, + 990.39); + checkInst(model, 65, 65, LocalDate.of(2019, 3, 7), 62, 50.00, 50.00, null, 0.93597251, 46.80, 6282.92, 6.76, 0.00, null, null, + 990.39); + checkInst(model, 66, 66, LocalDate.of(2019, 3, 8), 63, 50.00, 50.00, null, 0.93497413, 46.75, 6239.63, 6.71, 0.00, null, null, + 990.39); + checkInst(model, 67, 67, LocalDate.of(2019, 3, 9), 64, 50.00, 50.00, null, 0.93397681, 46.70, 6196.29, 6.66, 0.00, null, null, + 990.39); + checkInst(model, 68, 68, LocalDate.of(2019, 3, 10), 65, 50.00, 50.00, null, 0.93298056, 46.65, 6152.91, 6.62, 0.00, null, null, + 990.39); + checkInst(model, 69, 69, LocalDate.of(2019, 3, 11), 66, 50.00, 50.00, null, 0.93198538, 46.60, 6109.48, 6.57, 0.00, null, null, + 990.39); + checkInst(model, 70, 70, LocalDate.of(2019, 3, 12), 67, 50.00, 50.00, null, 0.93099125, 46.55, 6066.00, 6.52, 0.00, null, null, + 990.39); + checkInst(model, 71, 71, LocalDate.of(2019, 3, 13), 68, 50.00, 50.00, null, 0.92999818, 46.50, 6022.48, 6.48, 0.00, null, null, + 990.39); + checkInst(model, 72, 72, LocalDate.of(2019, 3, 14), 69, 50.00, 50.00, null, 0.92900618, 46.45, 5978.91, 6.43, 0.00, null, null, + 990.39); + checkInst(model, 73, 73, LocalDate.of(2019, 3, 15), 70, 50.00, 50.00, null, 0.92801523, 46.40, 5935.29, 6.38, 0.00, null, null, + 990.39); + checkInst(model, 74, 74, LocalDate.of(2019, 3, 16), 71, 50.00, 50.00, null, 0.92702534, 46.35, 5891.63, 6.34, 0.00, null, null, + 990.39); + checkInst(model, 75, 75, LocalDate.of(2019, 3, 17), 72, 50.00, 50.00, null, 0.92603650, 46.30, 5847.92, 6.29, 0.00, null, null, + 990.39); + checkInst(model, 76, 76, LocalDate.of(2019, 3, 18), 73, 50.00, 50.00, null, 0.92504872, 46.25, 5804.17, 6.24, 0.00, null, null, + 990.39); + checkInst(model, 77, 77, LocalDate.of(2019, 3, 19), 74, 50.00, 50.00, null, 0.92406200, 46.20, 5760.36, 6.20, 0.00, null, null, + 990.39); + checkInst(model, 78, 78, LocalDate.of(2019, 3, 20), 75, 50.00, 50.00, null, 0.92307632, 46.15, 5716.52, 6.15, 0.00, null, null, + 990.39); + checkInst(model, 79, 79, LocalDate.of(2019, 3, 21), 76, 50.00, 50.00, null, 0.92209170, 46.10, 5672.62, 6.10, 0.00, null, null, + 990.39); + checkInst(model, 80, 80, LocalDate.of(2019, 3, 22), 77, 50.00, 50.00, null, 0.92110813, 46.06, 5628.68, 6.06, 0.00, null, null, + 990.39); + checkInst(model, 81, 81, LocalDate.of(2019, 3, 23), 78, 50.00, 50.00, null, 0.92012560, 46.01, 5584.69, 6.01, 0.00, null, null, + 990.39); + checkInst(model, 82, 82, LocalDate.of(2019, 3, 24), 79, 50.00, 50.00, null, 0.91914413, 45.96, 5540.65, 5.96, 0.00, null, null, + 990.39); + checkInst(model, 83, 83, LocalDate.of(2019, 3, 25), 80, 50.00, 50.00, null, 0.91816370, 45.91, 5496.57, 5.92, 0.00, null, null, + 990.39); + checkInst(model, 84, 84, LocalDate.of(2019, 3, 26), 81, 50.00, 50.00, null, 0.91718432, 45.86, 5452.44, 5.87, 0.00, null, null, + 990.39); + checkInst(model, 85, 85, LocalDate.of(2019, 3, 27), 82, 50.00, 50.00, null, 0.91620598, 45.81, 5408.26, 5.82, 0.00, null, null, + 990.39); + checkInst(model, 86, 86, LocalDate.of(2019, 3, 28), 83, 50.00, 50.00, null, 0.91522868, 45.76, 5364.03, 5.78, 0.00, null, null, + 990.39); + checkInst(model, 87, 87, LocalDate.of(2019, 3, 29), 84, 50.00, 50.00, null, 0.91425243, 45.71, 5319.76, 5.73, 0.00, null, null, + 990.39); + checkInst(model, 88, 88, LocalDate.of(2019, 3, 30), 85, 50.00, 50.00, null, 0.91327722, 45.66, 5275.44, 5.68, 0.00, null, null, + 990.39); + checkInst(model, 89, 89, LocalDate.of(2019, 3, 31), 86, 50.00, 50.00, null, 0.91230305, 45.62, 5231.08, 5.63, 0.00, null, null, + 990.39); + checkInst(model, 90, 90, LocalDate.of(2019, 4, 1), 87, 50.00, 50.00, null, 0.91132992, 45.57, 5186.66, 5.59, 0.00, null, null, + 990.39); + checkInst(model, 91, 91, LocalDate.of(2019, 4, 2), 88, 50.00, 50.00, null, 0.91035783, 45.52, 5142.20, 5.54, 0.00, null, null, + 990.39); + checkInst(model, 92, 92, LocalDate.of(2019, 4, 3), 89, 50.00, 50.00, null, 0.90938677, 45.47, 5097.69, 5.49, 0.00, null, null, + 990.39); + checkInst(model, 93, 93, LocalDate.of(2019, 4, 4), 90, 50.00, 50.00, null, 0.90841675, 45.42, 5053.13, 5.44, 0.00, null, null, + 990.39); + checkInst(model, 94, 94, LocalDate.of(2019, 4, 5), 91, 50.00, 50.00, null, 0.90744776, 45.37, 5008.53, 5.40, 0.00, null, null, + 990.39); + checkInst(model, 95, 95, LocalDate.of(2019, 4, 6), 92, 50.00, 50.00, null, 0.90647981, 45.32, 4963.88, 5.35, 0.00, null, null, + 990.39); + checkInst(model, 96, 96, LocalDate.of(2019, 4, 7), 93, 50.00, 50.00, null, 0.90551289, 45.28, 4919.18, 5.30, 0.00, null, null, + 990.39); + checkInst(model, 97, 97, LocalDate.of(2019, 4, 8), 94, 50.00, 50.00, null, 0.90454700, 45.23, 4874.43, 5.25, 0.00, null, null, + 990.39); + checkInst(model, 98, 98, LocalDate.of(2019, 4, 9), 95, 50.00, 50.00, null, 0.90358215, 45.18, 4829.64, 5.20, 0.00, null, null, + 990.39); + checkInst(model, 99, 99, LocalDate.of(2019, 4, 10), 96, 50.00, 50.00, null, 0.90261832, 45.13, 4784.79, 5.16, 0.00, null, null, + 990.39); + checkInst(model, 100, 100, LocalDate.of(2019, 4, 11), 97, 50.00, 50.00, null, 0.90165552, 45.08, 4739.90, 5.11, 0.00, null, null, + 990.39); + checkInst(model, 101, 101, LocalDate.of(2019, 4, 12), 98, 50.00, 50.00, null, 0.90069374, 45.03, 4694.96, 5.06, 0.00, null, null, + 990.39); + checkInst(model, 102, 102, LocalDate.of(2019, 4, 13), 99, 50.00, 50.00, null, 0.89973299, 44.99, 4649.98, 5.01, 0.00, null, null, + 990.39); + checkInst(model, 103, 103, LocalDate.of(2019, 4, 14), 100, 50.00, 50.00, null, 0.89877327, 44.94, 4604.94, 4.97, 0.00, null, null, + 990.39); + checkInst(model, 104, 104, LocalDate.of(2019, 4, 15), 101, 50.00, 50.00, null, 0.89781457, 44.89, 4559.86, 4.92, 0.00, null, null, + 990.39); + checkInst(model, 105, 105, LocalDate.of(2019, 4, 16), 102, 50.00, 50.00, null, 0.89685689, 44.84, 4514.73, 4.87, 0.00, null, null, + 990.39); + checkInst(model, 106, 106, LocalDate.of(2019, 4, 17), 103, 50.00, 50.00, null, 0.89590024, 44.80, 4469.55, 4.82, 0.00, null, null, + 990.39); + checkInst(model, 107, 107, LocalDate.of(2019, 4, 18), 104, 50.00, 50.00, null, 0.89494460, 44.75, 4424.32, 4.77, 0.00, null, null, + 990.39); + checkInst(model, 108, 108, LocalDate.of(2019, 4, 19), 105, 50.00, 50.00, null, 0.89398999, 44.70, 4379.05, 4.72, 0.00, null, null, + 990.39); + checkInst(model, 109, 109, LocalDate.of(2019, 4, 20), 106, 50.00, 50.00, null, 0.89303639, 44.65, 4333.72, 4.68, 0.00, null, null, + 990.39); + checkInst(model, 110, 110, LocalDate.of(2019, 4, 21), 107, 50.00, 50.00, null, 0.89208381, 44.60, 4288.35, 4.63, 0.00, null, null, + 990.39); + checkInst(model, 111, 111, LocalDate.of(2019, 4, 22), 108, 50.00, 50.00, null, 0.89113225, 44.56, 4242.93, 4.58, 0.00, null, null, + 990.39); + checkInst(model, 112, 112, LocalDate.of(2019, 4, 23), 109, 50.00, 50.00, null, 0.89018170, 44.51, 4197.46, 4.53, 0.00, null, null, + 990.39); + checkInst(model, 113, 113, LocalDate.of(2019, 4, 24), 110, 50.00, 50.00, null, 0.88923216, 44.46, 4151.94, 4.48, 0.00, null, null, + 990.39); + checkInst(model, 114, 114, LocalDate.of(2019, 4, 25), 111, 50.00, 50.00, null, 0.88828364, 44.41, 4106.38, 4.43, 0.00, null, null, + 990.39); + checkInst(model, 115, 115, LocalDate.of(2019, 4, 26), 112, 50.00, 50.00, null, 0.88733613, 44.37, 4060.76, 4.38, 0.00, null, null, + 990.39); + checkInst(model, 116, 116, LocalDate.of(2019, 4, 27), 113, 50.00, 50.00, null, 0.88638963, 44.32, 4015.10, 4.34, 0.00, null, null, + 990.39); + checkInst(model, 117, 117, LocalDate.of(2019, 4, 28), 114, 50.00, 50.00, null, 0.88544414, 44.27, 3969.38, 4.29, 0.00, null, null, + 990.39); + checkInst(model, 118, 118, LocalDate.of(2019, 4, 29), 115, 50.00, 50.00, null, 0.88449966, 44.22, 3923.62, 4.24, 0.00, null, null, + 990.39); + checkInst(model, 119, 119, LocalDate.of(2019, 4, 30), 116, 50.00, 50.00, null, 0.88355619, 44.18, 3877.81, 4.19, 0.00, null, null, + 990.39); + checkInst(model, 120, 120, LocalDate.of(2019, 5, 1), 117, 50.00, 50.00, null, 0.88261372, 44.13, 3831.95, 4.14, 0.00, null, null, + 990.39); + checkInst(model, 121, 121, LocalDate.of(2019, 5, 2), 118, 50.00, 50.00, null, 0.88167226, 44.08, 3786.04, 4.09, 0.00, null, null, + 990.39); + checkInst(model, 122, 122, LocalDate.of(2019, 5, 3), 119, 50.00, 50.00, null, 0.88073180, 44.04, 3740.09, 4.04, 0.00, null, null, + 990.39); + checkInst(model, 123, 123, LocalDate.of(2019, 5, 4), 120, 50.00, 50.00, null, 0.87979234, 43.99, 3694.08, 3.99, 0.00, null, null, + 990.39); + checkInst(model, 124, 124, LocalDate.of(2019, 5, 5), 121, 50.00, 50.00, null, 0.87885389, 43.94, 3648.03, 3.94, 0.00, null, null, + 990.39); + checkInst(model, 125, 125, LocalDate.of(2019, 5, 6), 122, 50.00, 50.00, null, 0.87791644, 43.90, 3601.92, 3.90, 0.00, null, null, + 990.39); + checkInst(model, 126, 126, LocalDate.of(2019, 5, 7), 123, 50.00, 50.00, null, 0.87697999, 43.85, 3555.77, 3.85, 0.00, null, null, + 990.39); + checkInst(model, 127, 127, LocalDate.of(2019, 5, 8), 124, 50.00, 50.00, null, 0.87604453, 43.80, 3509.56, 3.80, 0.00, null, null, + 990.39); + checkInst(model, 128, 128, LocalDate.of(2019, 5, 9), 125, 50.00, 50.00, null, 0.87511008, 43.76, 3463.31, 3.75, 0.00, null, null, + 990.39); + checkInst(model, 129, 129, LocalDate.of(2019, 5, 10), 126, 50.00, 50.00, null, 0.87417662, 43.71, 3417.01, 3.70, 0.00, null, null, + 990.39); + checkInst(model, 130, 130, LocalDate.of(2019, 5, 11), 127, 50.00, 50.00, null, 0.87324416, 43.66, 3370.66, 3.65, 0.00, null, null, + 990.39); + checkInst(model, 131, 131, LocalDate.of(2019, 5, 12), 128, 50.00, 50.00, null, 0.87231269, 43.62, 3324.26, 3.60, 0.00, null, null, + 990.39); + checkInst(model, 132, 132, LocalDate.of(2019, 5, 13), 129, 50.00, 50.00, null, 0.87138221, 43.57, 3277.81, 3.55, 0.00, null, null, + 990.39); + checkInst(model, 133, 133, LocalDate.of(2019, 5, 14), 130, 50.00, 50.00, null, 0.87045273, 43.52, 3231.31, 3.50, 0.00, null, null, + 990.39); + checkInst(model, 134, 134, LocalDate.of(2019, 5, 15), 131, 50.00, 50.00, null, 0.86952424, 43.48, 3184.76, 3.45, 0.00, null, null, + 990.39); + checkInst(model, 135, 135, LocalDate.of(2019, 5, 16), 132, 50.00, 50.00, null, 0.86859674, 43.43, 3138.16, 3.40, 0.00, null, null, + 990.39); + checkInst(model, 136, 136, LocalDate.of(2019, 5, 17), 133, 50.00, 50.00, null, 0.86767023, 43.38, 3091.51, 3.35, 0.00, null, null, + 990.39); + checkInst(model, 137, 137, LocalDate.of(2019, 5, 18), 134, 50.00, 50.00, null, 0.86674471, 43.34, 3044.81, 3.30, 0.00, null, null, + 990.39); + checkInst(model, 138, 138, LocalDate.of(2019, 5, 19), 135, 50.00, 50.00, null, 0.86582017, 43.29, 2998.06, 3.25, 0.00, null, null, + 990.39); + checkInst(model, 139, 139, LocalDate.of(2019, 5, 20), 136, 50.00, 50.00, null, 0.86489662, 43.24, 2951.26, 3.20, 0.00, null, null, + 990.39); + checkInst(model, 140, 140, LocalDate.of(2019, 5, 21), 137, 50.00, 50.00, null, 0.86397406, 43.20, 2904.42, 3.15, 0.00, null, null, + 990.39); + checkInst(model, 141, 141, LocalDate.of(2019, 5, 22), 138, 50.00, 50.00, null, 0.86305248, 43.15, 2857.52, 3.10, 0.00, null, null, + 990.39); + checkInst(model, 142, 142, LocalDate.of(2019, 5, 23), 139, 50.00, 50.00, null, 0.86213188, 43.11, 2810.57, 3.05, 0.00, null, null, + 990.39); + checkInst(model, 143, 143, LocalDate.of(2019, 5, 24), 140, 50.00, 50.00, null, 0.86121227, 43.06, 2763.57, 3.00, 0.00, null, null, + 990.39); + checkInst(model, 144, 144, LocalDate.of(2019, 5, 25), 141, 50.00, 50.00, null, 0.86029363, 43.01, 2716.52, 2.95, 0.00, null, null, + 990.39); + checkInst(model, 145, 145, LocalDate.of(2019, 5, 26), 142, 50.00, 50.00, null, 0.85937598, 42.97, 2669.42, 2.90, 0.00, null, null, + 990.39); + checkInst(model, 146, 146, LocalDate.of(2019, 5, 27), 143, 50.00, 50.00, null, 0.85845930, 42.92, 2622.27, 2.85, 0.00, null, null, + 990.39); + checkInst(model, 147, 147, LocalDate.of(2019, 5, 28), 144, 50.00, 50.00, null, 0.85754361, 42.88, 2575.07, 2.80, 0.00, null, null, + 990.39); + checkInst(model, 148, 148, LocalDate.of(2019, 5, 29), 145, 50.00, 50.00, null, 0.85662889, 42.83, 2527.82, 2.75, 0.00, null, null, + 990.39); + checkInst(model, 149, 149, LocalDate.of(2019, 5, 30), 146, 50.00, 50.00, null, 0.85571514, 42.79, 2480.52, 2.70, 0.00, null, null, + 990.39); + checkInst(model, 150, 150, LocalDate.of(2019, 5, 31), 147, 50.00, 50.00, null, 0.85480237, 42.74, 2433.17, 2.65, 0.00, null, null, + 990.39); + checkInst(model, 151, 151, LocalDate.of(2019, 6, 1), 148, 50.00, 50.00, null, 0.85389057, 42.69, 2385.77, 2.60, 0.00, null, null, + 990.39); + checkInst(model, 152, 152, LocalDate.of(2019, 6, 2), 149, 50.00, 50.00, null, 0.85297975, 42.65, 2338.31, 2.55, 0.00, null, null, + 990.39); + checkInst(model, 153, 153, LocalDate.of(2019, 6, 3), 150, 50.00, 50.00, null, 0.85206990, 42.60, 2290.81, 2.50, 0.00, null, null, + 990.39); + checkInst(model, 154, 154, LocalDate.of(2019, 6, 4), 151, 50.00, 50.00, null, 0.85116101, 42.56, 2243.26, 2.45, 0.00, null, null, + 990.39); + checkInst(model, 155, 155, LocalDate.of(2019, 6, 5), 152, 50.00, 50.00, null, 0.85025310, 42.51, 2195.65, 2.40, 0.00, null, null, + 990.39); + checkInst(model, 156, 156, LocalDate.of(2019, 6, 6), 153, 50.00, 50.00, null, 0.84934616, 42.47, 2148.00, 2.34, 0.00, null, null, + 990.39); + checkInst(model, 157, 157, LocalDate.of(2019, 6, 7), 154, 50.00, 50.00, null, 0.84844018, 42.42, 2100.29, 2.29, 0.00, null, null, + 990.39); + checkInst(model, 158, 158, LocalDate.of(2019, 6, 8), 155, 50.00, 50.00, null, 0.84753517, 42.38, 2052.53, 2.24, 0.00, null, null, + 990.39); + checkInst(model, 159, 159, LocalDate.of(2019, 6, 9), 156, 50.00, 50.00, null, 0.84663113, 42.33, 2004.73, 2.19, 0.00, null, null, + 990.39); + checkInst(model, 160, 160, LocalDate.of(2019, 6, 10), 157, 50.00, 50.00, null, 0.84572805, 42.29, 1956.87, 2.14, 0.00, null, null, + 990.39); + checkInst(model, 161, 161, LocalDate.of(2019, 6, 11), 158, 50.00, 50.00, null, 0.84482593, 42.24, 1908.96, 2.09, 0.00, null, null, + 990.39); + checkInst(model, 162, 162, LocalDate.of(2019, 6, 12), 159, 50.00, 50.00, null, 0.84392477, 42.20, 1860.99, 2.04, 0.00, null, null, + 990.39); + checkInst(model, 163, 163, LocalDate.of(2019, 6, 13), 160, 50.00, 50.00, null, 0.84302458, 42.15, 1812.98, 1.99, 0.00, null, null, + 990.39); + checkInst(model, 164, 164, LocalDate.of(2019, 6, 14), 161, 50.00, 50.00, null, 0.84212535, 42.11, 1764.92, 1.94, 0.00, null, null, + 990.39); + checkInst(model, 165, 165, LocalDate.of(2019, 6, 15), 162, 50.00, 50.00, null, 0.84122707, 42.06, 1716.80, 1.88, 0.00, null, null, + 990.39); + checkInst(model, 166, 166, LocalDate.of(2019, 6, 16), 163, 50.00, 50.00, null, 0.84032975, 42.02, 1668.64, 1.83, 0.00, null, null, + 990.39); + checkInst(model, 167, 167, LocalDate.of(2019, 6, 17), 164, 50.00, 50.00, null, 0.83943340, 41.97, 1620.42, 1.78, 0.00, null, null, + 990.39); + checkInst(model, 168, 168, LocalDate.of(2019, 6, 18), 165, 50.00, 50.00, null, 0.83853799, 41.93, 1572.15, 1.73, 0.00, null, null, + 990.39); + checkInst(model, 169, 169, LocalDate.of(2019, 6, 19), 166, 50.00, 50.00, null, 0.83764354, 41.88, 1523.83, 1.68, 0.00, null, null, + 990.39); + checkInst(model, 170, 170, LocalDate.of(2019, 6, 20), 167, 50.00, 50.00, null, 0.83675005, 41.84, 1475.45, 1.63, 0.00, null, null, + 990.39); + checkInst(model, 171, 171, LocalDate.of(2019, 6, 21), 168, 50.00, 50.00, null, 0.83585751, 41.79, 1427.03, 1.58, 0.00, null, null, + 990.39); + checkInst(model, 172, 172, LocalDate.of(2019, 6, 22), 169, 50.00, 50.00, null, 0.83496592, 41.75, 1378.55, 1.52, 0.00, null, null, + 990.39); + checkInst(model, 173, 173, LocalDate.of(2019, 6, 23), 170, 50.00, 50.00, null, 0.83407528, 41.70, 1330.02, 1.47, 0.00, null, null, + 990.39); + checkInst(model, 174, 174, LocalDate.of(2019, 6, 24), 171, 50.00, 50.00, null, 0.83318560, 41.66, 1281.45, 1.42, 0.00, null, null, + 990.39); + checkInst(model, 175, 175, LocalDate.of(2019, 6, 25), 172, 50.00, 50.00, null, 0.83229686, 41.61, 1232.81, 1.37, 0.00, null, null, + 990.39); + checkInst(model, 176, 176, LocalDate.of(2019, 6, 26), 173, 50.00, 50.00, null, 0.83140907, 41.57, 1184.13, 1.32, 0.00, null, null, + 990.39); + checkInst(model, 177, 177, LocalDate.of(2019, 6, 27), 174, 50.00, 50.00, null, 0.83052222, 41.53, 1135.39, 1.26, 0.00, null, null, + 990.39); + checkInst(model, 178, 178, LocalDate.of(2019, 6, 28), 175, 50.00, 50.00, null, 0.82963633, 41.48, 1086.61, 1.21, 0.00, null, null, + 990.39); + checkInst(model, 179, 179, LocalDate.of(2019, 6, 29), 176, 50.00, 50.00, null, 0.82875137, 41.44, 1037.77, 1.16, 0.00, null, null, + 990.39); + checkInst(model, 180, 180, LocalDate.of(2019, 6, 30), 177, 50.00, 50.00, null, 0.82786736, 41.39, 988.88, 1.11, 0.00, null, null, + 990.39); + checkInst(model, 181, 181, LocalDate.of(2019, 7, 1), 178, 50.00, 50.00, null, 0.82698430, 41.35, 939.93, 1.06, 0.00, null, null, + 990.39); + checkInst(model, 182, 182, LocalDate.of(2019, 7, 2), 179, 50.00, 50.00, null, 0.82610217, 41.31, 890.93, 1.00, 0.00, null, null, + 990.39); + checkInst(model, 183, 183, LocalDate.of(2019, 7, 3), 180, 50.00, 50.00, null, 0.82522099, 41.26, 841.89, 0.95, 0.00, null, null, + 990.39); + checkInst(model, 184, 184, LocalDate.of(2019, 7, 4), 181, 50.00, 50.00, null, 0.82434075, 41.22, 792.79, 0.90, 0.00, null, null, + 990.39); + checkInst(model, 185, 185, LocalDate.of(2019, 7, 5), 182, 50.00, 50.00, null, 0.82346144, 41.17, 743.63, 0.85, 0.00, null, null, + 990.39); + checkInst(model, 186, 186, LocalDate.of(2019, 7, 6), 183, 50.00, 50.00, null, 0.82258308, 41.13, 694.43, 0.79, 0.00, null, null, + 990.39); + checkInst(model, 187, 187, LocalDate.of(2019, 7, 7), 184, 50.00, 50.00, null, 0.82170565, 41.09, 645.17, 0.74, 0.00, null, null, + 990.39); + checkInst(model, 188, 188, LocalDate.of(2019, 7, 8), 185, 50.00, 50.00, null, 0.82082916, 41.04, 595.86, 0.69, 0.00, null, null, + 990.39); + checkInst(model, 189, 189, LocalDate.of(2019, 7, 9), 186, 50.00, 50.00, null, 0.81995360, 41.00, 546.49, 0.64, 0.00, null, null, + 990.39); + checkInst(model, 190, 190, LocalDate.of(2019, 7, 10), 187, 50.00, 50.00, null, 0.81907897, 40.95, 497.08, 0.58, 0.00, null, null, + 990.39); + checkInst(model, 191, 191, LocalDate.of(2019, 7, 11), 188, 50.00, 50.00, null, 0.81820528, 40.91, 447.61, 0.53, 0.00, null, null, + 990.39); + checkInst(model, 192, 192, LocalDate.of(2019, 7, 12), 189, 50.00, 50.00, null, 0.81733252, 40.87, 398.08, 0.48, 0.00, null, null, + 990.39); + checkInst(model, 193, 193, LocalDate.of(2019, 7, 13), 190, 50.00, 50.00, null, 0.81646069, 40.82, 348.51, 0.43, 0.00, null, null, + 990.39); + checkInst(model, 194, 194, LocalDate.of(2019, 7, 14), 191, 50.00, 50.00, null, 0.81558979, 40.78, 298.88, 0.37, 0.00, null, null, + 990.39); + checkInst(model, 195, 195, LocalDate.of(2019, 7, 15), 192, 50.00, 50.00, null, 0.81471983, 40.74, 249.20, 0.32, 0.00, null, null, + 990.39); + checkInst(model, 196, 196, LocalDate.of(2019, 7, 16), 193, 50.00, 50.00, null, 0.81385078, 40.69, 199.47, 0.27, 0.00, null, null, + 990.39); + checkInst(model, 197, 197, LocalDate.of(2019, 7, 17), 194, 50.00, 50.00, null, 0.81298267, 40.65, 149.68, 0.21, 0.00, null, null, + 990.39); + checkInst(model, 198, 198, LocalDate.of(2019, 7, 18), 195, 50.00, 50.00, null, 0.81211548, 40.61, 99.84, 0.16, 0.00, null, null, + 990.39); + checkInst(model, 199, 199, LocalDate.of(2019, 7, 19), 196, 50.00, 50.00, null, 0.81124922, 40.56, 49.95, 0.11, 0.00, null, null, + 990.39); + checkInst(model, 200, 200, LocalDate.of(2019, 7, 20), 197, 50.00, 50.00, null, 0.81038388, 40.52, 0.00, 0.05, 0.00, null, null, + 990.39); - final BigDecimal newNetDisbursement = new BigDecimal("430"); - final LocalDate newDisbursementDate = LocalDate.of(2019, 1, 5); + assertEquals(203, model.projectedPayments().size(), "disbursement + 200 regular + 2 additional"); + checkInst(model, 201, 201, LocalDate.of(2019, 7, 21), 198, null, 50.00, null, 0.80951946, 40.48, null, null, 0.00, null, null, + null); + checkInst(model, 202, 202, LocalDate.of(2019, 7, 22), 199, null, 50.00, null, 0.80865597, 40.43, null, null, 0.00, null, null, + null); + } - final ProjectedAmortizationScheduleModel model2 = calculator.addDisbursement(model1, originationFee, newNetDisbursement, - newDisbursementDate); + @Test + void testLessPayment_term10_discountFee50_netDisbursement450_pay40() { + final BigDecimal smallDiscountFee = new BigDecimal("50"); + final BigDecimal smallNetDisbursement = new BigDecimal("450"); + final ProjectedAmortizationScheduleModel initial = calculator.generateModel(smallDiscountFee, smallNetDisbursement, TPV, RATE, + DAY_COUNT, EXPECTED_DISBURSEMENT_DATE, MC, CURRENCY); + final ProjectedAmortizationScheduleModel model = calculator.addDisbursement(initial, smallDiscountFee, smallNetDisbursement, + EXPECTED_DISBURSEMENT_DATE); - assertEquals(10, model2.loanTerm()); - assertEquals(11, model2.payments().size()); + calculator.applyPayment(model, EXPECTED_DISBURSEMENT_DATE.plusDays(1), new BigDecimal("40")); - checkInst(model2, 0, 0, LocalDate.of(2019, 1, 5), 10, 0, -430.00, null, null, 1.00000000, -430.00, 430.00, null, null, null, null, - 50.00); - checkInst(model2, 1, 1, LocalDate.of(2019, 1, 6), 9, 1, 50.00, 50.00, null, 0.97237826, 48.62, 392.21, 12.21, 0.00, null, -12.21, - 50.00); - checkInst(model2, 2, 2, LocalDate.of(2019, 1, 7), 8, 2, 50.00, 50.00, null, 0.94551948, 47.28, 353.36, 11.14, 0.00, null, -11.14, - 50.00); - checkInst(model2, 3, 3, LocalDate.of(2019, 1, 8), 7, 3, 50.00, 50.00, null, 0.91940259, 45.97, 313.39, 10.04, 0.00, null, -10.04, - 50.00); - checkInst(model2, 4, 4, LocalDate.of(2019, 1, 9), 6, 4, 50.00, 50.00, null, 0.89400709, 44.70, 272.30, 8.90, 0.00, null, -8.90, - 50.00); - checkInst(model2, 5, 5, LocalDate.of(2019, 1, 10), 5, 5, 50.00, 50.00, null, 0.86931306, 43.47, 230.03, 7.73, 0.00, null, -7.73, - 50.00); - checkInst(model2, 6, 6, LocalDate.of(2019, 1, 11), 4, 6, 50.00, 50.00, null, 0.84530113, 42.27, 186.57, 6.53, 0.00, null, -6.53, - 50.00); - checkInst(model2, 7, 7, LocalDate.of(2019, 1, 12), 3, 7, 50.00, 50.00, null, 0.82195244, 41.10, 141.87, 5.30, 0.00, null, -5.30, - 50.00); - checkInst(model2, 8, 8, LocalDate.of(2019, 1, 13), 2, 8, 50.00, 50.00, null, 0.79924869, 39.96, 95.89, 4.03, 0.00, null, -4.03, - 50.00); - checkInst(model2, 9, 9, LocalDate.of(2019, 1, 14), 1, 9, 50.00, 50.00, null, 0.77717205, 38.86, 48.62, 2.72, 0.00, null, -2.72, - 50.00); - checkInst(model2, 10, 10, LocalDate.of(2019, 1, 15), 0, 10, 50.00, 50.00, null, 0.75570520, 37.79, 0.00, 1.38, 0.00, null, -1.38, + assertEquals(12, model.projectedPayments().size(), "disbursement + 10 regular + 1 tail"); + + checkInst(model, 0, 0, EXPECTED_DISBURSEMENT_DATE, 0, -450.00, null, null, 1.00000000, -450.00, 450.00, null, null, null, null, 50.00); - } - @Test - void testProjectedSchedule_term200_originationFee1000_netDisbursement9000() { - final ProjectedAmortizationScheduleModel model = generateModel(); + checkInst(model, 1, 1, LocalDate.of(2019, 1, 2), 0, 50.00, 50.00, 40.00, 1.00000000, 40.00, 408.83, 8.83, 7.07, 7.06, -1.77, 42.94); + checkInst(model, 2, 2, LocalDate.of(2019, 1, 3), 1, 50.00, 50.00, null, 0.98074794, 49.04, 366.86, 8.03, 0.00, null, null, 42.94); + checkInst(model, 3, 3, LocalDate.of(2019, 1, 4), 2, 50.00, 50.00, null, 0.96186652, 48.09, 324.06, 7.20, 0.00, null, null, 42.94); + checkInst(model, 4, 4, LocalDate.of(2019, 1, 5), 3, 50.00, 50.00, null, 0.94334860, 47.17, 280.42, 6.36, 0.00, null, null, 42.94); + checkInst(model, 5, 5, LocalDate.of(2019, 1, 6), 4, 50.00, 50.00, null, 0.92518720, 46.26, 235.93, 5.50, 0.00, null, null, 42.94); + checkInst(model, 6, 6, LocalDate.of(2019, 1, 7), 5, 50.00, 50.00, null, 0.90737544, 45.37, 190.56, 4.63, 0.00, null, null, 42.94); + checkInst(model, 7, 7, LocalDate.of(2019, 1, 8), 6, 50.00, 50.00, null, 0.88990659, 44.50, 144.30, 3.74, 0.00, null, null, 42.94); + checkInst(model, 8, 8, LocalDate.of(2019, 1, 9), 7, 50.00, 50.00, null, 0.87277405, 43.64, 97.13, 2.83, 0.00, null, null, 42.94); + checkInst(model, 9, 9, LocalDate.of(2019, 1, 10), 8, 50.00, 50.00, null, 0.85597135, 42.80, 49.04, 1.91, 0.00, null, null, 42.94); + checkInst(model, 10, 10, LocalDate.of(2019, 1, 11), 9, 50.00, 50.00, null, 0.83949214, 41.97, 0.00, 0.96, 0.00, null, null, 42.94); - assertEquals(TERM, model.loanTerm()); - - checkInst(model, 0, 0, EXPECTED_DISBURSEMENT_DATE, 200, 0, -9000.00, null, null, 1.00000000, -9000.00, 9000.00, null, null, null, - null, 1000.00); - - checkInst(model, 1, 1, LocalDate.of(2019, 1, 2), 199, 1, 50.00, 50.00, null, 0.99893332, 49.95, 8959.61, 9.61, 0.00, null, -9.61, - 1000.00); - checkInst(model, 2, 2, LocalDate.of(2019, 1, 3), 198, 2, 50.00, 50.00, null, 0.99786779, 49.89, 8919.18, 9.57, 0.00, null, -9.57, - 1000.00); - checkInst(model, 3, 3, LocalDate.of(2019, 1, 4), 197, 3, 50.00, 50.00, null, 0.99680339, 49.84, 8878.70, 9.52, 0.00, null, -9.52, - 1000.00); - checkInst(model, 4, 4, LocalDate.of(2019, 1, 5), 196, 4, 50.00, 50.00, null, 0.99574012, 49.79, 8838.18, 9.48, 0.00, null, -9.48, - 1000.00); - checkInst(model, 5, 5, LocalDate.of(2019, 1, 6), 195, 5, 50.00, 50.00, null, 0.99467799, 49.73, 8797.62, 9.44, 0.00, null, -9.44, - 1000.00); - checkInst(model, 6, 6, LocalDate.of(2019, 1, 7), 194, 6, 50.00, 50.00, null, 0.99361699, 49.68, 8757.01, 9.39, 0.00, null, -9.39, - 1000.00); - checkInst(model, 7, 7, LocalDate.of(2019, 1, 8), 193, 7, 50.00, 50.00, null, 0.99255712, 49.63, 8716.36, 9.35, 0.00, null, -9.35, - 1000.00); - checkInst(model, 8, 8, LocalDate.of(2019, 1, 9), 192, 8, 50.00, 50.00, null, 0.99149839, 49.57, 8675.67, 9.31, 0.00, null, -9.31, - 1000.00); - checkInst(model, 9, 9, LocalDate.of(2019, 1, 10), 191, 9, 50.00, 50.00, null, 0.99044078, 49.52, 8634.94, 9.26, 0.00, null, -9.26, - 1000.00); - checkInst(model, 10, 10, LocalDate.of(2019, 1, 11), 190, 10, 50.00, 50.00, null, 0.98938430, 49.47, 8594.16, 9.22, 0.00, null, - -9.22, 1000.00); - checkInst(model, 11, 11, LocalDate.of(2019, 1, 12), 189, 11, 50.00, 50.00, null, 0.98832895, 49.42, 8553.33, 9.18, 0.00, null, - -9.18, 1000.00); - checkInst(model, 12, 12, LocalDate.of(2019, 1, 13), 188, 12, 50.00, 50.00, null, 0.98727472, 49.36, 8512.47, 9.13, 0.00, null, - -9.13, 1000.00); - checkInst(model, 13, 13, LocalDate.of(2019, 1, 14), 187, 13, 50.00, 50.00, null, 0.98622162, 49.31, 8471.56, 9.09, 0.00, null, - -9.09, 1000.00); - checkInst(model, 14, 14, LocalDate.of(2019, 1, 15), 186, 14, 50.00, 50.00, null, 0.98516964, 49.26, 8430.60, 9.05, 0.00, null, - -9.05, 1000.00); - checkInst(model, 15, 15, LocalDate.of(2019, 1, 16), 185, 15, 50.00, 50.00, null, 0.98411879, 49.21, 8389.61, 9.00, 0.00, null, - -9.00, 1000.00); - checkInst(model, 16, 16, LocalDate.of(2019, 1, 17), 184, 16, 50.00, 50.00, null, 0.98306905, 49.15, 8348.56, 8.96, 0.00, null, - -8.96, 1000.00); - checkInst(model, 17, 17, LocalDate.of(2019, 1, 18), 183, 17, 50.00, 50.00, null, 0.98202044, 49.10, 8307.48, 8.91, 0.00, null, - -8.91, 1000.00); - checkInst(model, 18, 18, LocalDate.of(2019, 1, 19), 182, 18, 50.00, 50.00, null, 0.98097294, 49.05, 8266.35, 8.87, 0.00, null, - -8.87, 1000.00); - checkInst(model, 19, 19, LocalDate.of(2019, 1, 20), 181, 19, 50.00, 50.00, null, 0.97992656, 49.00, 8225.18, 8.83, 0.00, null, - -8.83, 1000.00); - checkInst(model, 20, 20, LocalDate.of(2019, 1, 21), 180, 20, 50.00, 50.00, null, 0.97888129, 48.94, 8183.96, 8.78, 0.00, null, - -8.78, 1000.00); - checkInst(model, 21, 21, LocalDate.of(2019, 1, 22), 179, 21, 50.00, 50.00, null, 0.97783715, 48.89, 8142.70, 8.74, 0.00, null, - -8.74, 1000.00); - checkInst(model, 22, 22, LocalDate.of(2019, 1, 23), 178, 22, 50.00, 50.00, null, 0.97679411, 48.84, 8101.39, 8.69, 0.00, null, - -8.69, 1000.00); - checkInst(model, 23, 23, LocalDate.of(2019, 1, 24), 177, 23, 50.00, 50.00, null, 0.97575219, 48.79, 8060.04, 8.65, 0.00, null, - -8.65, 1000.00); - checkInst(model, 24, 24, LocalDate.of(2019, 1, 25), 176, 24, 50.00, 50.00, null, 0.97471138, 48.74, 8018.65, 8.61, 0.00, null, - -8.61, 1000.00); - checkInst(model, 25, 25, LocalDate.of(2019, 1, 26), 175, 25, 50.00, 50.00, null, 0.97367168, 48.68, 7977.21, 8.56, 0.00, null, - -8.56, 1000.00); - checkInst(model, 26, 26, LocalDate.of(2019, 1, 27), 174, 26, 50.00, 50.00, null, 0.97263309, 48.63, 7935.73, 8.52, 0.00, null, - -8.52, 1000.00); - checkInst(model, 27, 27, LocalDate.of(2019, 1, 28), 173, 27, 50.00, 50.00, null, 0.97159560, 48.58, 7894.21, 8.47, 0.00, null, - -8.47, 1000.00); - checkInst(model, 28, 28, LocalDate.of(2019, 1, 29), 172, 28, 50.00, 50.00, null, 0.97055922, 48.53, 7852.63, 8.43, 0.00, null, - -8.43, 1000.00); - checkInst(model, 29, 29, LocalDate.of(2019, 1, 30), 171, 29, 50.00, 50.00, null, 0.96952395, 48.48, 7811.02, 8.39, 0.00, null, - -8.39, 1000.00); - checkInst(model, 30, 30, LocalDate.of(2019, 1, 31), 170, 30, 50.00, 50.00, null, 0.96848979, 48.42, 7769.36, 8.34, 0.00, null, - -8.34, 1000.00); - checkInst(model, 31, 31, LocalDate.of(2019, 2, 1), 169, 31, 50.00, 50.00, null, 0.96745672, 48.37, 7727.66, 8.30, 0.00, null, -8.30, - 1000.00); - checkInst(model, 32, 32, LocalDate.of(2019, 2, 2), 168, 32, 50.00, 50.00, null, 0.96642476, 48.32, 7685.91, 8.25, 0.00, null, -8.25, - 1000.00); - checkInst(model, 33, 33, LocalDate.of(2019, 2, 3), 167, 33, 50.00, 50.00, null, 0.96539390, 48.27, 7644.12, 8.21, 0.00, null, -8.21, - 1000.00); - checkInst(model, 34, 34, LocalDate.of(2019, 2, 4), 166, 34, 50.00, 50.00, null, 0.96436413, 48.22, 7602.28, 8.16, 0.00, null, -8.16, - 1000.00); - checkInst(model, 35, 35, LocalDate.of(2019, 2, 5), 165, 35, 50.00, 50.00, null, 0.96333547, 48.17, 7560.40, 8.12, 0.00, null, -8.12, - 1000.00); - checkInst(model, 36, 36, LocalDate.of(2019, 2, 6), 164, 36, 50.00, 50.00, null, 0.96230790, 48.12, 7518.47, 8.07, 0.00, null, -8.07, - 1000.00); - checkInst(model, 37, 37, LocalDate.of(2019, 2, 7), 163, 37, 50.00, 50.00, null, 0.96128143, 48.06, 7476.50, 8.03, 0.00, null, -8.03, - 1000.00); - checkInst(model, 38, 38, LocalDate.of(2019, 2, 8), 162, 38, 50.00, 50.00, null, 0.96025606, 48.01, 7434.48, 7.98, 0.00, null, -7.98, - 1000.00); - checkInst(model, 39, 39, LocalDate.of(2019, 2, 9), 161, 39, 50.00, 50.00, null, 0.95923178, 47.96, 7392.42, 7.94, 0.00, null, -7.94, - 1000.00); - checkInst(model, 40, 40, LocalDate.of(2019, 2, 10), 160, 40, 50.00, 50.00, null, 0.95820859, 47.91, 7350.31, 7.89, 0.00, null, - -7.89, 1000.00); - checkInst(model, 41, 41, LocalDate.of(2019, 2, 11), 159, 41, 50.00, 50.00, null, 0.95718649, 47.86, 7308.16, 7.85, 0.00, null, - -7.85, 1000.00); - checkInst(model, 42, 42, LocalDate.of(2019, 2, 12), 158, 42, 50.00, 50.00, null, 0.95616548, 47.81, 7265.97, 7.80, 0.00, null, - -7.80, 1000.00); - checkInst(model, 43, 43, LocalDate.of(2019, 2, 13), 157, 43, 50.00, 50.00, null, 0.95514557, 47.76, 7223.72, 7.76, 0.00, null, - -7.76, 1000.00); - checkInst(model, 44, 44, LocalDate.of(2019, 2, 14), 156, 44, 50.00, 50.00, null, 0.95412674, 47.71, 7181.44, 7.71, 0.00, null, - -7.71, 1000.00); - checkInst(model, 45, 45, LocalDate.of(2019, 2, 15), 155, 45, 50.00, 50.00, null, 0.95310899, 47.66, 7139.11, 7.67, 0.00, null, - -7.67, 1000.00); - checkInst(model, 46, 46, LocalDate.of(2019, 2, 16), 154, 46, 50.00, 50.00, null, 0.95209233, 47.60, 7096.73, 7.62, 0.00, null, - -7.62, 1000.00); - checkInst(model, 47, 47, LocalDate.of(2019, 2, 17), 153, 47, 50.00, 50.00, null, 0.95107676, 47.55, 7054.31, 7.58, 0.00, null, - -7.58, 1000.00); - checkInst(model, 48, 48, LocalDate.of(2019, 2, 18), 152, 48, 50.00, 50.00, null, 0.95006227, 47.50, 7011.84, 7.53, 0.00, null, - -7.53, 1000.00); - checkInst(model, 49, 49, LocalDate.of(2019, 2, 19), 151, 49, 50.00, 50.00, null, 0.94904886, 47.45, 6969.33, 7.49, 0.00, null, - -7.49, 1000.00); - checkInst(model, 50, 50, LocalDate.of(2019, 2, 20), 150, 50, 50.00, 50.00, null, 0.94803653, 47.40, 6926.77, 7.44, 0.00, null, - -7.44, 1000.00); - checkInst(model, 51, 51, LocalDate.of(2019, 2, 21), 149, 51, 50.00, 50.00, null, 0.94702529, 47.35, 6884.17, 7.40, 0.00, null, - -7.40, 1000.00); - checkInst(model, 52, 52, LocalDate.of(2019, 2, 22), 148, 52, 50.00, 50.00, null, 0.94601512, 47.30, 6841.52, 7.35, 0.00, null, - -7.35, 1000.00); - checkInst(model, 53, 53, LocalDate.of(2019, 2, 23), 147, 53, 50.00, 50.00, null, 0.94500603, 47.25, 6798.82, 7.31, 0.00, null, - -7.31, 1000.00); - checkInst(model, 54, 54, LocalDate.of(2019, 2, 24), 146, 54, 50.00, 50.00, null, 0.94399801, 47.20, 6756.08, 7.26, 0.00, null, - -7.26, 1000.00); - checkInst(model, 55, 55, LocalDate.of(2019, 2, 25), 145, 55, 50.00, 50.00, null, 0.94299107, 47.15, 6713.30, 7.21, 0.00, null, - -7.21, 1000.00); - checkInst(model, 56, 56, LocalDate.of(2019, 2, 26), 144, 56, 50.00, 50.00, null, 0.94198521, 47.10, 6670.47, 7.17, 0.00, null, - -7.17, 1000.00); - checkInst(model, 57, 57, LocalDate.of(2019, 2, 27), 143, 57, 50.00, 50.00, null, 0.94098042, 47.05, 6627.59, 7.12, 0.00, null, - -7.12, 1000.00); - checkInst(model, 58, 58, LocalDate.of(2019, 2, 28), 142, 58, 50.00, 50.00, null, 0.93997669, 47.00, 6584.67, 7.08, 0.00, null, - -7.08, 1000.00); - checkInst(model, 59, 59, LocalDate.of(2019, 3, 1), 141, 59, 50.00, 50.00, null, 0.93897404, 46.95, 6541.70, 7.03, 0.00, null, -7.03, - 1000.00); - checkInst(model, 60, 60, LocalDate.of(2019, 3, 2), 140, 60, 50.00, 50.00, null, 0.93797246, 46.90, 6498.68, 6.99, 0.00, null, -6.99, - 1000.00); - checkInst(model, 61, 61, LocalDate.of(2019, 3, 3), 139, 61, 50.00, 50.00, null, 0.93697195, 46.85, 6455.62, 6.94, 0.00, null, -6.94, - 1000.00); - checkInst(model, 62, 62, LocalDate.of(2019, 3, 4), 138, 62, 50.00, 50.00, null, 0.93597251, 46.80, 6412.51, 6.89, 0.00, null, -6.89, - 1000.00); - checkInst(model, 63, 63, LocalDate.of(2019, 3, 5), 137, 63, 50.00, 50.00, null, 0.93497413, 46.75, 6369.36, 6.85, 0.00, null, -6.85, - 1000.00); - checkInst(model, 64, 64, LocalDate.of(2019, 3, 6), 136, 64, 50.00, 50.00, null, 0.93397681, 46.70, 6326.16, 6.80, 0.00, null, -6.80, - 1000.00); - checkInst(model, 65, 65, LocalDate.of(2019, 3, 7), 135, 65, 50.00, 50.00, null, 0.93298056, 46.65, 6282.92, 6.76, 0.00, null, -6.76, - 1000.00); - checkInst(model, 66, 66, LocalDate.of(2019, 3, 8), 134, 66, 50.00, 50.00, null, 0.93198538, 46.60, 6239.63, 6.71, 0.00, null, -6.71, - 1000.00); - checkInst(model, 67, 67, LocalDate.of(2019, 3, 9), 133, 67, 50.00, 50.00, null, 0.93099125, 46.55, 6196.29, 6.66, 0.00, null, -6.66, - 1000.00); - checkInst(model, 68, 68, LocalDate.of(2019, 3, 10), 132, 68, 50.00, 50.00, null, 0.92999818, 46.50, 6152.91, 6.62, 0.00, null, - -6.62, 1000.00); - checkInst(model, 69, 69, LocalDate.of(2019, 3, 11), 131, 69, 50.00, 50.00, null, 0.92900618, 46.45, 6109.48, 6.57, 0.00, null, - -6.57, 1000.00); - checkInst(model, 70, 70, LocalDate.of(2019, 3, 12), 130, 70, 50.00, 50.00, null, 0.92801523, 46.40, 6066.00, 6.52, 0.00, null, - -6.52, 1000.00); - checkInst(model, 71, 71, LocalDate.of(2019, 3, 13), 129, 71, 50.00, 50.00, null, 0.92702534, 46.35, 6022.48, 6.48, 0.00, null, - -6.48, 1000.00); - checkInst(model, 72, 72, LocalDate.of(2019, 3, 14), 128, 72, 50.00, 50.00, null, 0.92603650, 46.30, 5978.91, 6.43, 0.00, null, - -6.43, 1000.00); - checkInst(model, 73, 73, LocalDate.of(2019, 3, 15), 127, 73, 50.00, 50.00, null, 0.92504872, 46.25, 5935.29, 6.38, 0.00, null, - -6.38, 1000.00); - checkInst(model, 74, 74, LocalDate.of(2019, 3, 16), 126, 74, 50.00, 50.00, null, 0.92406200, 46.20, 5891.63, 6.34, 0.00, null, - -6.34, 1000.00); - checkInst(model, 75, 75, LocalDate.of(2019, 3, 17), 125, 75, 50.00, 50.00, null, 0.92307632, 46.15, 5847.92, 6.29, 0.00, null, - -6.29, 1000.00); - checkInst(model, 76, 76, LocalDate.of(2019, 3, 18), 124, 76, 50.00, 50.00, null, 0.92209170, 46.10, 5804.17, 6.24, 0.00, null, - -6.24, 1000.00); - checkInst(model, 77, 77, LocalDate.of(2019, 3, 19), 123, 77, 50.00, 50.00, null, 0.92110813, 46.06, 5760.36, 6.20, 0.00, null, - -6.20, 1000.00); - checkInst(model, 78, 78, LocalDate.of(2019, 3, 20), 122, 78, 50.00, 50.00, null, 0.92012560, 46.01, 5716.52, 6.15, 0.00, null, - -6.15, 1000.00); - checkInst(model, 79, 79, LocalDate.of(2019, 3, 21), 121, 79, 50.00, 50.00, null, 0.91914413, 45.96, 5672.62, 6.10, 0.00, null, - -6.10, 1000.00); - checkInst(model, 80, 80, LocalDate.of(2019, 3, 22), 120, 80, 50.00, 50.00, null, 0.91816370, 45.91, 5628.68, 6.06, 0.00, null, - -6.06, 1000.00); - checkInst(model, 81, 81, LocalDate.of(2019, 3, 23), 119, 81, 50.00, 50.00, null, 0.91718432, 45.86, 5584.69, 6.01, 0.00, null, - -6.01, 1000.00); - checkInst(model, 82, 82, LocalDate.of(2019, 3, 24), 118, 82, 50.00, 50.00, null, 0.91620598, 45.81, 5540.65, 5.96, 0.00, null, - -5.96, 1000.00); - checkInst(model, 83, 83, LocalDate.of(2019, 3, 25), 117, 83, 50.00, 50.00, null, 0.91522868, 45.76, 5496.57, 5.92, 0.00, null, - -5.92, 1000.00); - checkInst(model, 84, 84, LocalDate.of(2019, 3, 26), 116, 84, 50.00, 50.00, null, 0.91425243, 45.71, 5452.44, 5.87, 0.00, null, - -5.87, 1000.00); - checkInst(model, 85, 85, LocalDate.of(2019, 3, 27), 115, 85, 50.00, 50.00, null, 0.91327722, 45.66, 5408.26, 5.82, 0.00, null, - -5.82, 1000.00); - checkInst(model, 86, 86, LocalDate.of(2019, 3, 28), 114, 86, 50.00, 50.00, null, 0.91230305, 45.62, 5364.03, 5.78, 0.00, null, - -5.78, 1000.00); - checkInst(model, 87, 87, LocalDate.of(2019, 3, 29), 113, 87, 50.00, 50.00, null, 0.91132992, 45.57, 5319.76, 5.73, 0.00, null, - -5.73, 1000.00); - checkInst(model, 88, 88, LocalDate.of(2019, 3, 30), 112, 88, 50.00, 50.00, null, 0.91035783, 45.52, 5275.44, 5.68, 0.00, null, - -5.68, 1000.00); - checkInst(model, 89, 89, LocalDate.of(2019, 3, 31), 111, 89, 50.00, 50.00, null, 0.90938677, 45.47, 5231.08, 5.63, 0.00, null, - -5.63, 1000.00); - checkInst(model, 90, 90, LocalDate.of(2019, 4, 1), 110, 90, 50.00, 50.00, null, 0.90841675, 45.42, 5186.66, 5.59, 0.00, null, -5.59, - 1000.00); - checkInst(model, 91, 91, LocalDate.of(2019, 4, 2), 109, 91, 50.00, 50.00, null, 0.90744776, 45.37, 5142.20, 5.54, 0.00, null, -5.54, - 1000.00); - checkInst(model, 92, 92, LocalDate.of(2019, 4, 3), 108, 92, 50.00, 50.00, null, 0.90647981, 45.32, 5097.69, 5.49, 0.00, null, -5.49, - 1000.00); - checkInst(model, 93, 93, LocalDate.of(2019, 4, 4), 107, 93, 50.00, 50.00, null, 0.90551289, 45.28, 5053.13, 5.44, 0.00, null, -5.44, - 1000.00); - checkInst(model, 94, 94, LocalDate.of(2019, 4, 5), 106, 94, 50.00, 50.00, null, 0.90454700, 45.23, 5008.53, 5.40, 0.00, null, -5.40, - 1000.00); - checkInst(model, 95, 95, LocalDate.of(2019, 4, 6), 105, 95, 50.00, 50.00, null, 0.90358215, 45.18, 4963.88, 5.35, 0.00, null, -5.35, - 1000.00); - checkInst(model, 96, 96, LocalDate.of(2019, 4, 7), 104, 96, 50.00, 50.00, null, 0.90261832, 45.13, 4919.18, 5.30, 0.00, null, -5.30, - 1000.00); - checkInst(model, 97, 97, LocalDate.of(2019, 4, 8), 103, 97, 50.00, 50.00, null, 0.90165552, 45.08, 4874.43, 5.25, 0.00, null, -5.25, - 1000.00); - checkInst(model, 98, 98, LocalDate.of(2019, 4, 9), 102, 98, 50.00, 50.00, null, 0.90069374, 45.03, 4829.64, 5.20, 0.00, null, -5.20, - 1000.00); - checkInst(model, 99, 99, LocalDate.of(2019, 4, 10), 101, 99, 50.00, 50.00, null, 0.89973299, 44.99, 4784.79, 5.16, 0.00, null, - -5.16, 1000.00); - checkInst(model, 100, 100, LocalDate.of(2019, 4, 11), 100, 100, 50.00, 50.00, null, 0.89877327, 44.94, 4739.90, 5.11, 0.00, null, - -5.11, 1000.00); - checkInst(model, 101, 101, LocalDate.of(2019, 4, 12), 99, 101, 50.00, 50.00, null, 0.89781457, 44.89, 4694.96, 5.06, 0.00, null, - -5.06, 1000.00); - checkInst(model, 102, 102, LocalDate.of(2019, 4, 13), 98, 102, 50.00, 50.00, null, 0.89685689, 44.84, 4649.98, 5.01, 0.00, null, - -5.01, 1000.00); - checkInst(model, 103, 103, LocalDate.of(2019, 4, 14), 97, 103, 50.00, 50.00, null, 0.89590024, 44.80, 4604.94, 4.97, 0.00, null, - -4.97, 1000.00); - checkInst(model, 104, 104, LocalDate.of(2019, 4, 15), 96, 104, 50.00, 50.00, null, 0.89494460, 44.75, 4559.86, 4.92, 0.00, null, - -4.92, 1000.00); - checkInst(model, 105, 105, LocalDate.of(2019, 4, 16), 95, 105, 50.00, 50.00, null, 0.89398999, 44.70, 4514.73, 4.87, 0.00, null, - -4.87, 1000.00); - checkInst(model, 106, 106, LocalDate.of(2019, 4, 17), 94, 106, 50.00, 50.00, null, 0.89303639, 44.65, 4469.55, 4.82, 0.00, null, - -4.82, 1000.00); - checkInst(model, 107, 107, LocalDate.of(2019, 4, 18), 93, 107, 50.00, 50.00, null, 0.89208381, 44.60, 4424.32, 4.77, 0.00, null, - -4.77, 1000.00); - checkInst(model, 108, 108, LocalDate.of(2019, 4, 19), 92, 108, 50.00, 50.00, null, 0.89113225, 44.56, 4379.05, 4.72, 0.00, null, - -4.72, 1000.00); - checkInst(model, 109, 109, LocalDate.of(2019, 4, 20), 91, 109, 50.00, 50.00, null, 0.89018170, 44.51, 4333.72, 4.68, 0.00, null, - -4.68, 1000.00); - checkInst(model, 110, 110, LocalDate.of(2019, 4, 21), 90, 110, 50.00, 50.00, null, 0.88923216, 44.46, 4288.35, 4.63, 0.00, null, - -4.63, 1000.00); - checkInst(model, 111, 111, LocalDate.of(2019, 4, 22), 89, 111, 50.00, 50.00, null, 0.88828364, 44.41, 4242.93, 4.58, 0.00, null, - -4.58, 1000.00); - checkInst(model, 112, 112, LocalDate.of(2019, 4, 23), 88, 112, 50.00, 50.00, null, 0.88733613, 44.37, 4197.46, 4.53, 0.00, null, - -4.53, 1000.00); - checkInst(model, 113, 113, LocalDate.of(2019, 4, 24), 87, 113, 50.00, 50.00, null, 0.88638963, 44.32, 4151.94, 4.48, 0.00, null, - -4.48, 1000.00); - checkInst(model, 114, 114, LocalDate.of(2019, 4, 25), 86, 114, 50.00, 50.00, null, 0.88544414, 44.27, 4106.38, 4.43, 0.00, null, - -4.43, 1000.00); - checkInst(model, 115, 115, LocalDate.of(2019, 4, 26), 85, 115, 50.00, 50.00, null, 0.88449966, 44.22, 4060.76, 4.38, 0.00, null, - -4.38, 1000.00); - checkInst(model, 116, 116, LocalDate.of(2019, 4, 27), 84, 116, 50.00, 50.00, null, 0.88355619, 44.18, 4015.10, 4.34, 0.00, null, - -4.34, 1000.00); - checkInst(model, 117, 117, LocalDate.of(2019, 4, 28), 83, 117, 50.00, 50.00, null, 0.88261372, 44.13, 3969.38, 4.29, 0.00, null, - -4.29, 1000.00); - checkInst(model, 118, 118, LocalDate.of(2019, 4, 29), 82, 118, 50.00, 50.00, null, 0.88167226, 44.08, 3923.62, 4.24, 0.00, null, - -4.24, 1000.00); - checkInst(model, 119, 119, LocalDate.of(2019, 4, 30), 81, 119, 50.00, 50.00, null, 0.88073180, 44.04, 3877.81, 4.19, 0.00, null, - -4.19, 1000.00); - checkInst(model, 120, 120, LocalDate.of(2019, 5, 1), 80, 120, 50.00, 50.00, null, 0.87979234, 43.99, 3831.95, 4.14, 0.00, null, - -4.14, 1000.00); - checkInst(model, 121, 121, LocalDate.of(2019, 5, 2), 79, 121, 50.00, 50.00, null, 0.87885389, 43.94, 3786.04, 4.09, 0.00, null, - -4.09, 1000.00); - checkInst(model, 122, 122, LocalDate.of(2019, 5, 3), 78, 122, 50.00, 50.00, null, 0.87791644, 43.90, 3740.09, 4.04, 0.00, null, - -4.04, 1000.00); - checkInst(model, 123, 123, LocalDate.of(2019, 5, 4), 77, 123, 50.00, 50.00, null, 0.87697999, 43.85, 3694.08, 3.99, 0.00, null, - -3.99, 1000.00); - checkInst(model, 124, 124, LocalDate.of(2019, 5, 5), 76, 124, 50.00, 50.00, null, 0.87604453, 43.80, 3648.03, 3.94, 0.00, null, - -3.94, 1000.00); - checkInst(model, 125, 125, LocalDate.of(2019, 5, 6), 75, 125, 50.00, 50.00, null, 0.87511008, 43.76, 3601.92, 3.90, 0.00, null, - -3.90, 1000.00); - checkInst(model, 126, 126, LocalDate.of(2019, 5, 7), 74, 126, 50.00, 50.00, null, 0.87417662, 43.71, 3555.77, 3.85, 0.00, null, - -3.85, 1000.00); - checkInst(model, 127, 127, LocalDate.of(2019, 5, 8), 73, 127, 50.00, 50.00, null, 0.87324416, 43.66, 3509.56, 3.80, 0.00, null, - -3.80, 1000.00); - checkInst(model, 128, 128, LocalDate.of(2019, 5, 9), 72, 128, 50.00, 50.00, null, 0.87231269, 43.62, 3463.31, 3.75, 0.00, null, - -3.75, 1000.00); - checkInst(model, 129, 129, LocalDate.of(2019, 5, 10), 71, 129, 50.00, 50.00, null, 0.87138221, 43.57, 3417.01, 3.70, 0.00, null, - -3.70, 1000.00); - checkInst(model, 130, 130, LocalDate.of(2019, 5, 11), 70, 130, 50.00, 50.00, null, 0.87045273, 43.52, 3370.66, 3.65, 0.00, null, - -3.65, 1000.00); - checkInst(model, 131, 131, LocalDate.of(2019, 5, 12), 69, 131, 50.00, 50.00, null, 0.86952424, 43.48, 3324.26, 3.60, 0.00, null, - -3.60, 1000.00); - checkInst(model, 132, 132, LocalDate.of(2019, 5, 13), 68, 132, 50.00, 50.00, null, 0.86859674, 43.43, 3277.81, 3.55, 0.00, null, - -3.55, 1000.00); - checkInst(model, 133, 133, LocalDate.of(2019, 5, 14), 67, 133, 50.00, 50.00, null, 0.86767023, 43.38, 3231.31, 3.50, 0.00, null, - -3.50, 1000.00); - checkInst(model, 134, 134, LocalDate.of(2019, 5, 15), 66, 134, 50.00, 50.00, null, 0.86674471, 43.34, 3184.76, 3.45, 0.00, null, - -3.45, 1000.00); - checkInst(model, 135, 135, LocalDate.of(2019, 5, 16), 65, 135, 50.00, 50.00, null, 0.86582017, 43.29, 3138.16, 3.40, 0.00, null, - -3.40, 1000.00); - checkInst(model, 136, 136, LocalDate.of(2019, 5, 17), 64, 136, 50.00, 50.00, null, 0.86489662, 43.24, 3091.51, 3.35, 0.00, null, - -3.35, 1000.00); - checkInst(model, 137, 137, LocalDate.of(2019, 5, 18), 63, 137, 50.00, 50.00, null, 0.86397406, 43.20, 3044.81, 3.30, 0.00, null, - -3.30, 1000.00); - checkInst(model, 138, 138, LocalDate.of(2019, 5, 19), 62, 138, 50.00, 50.00, null, 0.86305248, 43.15, 2998.06, 3.25, 0.00, null, - -3.25, 1000.00); - checkInst(model, 139, 139, LocalDate.of(2019, 5, 20), 61, 139, 50.00, 50.00, null, 0.86213188, 43.11, 2951.26, 3.20, 0.00, null, - -3.20, 1000.00); - checkInst(model, 140, 140, LocalDate.of(2019, 5, 21), 60, 140, 50.00, 50.00, null, 0.86121227, 43.06, 2904.42, 3.15, 0.00, null, - -3.15, 1000.00); - checkInst(model, 141, 141, LocalDate.of(2019, 5, 22), 59, 141, 50.00, 50.00, null, 0.86029363, 43.01, 2857.52, 3.10, 0.00, null, - -3.10, 1000.00); - checkInst(model, 142, 142, LocalDate.of(2019, 5, 23), 58, 142, 50.00, 50.00, null, 0.85937598, 42.97, 2810.57, 3.05, 0.00, null, - -3.05, 1000.00); - checkInst(model, 143, 143, LocalDate.of(2019, 5, 24), 57, 143, 50.00, 50.00, null, 0.85845930, 42.92, 2763.57, 3.00, 0.00, null, - -3.00, 1000.00); - checkInst(model, 144, 144, LocalDate.of(2019, 5, 25), 56, 144, 50.00, 50.00, null, 0.85754361, 42.88, 2716.52, 2.95, 0.00, null, - -2.95, 1000.00); - checkInst(model, 145, 145, LocalDate.of(2019, 5, 26), 55, 145, 50.00, 50.00, null, 0.85662889, 42.83, 2669.42, 2.90, 0.00, null, - -2.90, 1000.00); - checkInst(model, 146, 146, LocalDate.of(2019, 5, 27), 54, 146, 50.00, 50.00, null, 0.85571514, 42.79, 2622.27, 2.85, 0.00, null, - -2.85, 1000.00); - checkInst(model, 147, 147, LocalDate.of(2019, 5, 28), 53, 147, 50.00, 50.00, null, 0.85480237, 42.74, 2575.07, 2.80, 0.00, null, - -2.80, 1000.00); - checkInst(model, 148, 148, LocalDate.of(2019, 5, 29), 52, 148, 50.00, 50.00, null, 0.85389057, 42.69, 2527.82, 2.75, 0.00, null, - -2.75, 1000.00); - checkInst(model, 149, 149, LocalDate.of(2019, 5, 30), 51, 149, 50.00, 50.00, null, 0.85297975, 42.65, 2480.52, 2.70, 0.00, null, - -2.70, 1000.00); - checkInst(model, 150, 150, LocalDate.of(2019, 5, 31), 50, 150, 50.00, 50.00, null, 0.85206990, 42.60, 2433.17, 2.65, 0.00, null, - -2.65, 1000.00); - checkInst(model, 151, 151, LocalDate.of(2019, 6, 1), 49, 151, 50.00, 50.00, null, 0.85116101, 42.56, 2385.77, 2.60, 0.00, null, - -2.60, 1000.00); - checkInst(model, 152, 152, LocalDate.of(2019, 6, 2), 48, 152, 50.00, 50.00, null, 0.85025310, 42.51, 2338.31, 2.55, 0.00, null, - -2.55, 1000.00); - checkInst(model, 153, 153, LocalDate.of(2019, 6, 3), 47, 153, 50.00, 50.00, null, 0.84934616, 42.47, 2290.81, 2.50, 0.00, null, - -2.50, 1000.00); - checkInst(model, 154, 154, LocalDate.of(2019, 6, 4), 46, 154, 50.00, 50.00, null, 0.84844018, 42.42, 2243.26, 2.45, 0.00, null, - -2.45, 1000.00); - checkInst(model, 155, 155, LocalDate.of(2019, 6, 5), 45, 155, 50.00, 50.00, null, 0.84753517, 42.38, 2195.65, 2.40, 0.00, null, - -2.40, 1000.00); - checkInst(model, 156, 156, LocalDate.of(2019, 6, 6), 44, 156, 50.00, 50.00, null, 0.84663113, 42.33, 2148.00, 2.34, 0.00, null, - -2.34, 1000.00); - checkInst(model, 157, 157, LocalDate.of(2019, 6, 7), 43, 157, 50.00, 50.00, null, 0.84572805, 42.29, 2100.29, 2.29, 0.00, null, - -2.29, 1000.00); - checkInst(model, 158, 158, LocalDate.of(2019, 6, 8), 42, 158, 50.00, 50.00, null, 0.84482593, 42.24, 2052.53, 2.24, 0.00, null, - -2.24, 1000.00); - checkInst(model, 159, 159, LocalDate.of(2019, 6, 9), 41, 159, 50.00, 50.00, null, 0.84392477, 42.20, 2004.73, 2.19, 0.00, null, - -2.19, 1000.00); - checkInst(model, 160, 160, LocalDate.of(2019, 6, 10), 40, 160, 50.00, 50.00, null, 0.84302458, 42.15, 1956.87, 2.14, 0.00, null, - -2.14, 1000.00); - checkInst(model, 161, 161, LocalDate.of(2019, 6, 11), 39, 161, 50.00, 50.00, null, 0.84212535, 42.11, 1908.96, 2.09, 0.00, null, - -2.09, 1000.00); - checkInst(model, 162, 162, LocalDate.of(2019, 6, 12), 38, 162, 50.00, 50.00, null, 0.84122707, 42.06, 1860.99, 2.04, 0.00, null, - -2.04, 1000.00); - checkInst(model, 163, 163, LocalDate.of(2019, 6, 13), 37, 163, 50.00, 50.00, null, 0.84032975, 42.02, 1812.98, 1.99, 0.00, null, - -1.99, 1000.00); - checkInst(model, 164, 164, LocalDate.of(2019, 6, 14), 36, 164, 50.00, 50.00, null, 0.83943340, 41.97, 1764.92, 1.94, 0.00, null, - -1.94, 1000.00); - checkInst(model, 165, 165, LocalDate.of(2019, 6, 15), 35, 165, 50.00, 50.00, null, 0.83853799, 41.93, 1716.80, 1.88, 0.00, null, - -1.88, 1000.00); - checkInst(model, 166, 166, LocalDate.of(2019, 6, 16), 34, 166, 50.00, 50.00, null, 0.83764354, 41.88, 1668.64, 1.83, 0.00, null, - -1.83, 1000.00); - checkInst(model, 167, 167, LocalDate.of(2019, 6, 17), 33, 167, 50.00, 50.00, null, 0.83675005, 41.84, 1620.42, 1.78, 0.00, null, - -1.78, 1000.00); - checkInst(model, 168, 168, LocalDate.of(2019, 6, 18), 32, 168, 50.00, 50.00, null, 0.83585751, 41.79, 1572.15, 1.73, 0.00, null, - -1.73, 1000.00); - checkInst(model, 169, 169, LocalDate.of(2019, 6, 19), 31, 169, 50.00, 50.00, null, 0.83496592, 41.75, 1523.83, 1.68, 0.00, null, - -1.68, 1000.00); - checkInst(model, 170, 170, LocalDate.of(2019, 6, 20), 30, 170, 50.00, 50.00, null, 0.83407528, 41.70, 1475.45, 1.63, 0.00, null, - -1.63, 1000.00); - checkInst(model, 171, 171, LocalDate.of(2019, 6, 21), 29, 171, 50.00, 50.00, null, 0.83318560, 41.66, 1427.03, 1.58, 0.00, null, - -1.58, 1000.00); - checkInst(model, 172, 172, LocalDate.of(2019, 6, 22), 28, 172, 50.00, 50.00, null, 0.83229686, 41.61, 1378.55, 1.52, 0.00, null, - -1.52, 1000.00); - checkInst(model, 173, 173, LocalDate.of(2019, 6, 23), 27, 173, 50.00, 50.00, null, 0.83140907, 41.57, 1330.02, 1.47, 0.00, null, - -1.47, 1000.00); - checkInst(model, 174, 174, LocalDate.of(2019, 6, 24), 26, 174, 50.00, 50.00, null, 0.83052222, 41.53, 1281.45, 1.42, 0.00, null, - -1.42, 1000.00); - checkInst(model, 175, 175, LocalDate.of(2019, 6, 25), 25, 175, 50.00, 50.00, null, 0.82963633, 41.48, 1232.81, 1.37, 0.00, null, - -1.37, 1000.00); - checkInst(model, 176, 176, LocalDate.of(2019, 6, 26), 24, 176, 50.00, 50.00, null, 0.82875137, 41.44, 1184.13, 1.32, 0.00, null, - -1.32, 1000.00); - checkInst(model, 177, 177, LocalDate.of(2019, 6, 27), 23, 177, 50.00, 50.00, null, 0.82786736, 41.39, 1135.39, 1.26, 0.00, null, - -1.26, 1000.00); - checkInst(model, 178, 178, LocalDate.of(2019, 6, 28), 22, 178, 50.00, 50.00, null, 0.82698430, 41.35, 1086.61, 1.21, 0.00, null, - -1.21, 1000.00); - checkInst(model, 179, 179, LocalDate.of(2019, 6, 29), 21, 179, 50.00, 50.00, null, 0.82610217, 41.31, 1037.77, 1.16, 0.00, null, - -1.16, 1000.00); - checkInst(model, 180, 180, LocalDate.of(2019, 6, 30), 20, 180, 50.00, 50.00, null, 0.82522099, 41.26, 988.88, 1.11, 0.00, null, - -1.11, 1000.00); - checkInst(model, 181, 181, LocalDate.of(2019, 7, 1), 19, 181, 50.00, 50.00, null, 0.82434075, 41.22, 939.93, 1.06, 0.00, null, - -1.06, 1000.00); - checkInst(model, 182, 182, LocalDate.of(2019, 7, 2), 18, 182, 50.00, 50.00, null, 0.82346144, 41.17, 890.93, 1.00, 0.00, null, - -1.00, 1000.00); - checkInst(model, 183, 183, LocalDate.of(2019, 7, 3), 17, 183, 50.00, 50.00, null, 0.82258308, 41.13, 841.89, 0.95, 0.00, null, - -0.95, 1000.00); - checkInst(model, 184, 184, LocalDate.of(2019, 7, 4), 16, 184, 50.00, 50.00, null, 0.82170565, 41.09, 792.79, 0.90, 0.00, null, - -0.90, 1000.00); - checkInst(model, 185, 185, LocalDate.of(2019, 7, 5), 15, 185, 50.00, 50.00, null, 0.82082916, 41.04, 743.63, 0.85, 0.00, null, - -0.85, 1000.00); - checkInst(model, 186, 186, LocalDate.of(2019, 7, 6), 14, 186, 50.00, 50.00, null, 0.81995360, 41.00, 694.43, 0.79, 0.00, null, - -0.79, 1000.00); - checkInst(model, 187, 187, LocalDate.of(2019, 7, 7), 13, 187, 50.00, 50.00, null, 0.81907897, 40.95, 645.17, 0.74, 0.00, null, - -0.74, 1000.00); - checkInst(model, 188, 188, LocalDate.of(2019, 7, 8), 12, 188, 50.00, 50.00, null, 0.81820528, 40.91, 595.86, 0.69, 0.00, null, - -0.69, 1000.00); - checkInst(model, 189, 189, LocalDate.of(2019, 7, 9), 11, 189, 50.00, 50.00, null, 0.81733252, 40.87, 546.49, 0.64, 0.00, null, - -0.64, 1000.00); - checkInst(model, 190, 190, LocalDate.of(2019, 7, 10), 10, 190, 50.00, 50.00, null, 0.81646069, 40.82, 497.08, 0.58, 0.00, null, - -0.58, 1000.00); - checkInst(model, 191, 191, LocalDate.of(2019, 7, 11), 9, 191, 50.00, 50.00, null, 0.81558979, 40.78, 447.61, 0.53, 0.00, null, - -0.53, 1000.00); - checkInst(model, 192, 192, LocalDate.of(2019, 7, 12), 8, 192, 50.00, 50.00, null, 0.81471983, 40.74, 398.08, 0.48, 0.00, null, - -0.48, 1000.00); - checkInst(model, 193, 193, LocalDate.of(2019, 7, 13), 7, 193, 50.00, 50.00, null, 0.81385078, 40.69, 348.51, 0.43, 0.00, null, - -0.43, 1000.00); - checkInst(model, 194, 194, LocalDate.of(2019, 7, 14), 6, 194, 50.00, 50.00, null, 0.81298267, 40.65, 298.88, 0.37, 0.00, null, - -0.37, 1000.00); - checkInst(model, 195, 195, LocalDate.of(2019, 7, 15), 5, 195, 50.00, 50.00, null, 0.81211548, 40.61, 249.20, 0.32, 0.00, null, - -0.32, 1000.00); - checkInst(model, 196, 196, LocalDate.of(2019, 7, 16), 4, 196, 50.00, 50.00, null, 0.81124922, 40.56, 199.47, 0.27, 0.00, null, - -0.27, 1000.00); - checkInst(model, 197, 197, LocalDate.of(2019, 7, 17), 3, 197, 50.00, 50.00, null, 0.81038388, 40.52, 149.68, 0.21, 0.00, null, - -0.21, 1000.00); - checkInst(model, 198, 198, LocalDate.of(2019, 7, 18), 2, 198, 50.00, 50.00, null, 0.80951946, 40.48, 99.84, 0.16, 0.00, null, -0.16, - 1000.00); - checkInst(model, 199, 199, LocalDate.of(2019, 7, 19), 1, 199, 50.00, 50.00, null, 0.80865597, 40.43, 49.95, 0.11, 0.00, null, -0.11, - 1000.00); - checkInst(model, 200, 200, LocalDate.of(2019, 7, 20), 0, 200, 50.00, 50.00, null, 0.80779339, 40.39, 0.00, 0.05, 0.00, null, -0.05, - 1000.00); + checkInst(model, 11, 11, LocalDate.of(2019, 1, 12), 10, null, 10.00, null, 0.82333018, 8.23, null, null, 0.00, null, null, null); } @Test - void testOnTimePayment_term200_originationFee1000_netDisbursement9000_pay50_50() { - final ProjectedAmortizationScheduleModel model = generateModel(); + void testExcessPayment_term10_discountFee50_netDisbursement450_pay110() { + final BigDecimal smallDiscountFee = new BigDecimal("50"); + final BigDecimal smallNetDisbursement = new BigDecimal("450"); + final ProjectedAmortizationScheduleModel initial = calculator.generateModel(smallDiscountFee, smallNetDisbursement, TPV, RATE, + DAY_COUNT, EXPECTED_DISBURSEMENT_DATE, MC, CURRENCY); + final ProjectedAmortizationScheduleModel model = calculator.addDisbursement(initial, smallDiscountFee, smallNetDisbursement, + EXPECTED_DISBURSEMENT_DATE); - calculator.applyPayment(model, EXPECTED_DISBURSEMENT_DATE.plusDays(1), new BigDecimal("50")); - calculator.applyPayment(model, EXPECTED_DISBURSEMENT_DATE.plusDays(2), new BigDecimal("50")); + calculator.applyPayment(model, EXPECTED_DISBURSEMENT_DATE.plusDays(1), new BigDecimal("110")); - checkInst(model, 0, 0, EXPECTED_DISBURSEMENT_DATE, 202, 0, -9000.00, null, null, 1.00000000, -9000.00, 9000.00, null, null, null, - null, 1000.00); - - checkInst(model, 1, 1, LocalDate.of(2019, 1, 2), 201, 0, 50.00, 50.00, 50.00, 1.00000000, 50.00, 8959.61, 9.61, 19.18, 9.61, 0.00, - 990.39); - checkInst(model, 2, 2, LocalDate.of(2019, 1, 3), 200, 0, 50.00, 50.00, 50.00, 1.00000000, 50.00, 8919.18, 9.57, 9.57, 9.57, 0.00, - 980.82); - checkInst(model, 3, 3, LocalDate.of(2019, 1, 4), 199, 1, 50.00, 50.00, null, 0.99893332, 49.95, 8878.70, 9.52, 0.00, null, -9.52, - 980.82); - checkInst(model, 4, 4, LocalDate.of(2019, 1, 5), 198, 2, 50.00, 50.00, null, 0.99786779, 49.89, 8838.18, 9.48, 0.00, null, -9.48, - 980.82); - checkInst(model, 5, 5, LocalDate.of(2019, 1, 6), 197, 3, 50.00, 50.00, null, 0.99680339, 49.84, 8797.62, 9.44, 0.00, null, -9.44, - 980.82); - checkInst(model, 6, 6, LocalDate.of(2019, 1, 7), 196, 4, 50.00, 50.00, null, 0.99574012, 49.79, 8757.01, 9.39, 0.00, null, -9.39, - 980.82); - checkInst(model, 7, 7, LocalDate.of(2019, 1, 8), 195, 5, 50.00, 50.00, null, 0.99467799, 49.73, 8716.36, 9.35, 0.00, null, -9.35, - 980.82); - checkInst(model, 8, 8, LocalDate.of(2019, 1, 9), 194, 6, 50.00, 50.00, null, 0.99361699, 49.68, 8675.67, 9.31, 0.00, null, -9.31, - 980.82); - checkInst(model, 9, 9, LocalDate.of(2019, 1, 10), 193, 7, 50.00, 50.00, null, 0.99255712, 49.63, 8634.94, 9.26, 0.00, null, -9.26, - 980.82); - checkInst(model, 10, 10, LocalDate.of(2019, 1, 11), 192, 8, 50.00, 50.00, null, 0.99149839, 49.57, 8594.16, 9.22, 0.00, null, -9.22, - 980.82); - checkInst(model, 11, 11, LocalDate.of(2019, 1, 12), 191, 9, 50.00, 50.00, null, 0.99044078, 49.52, 8553.33, 9.18, 0.00, null, -9.18, - 980.82); - checkInst(model, 12, 12, LocalDate.of(2019, 1, 13), 190, 10, 50.00, 50.00, null, 0.98938430, 49.47, 8512.47, 9.13, 0.00, null, - -9.13, 980.82); - checkInst(model, 13, 13, LocalDate.of(2019, 1, 14), 189, 11, 50.00, 50.00, null, 0.98832895, 49.42, 8471.56, 9.09, 0.00, null, - -9.09, 980.82); - checkInst(model, 14, 14, LocalDate.of(2019, 1, 15), 188, 12, 50.00, 50.00, null, 0.98727472, 49.36, 8430.60, 9.05, 0.00, null, - -9.05, 980.82); - checkInst(model, 15, 15, LocalDate.of(2019, 1, 16), 187, 13, 50.00, 50.00, null, 0.98622162, 49.31, 8389.61, 9.00, 0.00, null, - -9.00, 980.82); - checkInst(model, 16, 16, LocalDate.of(2019, 1, 17), 186, 14, 50.00, 50.00, null, 0.98516964, 49.26, 8348.56, 8.96, 0.00, null, - -8.96, 980.82); - checkInst(model, 17, 17, LocalDate.of(2019, 1, 18), 185, 15, 50.00, 50.00, null, 0.98411879, 49.21, 8307.48, 8.91, 0.00, null, - -8.91, 980.82); - checkInst(model, 18, 18, LocalDate.of(2019, 1, 19), 184, 16, 50.00, 50.00, null, 0.98306905, 49.15, 8266.35, 8.87, 0.00, null, - -8.87, 980.82); - checkInst(model, 19, 19, LocalDate.of(2019, 1, 20), 183, 17, 50.00, 50.00, null, 0.98202044, 49.10, 8225.18, 8.83, 0.00, null, - -8.83, 980.82); - checkInst(model, 20, 20, LocalDate.of(2019, 1, 21), 182, 18, 50.00, 50.00, null, 0.98097294, 49.05, 8183.96, 8.78, 0.00, null, - -8.78, 980.82); - checkInst(model, 21, 21, LocalDate.of(2019, 1, 22), 181, 19, 50.00, 50.00, null, 0.97992656, 49.00, 8142.70, 8.74, 0.00, null, - -8.74, 980.82); - checkInst(model, 22, 22, LocalDate.of(2019, 1, 23), 180, 20, 50.00, 50.00, null, 0.97888129, 48.94, 8101.39, 8.69, 0.00, null, - -8.69, 980.82); - checkInst(model, 23, 23, LocalDate.of(2019, 1, 24), 179, 21, 50.00, 50.00, null, 0.97783715, 48.89, 8060.04, 8.65, 0.00, null, - -8.65, 980.82); - checkInst(model, 24, 24, LocalDate.of(2019, 1, 25), 178, 22, 50.00, 50.00, null, 0.97679411, 48.84, 8018.65, 8.61, 0.00, null, - -8.61, 980.82); - checkInst(model, 25, 25, LocalDate.of(2019, 1, 26), 177, 23, 50.00, 50.00, null, 0.97575219, 48.79, 7977.21, 8.56, 0.00, null, - -8.56, 980.82); - checkInst(model, 26, 26, LocalDate.of(2019, 1, 27), 176, 24, 50.00, 50.00, null, 0.97471138, 48.74, 7935.73, 8.52, 0.00, null, - -8.52, 980.82); - checkInst(model, 27, 27, LocalDate.of(2019, 1, 28), 175, 25, 50.00, 50.00, null, 0.97367168, 48.68, 7894.21, 8.47, 0.00, null, - -8.47, 980.82); - checkInst(model, 28, 28, LocalDate.of(2019, 1, 29), 174, 26, 50.00, 50.00, null, 0.97263309, 48.63, 7852.63, 8.43, 0.00, null, - -8.43, 980.82); - checkInst(model, 29, 29, LocalDate.of(2019, 1, 30), 173, 27, 50.00, 50.00, null, 0.97159560, 48.58, 7811.02, 8.39, 0.00, null, - -8.39, 980.82); - checkInst(model, 30, 30, LocalDate.of(2019, 1, 31), 172, 28, 50.00, 50.00, null, 0.97055922, 48.53, 7769.36, 8.34, 0.00, null, - -8.34, 980.82); - checkInst(model, 31, 31, LocalDate.of(2019, 2, 1), 171, 29, 50.00, 50.00, null, 0.96952395, 48.48, 7727.66, 8.30, 0.00, null, -8.30, - 980.82); - checkInst(model, 32, 32, LocalDate.of(2019, 2, 2), 170, 30, 50.00, 50.00, null, 0.96848979, 48.42, 7685.91, 8.25, 0.00, null, -8.25, - 980.82); - checkInst(model, 33, 33, LocalDate.of(2019, 2, 3), 169, 31, 50.00, 50.00, null, 0.96745672, 48.37, 7644.12, 8.21, 0.00, null, -8.21, - 980.82); - checkInst(model, 34, 34, LocalDate.of(2019, 2, 4), 168, 32, 50.00, 50.00, null, 0.96642476, 48.32, 7602.28, 8.16, 0.00, null, -8.16, - 980.82); - checkInst(model, 35, 35, LocalDate.of(2019, 2, 5), 167, 33, 50.00, 50.00, null, 0.96539390, 48.27, 7560.40, 8.12, 0.00, null, -8.12, - 980.82); - checkInst(model, 36, 36, LocalDate.of(2019, 2, 6), 166, 34, 50.00, 50.00, null, 0.96436413, 48.22, 7518.47, 8.07, 0.00, null, -8.07, - 980.82); - checkInst(model, 37, 37, LocalDate.of(2019, 2, 7), 165, 35, 50.00, 50.00, null, 0.96333547, 48.17, 7476.50, 8.03, 0.00, null, -8.03, - 980.82); - checkInst(model, 38, 38, LocalDate.of(2019, 2, 8), 164, 36, 50.00, 50.00, null, 0.96230790, 48.12, 7434.48, 7.98, 0.00, null, -7.98, - 980.82); - checkInst(model, 39, 39, LocalDate.of(2019, 2, 9), 163, 37, 50.00, 50.00, null, 0.96128143, 48.06, 7392.42, 7.94, 0.00, null, -7.94, - 980.82); - checkInst(model, 40, 40, LocalDate.of(2019, 2, 10), 162, 38, 50.00, 50.00, null, 0.96025606, 48.01, 7350.31, 7.89, 0.00, null, - -7.89, 980.82); - checkInst(model, 41, 41, LocalDate.of(2019, 2, 11), 161, 39, 50.00, 50.00, null, 0.95923178, 47.96, 7308.16, 7.85, 0.00, null, - -7.85, 980.82); - checkInst(model, 42, 42, LocalDate.of(2019, 2, 12), 160, 40, 50.00, 50.00, null, 0.95820859, 47.91, 7265.97, 7.80, 0.00, null, - -7.80, 980.82); - checkInst(model, 43, 43, LocalDate.of(2019, 2, 13), 159, 41, 50.00, 50.00, null, 0.95718649, 47.86, 7223.72, 7.76, 0.00, null, - -7.76, 980.82); - checkInst(model, 44, 44, LocalDate.of(2019, 2, 14), 158, 42, 50.00, 50.00, null, 0.95616548, 47.81, 7181.44, 7.71, 0.00, null, - -7.71, 980.82); - checkInst(model, 45, 45, LocalDate.of(2019, 2, 15), 157, 43, 50.00, 50.00, null, 0.95514557, 47.76, 7139.11, 7.67, 0.00, null, - -7.67, 980.82); - checkInst(model, 46, 46, LocalDate.of(2019, 2, 16), 156, 44, 50.00, 50.00, null, 0.95412674, 47.71, 7096.73, 7.62, 0.00, null, - -7.62, 980.82); - checkInst(model, 47, 47, LocalDate.of(2019, 2, 17), 155, 45, 50.00, 50.00, null, 0.95310899, 47.66, 7054.31, 7.58, 0.00, null, - -7.58, 980.82); - checkInst(model, 48, 48, LocalDate.of(2019, 2, 18), 154, 46, 50.00, 50.00, null, 0.95209233, 47.60, 7011.84, 7.53, 0.00, null, - -7.53, 980.82); - checkInst(model, 49, 49, LocalDate.of(2019, 2, 19), 153, 47, 50.00, 50.00, null, 0.95107676, 47.55, 6969.33, 7.49, 0.00, null, - -7.49, 980.82); - checkInst(model, 50, 50, LocalDate.of(2019, 2, 20), 152, 48, 50.00, 50.00, null, 0.95006227, 47.50, 6926.77, 7.44, 0.00, null, - -7.44, 980.82); - checkInst(model, 51, 51, LocalDate.of(2019, 2, 21), 151, 49, 50.00, 50.00, null, 0.94904886, 47.45, 6884.17, 7.40, 0.00, null, - -7.40, 980.82); - checkInst(model, 52, 52, LocalDate.of(2019, 2, 22), 150, 50, 50.00, 50.00, null, 0.94803653, 47.40, 6841.52, 7.35, 0.00, null, - -7.35, 980.82); - checkInst(model, 53, 53, LocalDate.of(2019, 2, 23), 149, 51, 50.00, 50.00, null, 0.94702529, 47.35, 6798.82, 7.31, 0.00, null, - -7.31, 980.82); - checkInst(model, 54, 54, LocalDate.of(2019, 2, 24), 148, 52, 50.00, 50.00, null, 0.94601512, 47.30, 6756.08, 7.26, 0.00, null, - -7.26, 980.82); - checkInst(model, 55, 55, LocalDate.of(2019, 2, 25), 147, 53, 50.00, 50.00, null, 0.94500603, 47.25, 6713.30, 7.21, 0.00, null, - -7.21, 980.82); - checkInst(model, 56, 56, LocalDate.of(2019, 2, 26), 146, 54, 50.00, 50.00, null, 0.94399801, 47.20, 6670.47, 7.17, 0.00, null, - -7.17, 980.82); - checkInst(model, 57, 57, LocalDate.of(2019, 2, 27), 145, 55, 50.00, 50.00, null, 0.94299107, 47.15, 6627.59, 7.12, 0.00, null, - -7.12, 980.82); - checkInst(model, 58, 58, LocalDate.of(2019, 2, 28), 144, 56, 50.00, 50.00, null, 0.94198521, 47.10, 6584.67, 7.08, 0.00, null, - -7.08, 980.82); - checkInst(model, 59, 59, LocalDate.of(2019, 3, 1), 143, 57, 50.00, 50.00, null, 0.94098042, 47.05, 6541.70, 7.03, 0.00, null, -7.03, - 980.82); - checkInst(model, 60, 60, LocalDate.of(2019, 3, 2), 142, 58, 50.00, 50.00, null, 0.93997669, 47.00, 6498.68, 6.99, 0.00, null, -6.99, - 980.82); - checkInst(model, 61, 61, LocalDate.of(2019, 3, 3), 141, 59, 50.00, 50.00, null, 0.93897404, 46.95, 6455.62, 6.94, 0.00, null, -6.94, - 980.82); - checkInst(model, 62, 62, LocalDate.of(2019, 3, 4), 140, 60, 50.00, 50.00, null, 0.93797246, 46.90, 6412.51, 6.89, 0.00, null, -6.89, - 980.82); - checkInst(model, 63, 63, LocalDate.of(2019, 3, 5), 139, 61, 50.00, 50.00, null, 0.93697195, 46.85, 6369.36, 6.85, 0.00, null, -6.85, - 980.82); - checkInst(model, 64, 64, LocalDate.of(2019, 3, 6), 138, 62, 50.00, 50.00, null, 0.93597251, 46.80, 6326.16, 6.80, 0.00, null, -6.80, - 980.82); - checkInst(model, 65, 65, LocalDate.of(2019, 3, 7), 137, 63, 50.00, 50.00, null, 0.93497413, 46.75, 6282.92, 6.76, 0.00, null, -6.76, - 980.82); - checkInst(model, 66, 66, LocalDate.of(2019, 3, 8), 136, 64, 50.00, 50.00, null, 0.93397681, 46.70, 6239.63, 6.71, 0.00, null, -6.71, - 980.82); - checkInst(model, 67, 67, LocalDate.of(2019, 3, 9), 135, 65, 50.00, 50.00, null, 0.93298056, 46.65, 6196.29, 6.66, 0.00, null, -6.66, - 980.82); - checkInst(model, 68, 68, LocalDate.of(2019, 3, 10), 134, 66, 50.00, 50.00, null, 0.93198538, 46.60, 6152.91, 6.62, 0.00, null, - -6.62, 980.82); - checkInst(model, 69, 69, LocalDate.of(2019, 3, 11), 133, 67, 50.00, 50.00, null, 0.93099125, 46.55, 6109.48, 6.57, 0.00, null, - -6.57, 980.82); - checkInst(model, 70, 70, LocalDate.of(2019, 3, 12), 132, 68, 50.00, 50.00, null, 0.92999818, 46.50, 6066.00, 6.52, 0.00, null, - -6.52, 980.82); - checkInst(model, 71, 71, LocalDate.of(2019, 3, 13), 131, 69, 50.00, 50.00, null, 0.92900618, 46.45, 6022.48, 6.48, 0.00, null, - -6.48, 980.82); - checkInst(model, 72, 72, LocalDate.of(2019, 3, 14), 130, 70, 50.00, 50.00, null, 0.92801523, 46.40, 5978.91, 6.43, 0.00, null, - -6.43, 980.82); - checkInst(model, 73, 73, LocalDate.of(2019, 3, 15), 129, 71, 50.00, 50.00, null, 0.92702534, 46.35, 5935.29, 6.38, 0.00, null, - -6.38, 980.82); - checkInst(model, 74, 74, LocalDate.of(2019, 3, 16), 128, 72, 50.00, 50.00, null, 0.92603650, 46.30, 5891.63, 6.34, 0.00, null, - -6.34, 980.82); - checkInst(model, 75, 75, LocalDate.of(2019, 3, 17), 127, 73, 50.00, 50.00, null, 0.92504872, 46.25, 5847.92, 6.29, 0.00, null, - -6.29, 980.82); - checkInst(model, 76, 76, LocalDate.of(2019, 3, 18), 126, 74, 50.00, 50.00, null, 0.92406200, 46.20, 5804.17, 6.24, 0.00, null, - -6.24, 980.82); - checkInst(model, 77, 77, LocalDate.of(2019, 3, 19), 125, 75, 50.00, 50.00, null, 0.92307632, 46.15, 5760.36, 6.20, 0.00, null, - -6.20, 980.82); - checkInst(model, 78, 78, LocalDate.of(2019, 3, 20), 124, 76, 50.00, 50.00, null, 0.92209170, 46.10, 5716.52, 6.15, 0.00, null, - -6.15, 980.82); - checkInst(model, 79, 79, LocalDate.of(2019, 3, 21), 123, 77, 50.00, 50.00, null, 0.92110813, 46.06, 5672.62, 6.10, 0.00, null, - -6.10, 980.82); - checkInst(model, 80, 80, LocalDate.of(2019, 3, 22), 122, 78, 50.00, 50.00, null, 0.92012560, 46.01, 5628.68, 6.06, 0.00, null, - -6.06, 980.82); - checkInst(model, 81, 81, LocalDate.of(2019, 3, 23), 121, 79, 50.00, 50.00, null, 0.91914413, 45.96, 5584.69, 6.01, 0.00, null, - -6.01, 980.82); - checkInst(model, 82, 82, LocalDate.of(2019, 3, 24), 120, 80, 50.00, 50.00, null, 0.91816370, 45.91, 5540.65, 5.96, 0.00, null, - -5.96, 980.82); - checkInst(model, 83, 83, LocalDate.of(2019, 3, 25), 119, 81, 50.00, 50.00, null, 0.91718432, 45.86, 5496.57, 5.92, 0.00, null, - -5.92, 980.82); - checkInst(model, 84, 84, LocalDate.of(2019, 3, 26), 118, 82, 50.00, 50.00, null, 0.91620598, 45.81, 5452.44, 5.87, 0.00, null, - -5.87, 980.82); - checkInst(model, 85, 85, LocalDate.of(2019, 3, 27), 117, 83, 50.00, 50.00, null, 0.91522868, 45.76, 5408.26, 5.82, 0.00, null, - -5.82, 980.82); - checkInst(model, 86, 86, LocalDate.of(2019, 3, 28), 116, 84, 50.00, 50.00, null, 0.91425243, 45.71, 5364.03, 5.78, 0.00, null, - -5.78, 980.82); - checkInst(model, 87, 87, LocalDate.of(2019, 3, 29), 115, 85, 50.00, 50.00, null, 0.91327722, 45.66, 5319.76, 5.73, 0.00, null, - -5.73, 980.82); - checkInst(model, 88, 88, LocalDate.of(2019, 3, 30), 114, 86, 50.00, 50.00, null, 0.91230305, 45.62, 5275.44, 5.68, 0.00, null, - -5.68, 980.82); - checkInst(model, 89, 89, LocalDate.of(2019, 3, 31), 113, 87, 50.00, 50.00, null, 0.91132992, 45.57, 5231.08, 5.63, 0.00, null, - -5.63, 980.82); - checkInst(model, 90, 90, LocalDate.of(2019, 4, 1), 112, 88, 50.00, 50.00, null, 0.91035783, 45.52, 5186.66, 5.59, 0.00, null, -5.59, - 980.82); - checkInst(model, 91, 91, LocalDate.of(2019, 4, 2), 111, 89, 50.00, 50.00, null, 0.90938677, 45.47, 5142.20, 5.54, 0.00, null, -5.54, - 980.82); - checkInst(model, 92, 92, LocalDate.of(2019, 4, 3), 110, 90, 50.00, 50.00, null, 0.90841675, 45.42, 5097.69, 5.49, 0.00, null, -5.49, - 980.82); - checkInst(model, 93, 93, LocalDate.of(2019, 4, 4), 109, 91, 50.00, 50.00, null, 0.90744776, 45.37, 5053.13, 5.44, 0.00, null, -5.44, - 980.82); - checkInst(model, 94, 94, LocalDate.of(2019, 4, 5), 108, 92, 50.00, 50.00, null, 0.90647981, 45.32, 5008.53, 5.40, 0.00, null, -5.40, - 980.82); - checkInst(model, 95, 95, LocalDate.of(2019, 4, 6), 107, 93, 50.00, 50.00, null, 0.90551289, 45.28, 4963.88, 5.35, 0.00, null, -5.35, - 980.82); - checkInst(model, 96, 96, LocalDate.of(2019, 4, 7), 106, 94, 50.00, 50.00, null, 0.90454700, 45.23, 4919.18, 5.30, 0.00, null, -5.30, - 980.82); - checkInst(model, 97, 97, LocalDate.of(2019, 4, 8), 105, 95, 50.00, 50.00, null, 0.90358215, 45.18, 4874.43, 5.25, 0.00, null, -5.25, - 980.82); - checkInst(model, 98, 98, LocalDate.of(2019, 4, 9), 104, 96, 50.00, 50.00, null, 0.90261832, 45.13, 4829.64, 5.20, 0.00, null, -5.20, - 980.82); - checkInst(model, 99, 99, LocalDate.of(2019, 4, 10), 103, 97, 50.00, 50.00, null, 0.90165552, 45.08, 4784.79, 5.16, 0.00, null, - -5.16, 980.82); - checkInst(model, 100, 100, LocalDate.of(2019, 4, 11), 102, 98, 50.00, 50.00, null, 0.90069374, 45.03, 4739.90, 5.11, 0.00, null, - -5.11, 980.82); - checkInst(model, 101, 101, LocalDate.of(2019, 4, 12), 101, 99, 50.00, 50.00, null, 0.89973299, 44.99, 4694.96, 5.06, 0.00, null, - -5.06, 980.82); - checkInst(model, 102, 102, LocalDate.of(2019, 4, 13), 100, 100, 50.00, 50.00, null, 0.89877327, 44.94, 4649.98, 5.01, 0.00, null, - -5.01, 980.82); - checkInst(model, 103, 103, LocalDate.of(2019, 4, 14), 99, 101, 50.00, 50.00, null, 0.89781457, 44.89, 4604.94, 4.97, 0.00, null, - -4.97, 980.82); - checkInst(model, 104, 104, LocalDate.of(2019, 4, 15), 98, 102, 50.00, 50.00, null, 0.89685689, 44.84, 4559.86, 4.92, 0.00, null, - -4.92, 980.82); - checkInst(model, 105, 105, LocalDate.of(2019, 4, 16), 97, 103, 50.00, 50.00, null, 0.89590024, 44.80, 4514.73, 4.87, 0.00, null, - -4.87, 980.82); - checkInst(model, 106, 106, LocalDate.of(2019, 4, 17), 96, 104, 50.00, 50.00, null, 0.89494460, 44.75, 4469.55, 4.82, 0.00, null, - -4.82, 980.82); - checkInst(model, 107, 107, LocalDate.of(2019, 4, 18), 95, 105, 50.00, 50.00, null, 0.89398999, 44.70, 4424.32, 4.77, 0.00, null, - -4.77, 980.82); - checkInst(model, 108, 108, LocalDate.of(2019, 4, 19), 94, 106, 50.00, 50.00, null, 0.89303639, 44.65, 4379.05, 4.72, 0.00, null, - -4.72, 980.82); - checkInst(model, 109, 109, LocalDate.of(2019, 4, 20), 93, 107, 50.00, 50.00, null, 0.89208381, 44.60, 4333.72, 4.68, 0.00, null, - -4.68, 980.82); - checkInst(model, 110, 110, LocalDate.of(2019, 4, 21), 92, 108, 50.00, 50.00, null, 0.89113225, 44.56, 4288.35, 4.63, 0.00, null, - -4.63, 980.82); - checkInst(model, 111, 111, LocalDate.of(2019, 4, 22), 91, 109, 50.00, 50.00, null, 0.89018170, 44.51, 4242.93, 4.58, 0.00, null, - -4.58, 980.82); - checkInst(model, 112, 112, LocalDate.of(2019, 4, 23), 90, 110, 50.00, 50.00, null, 0.88923216, 44.46, 4197.46, 4.53, 0.00, null, - -4.53, 980.82); - checkInst(model, 113, 113, LocalDate.of(2019, 4, 24), 89, 111, 50.00, 50.00, null, 0.88828364, 44.41, 4151.94, 4.48, 0.00, null, - -4.48, 980.82); - checkInst(model, 114, 114, LocalDate.of(2019, 4, 25), 88, 112, 50.00, 50.00, null, 0.88733613, 44.37, 4106.38, 4.43, 0.00, null, - -4.43, 980.82); - checkInst(model, 115, 115, LocalDate.of(2019, 4, 26), 87, 113, 50.00, 50.00, null, 0.88638963, 44.32, 4060.76, 4.38, 0.00, null, - -4.38, 980.82); - checkInst(model, 116, 116, LocalDate.of(2019, 4, 27), 86, 114, 50.00, 50.00, null, 0.88544414, 44.27, 4015.10, 4.34, 0.00, null, - -4.34, 980.82); - checkInst(model, 117, 117, LocalDate.of(2019, 4, 28), 85, 115, 50.00, 50.00, null, 0.88449966, 44.22, 3969.38, 4.29, 0.00, null, - -4.29, 980.82); - checkInst(model, 118, 118, LocalDate.of(2019, 4, 29), 84, 116, 50.00, 50.00, null, 0.88355619, 44.18, 3923.62, 4.24, 0.00, null, - -4.24, 980.82); - checkInst(model, 119, 119, LocalDate.of(2019, 4, 30), 83, 117, 50.00, 50.00, null, 0.88261372, 44.13, 3877.81, 4.19, 0.00, null, - -4.19, 980.82); - checkInst(model, 120, 120, LocalDate.of(2019, 5, 1), 82, 118, 50.00, 50.00, null, 0.88167226, 44.08, 3831.95, 4.14, 0.00, null, - -4.14, 980.82); - checkInst(model, 121, 121, LocalDate.of(2019, 5, 2), 81, 119, 50.00, 50.00, null, 0.88073180, 44.04, 3786.04, 4.09, 0.00, null, - -4.09, 980.82); - checkInst(model, 122, 122, LocalDate.of(2019, 5, 3), 80, 120, 50.00, 50.00, null, 0.87979234, 43.99, 3740.09, 4.04, 0.00, null, - -4.04, 980.82); - checkInst(model, 123, 123, LocalDate.of(2019, 5, 4), 79, 121, 50.00, 50.00, null, 0.87885389, 43.94, 3694.08, 3.99, 0.00, null, - -3.99, 980.82); - checkInst(model, 124, 124, LocalDate.of(2019, 5, 5), 78, 122, 50.00, 50.00, null, 0.87791644, 43.90, 3648.03, 3.94, 0.00, null, - -3.94, 980.82); - checkInst(model, 125, 125, LocalDate.of(2019, 5, 6), 77, 123, 50.00, 50.00, null, 0.87697999, 43.85, 3601.92, 3.90, 0.00, null, - -3.90, 980.82); - checkInst(model, 126, 126, LocalDate.of(2019, 5, 7), 76, 124, 50.00, 50.00, null, 0.87604453, 43.80, 3555.77, 3.85, 0.00, null, - -3.85, 980.82); - checkInst(model, 127, 127, LocalDate.of(2019, 5, 8), 75, 125, 50.00, 50.00, null, 0.87511008, 43.76, 3509.56, 3.80, 0.00, null, - -3.80, 980.82); - checkInst(model, 128, 128, LocalDate.of(2019, 5, 9), 74, 126, 50.00, 50.00, null, 0.87417662, 43.71, 3463.31, 3.75, 0.00, null, - -3.75, 980.82); - checkInst(model, 129, 129, LocalDate.of(2019, 5, 10), 73, 127, 50.00, 50.00, null, 0.87324416, 43.66, 3417.01, 3.70, 0.00, null, - -3.70, 980.82); - checkInst(model, 130, 130, LocalDate.of(2019, 5, 11), 72, 128, 50.00, 50.00, null, 0.87231269, 43.62, 3370.66, 3.65, 0.00, null, - -3.65, 980.82); - checkInst(model, 131, 131, LocalDate.of(2019, 5, 12), 71, 129, 50.00, 50.00, null, 0.87138221, 43.57, 3324.26, 3.60, 0.00, null, - -3.60, 980.82); - checkInst(model, 132, 132, LocalDate.of(2019, 5, 13), 70, 130, 50.00, 50.00, null, 0.87045273, 43.52, 3277.81, 3.55, 0.00, null, - -3.55, 980.82); - checkInst(model, 133, 133, LocalDate.of(2019, 5, 14), 69, 131, 50.00, 50.00, null, 0.86952424, 43.48, 3231.31, 3.50, 0.00, null, - -3.50, 980.82); - checkInst(model, 134, 134, LocalDate.of(2019, 5, 15), 68, 132, 50.00, 50.00, null, 0.86859674, 43.43, 3184.76, 3.45, 0.00, null, - -3.45, 980.82); - checkInst(model, 135, 135, LocalDate.of(2019, 5, 16), 67, 133, 50.00, 50.00, null, 0.86767023, 43.38, 3138.16, 3.40, 0.00, null, - -3.40, 980.82); - checkInst(model, 136, 136, LocalDate.of(2019, 5, 17), 66, 134, 50.00, 50.00, null, 0.86674471, 43.34, 3091.51, 3.35, 0.00, null, - -3.35, 980.82); - checkInst(model, 137, 137, LocalDate.of(2019, 5, 18), 65, 135, 50.00, 50.00, null, 0.86582017, 43.29, 3044.81, 3.30, 0.00, null, - -3.30, 980.82); - checkInst(model, 138, 138, LocalDate.of(2019, 5, 19), 64, 136, 50.00, 50.00, null, 0.86489662, 43.24, 2998.06, 3.25, 0.00, null, - -3.25, 980.82); - checkInst(model, 139, 139, LocalDate.of(2019, 5, 20), 63, 137, 50.00, 50.00, null, 0.86397406, 43.20, 2951.26, 3.20, 0.00, null, - -3.20, 980.82); - checkInst(model, 140, 140, LocalDate.of(2019, 5, 21), 62, 138, 50.00, 50.00, null, 0.86305248, 43.15, 2904.42, 3.15, 0.00, null, - -3.15, 980.82); - checkInst(model, 141, 141, LocalDate.of(2019, 5, 22), 61, 139, 50.00, 50.00, null, 0.86213188, 43.11, 2857.52, 3.10, 0.00, null, - -3.10, 980.82); - checkInst(model, 142, 142, LocalDate.of(2019, 5, 23), 60, 140, 50.00, 50.00, null, 0.86121227, 43.06, 2810.57, 3.05, 0.00, null, - -3.05, 980.82); - checkInst(model, 143, 143, LocalDate.of(2019, 5, 24), 59, 141, 50.00, 50.00, null, 0.86029363, 43.01, 2763.57, 3.00, 0.00, null, - -3.00, 980.82); - checkInst(model, 144, 144, LocalDate.of(2019, 5, 25), 58, 142, 50.00, 50.00, null, 0.85937598, 42.97, 2716.52, 2.95, 0.00, null, - -2.95, 980.82); - checkInst(model, 145, 145, LocalDate.of(2019, 5, 26), 57, 143, 50.00, 50.00, null, 0.85845930, 42.92, 2669.42, 2.90, 0.00, null, - -2.90, 980.82); - checkInst(model, 146, 146, LocalDate.of(2019, 5, 27), 56, 144, 50.00, 50.00, null, 0.85754361, 42.88, 2622.27, 2.85, 0.00, null, - -2.85, 980.82); - checkInst(model, 147, 147, LocalDate.of(2019, 5, 28), 55, 145, 50.00, 50.00, null, 0.85662889, 42.83, 2575.07, 2.80, 0.00, null, - -2.80, 980.82); - checkInst(model, 148, 148, LocalDate.of(2019, 5, 29), 54, 146, 50.00, 50.00, null, 0.85571514, 42.79, 2527.82, 2.75, 0.00, null, - -2.75, 980.82); - checkInst(model, 149, 149, LocalDate.of(2019, 5, 30), 53, 147, 50.00, 50.00, null, 0.85480237, 42.74, 2480.52, 2.70, 0.00, null, - -2.70, 980.82); - checkInst(model, 150, 150, LocalDate.of(2019, 5, 31), 52, 148, 50.00, 50.00, null, 0.85389057, 42.69, 2433.17, 2.65, 0.00, null, - -2.65, 980.82); - checkInst(model, 151, 151, LocalDate.of(2019, 6, 1), 51, 149, 50.00, 50.00, null, 0.85297975, 42.65, 2385.77, 2.60, 0.00, null, - -2.60, 980.82); - checkInst(model, 152, 152, LocalDate.of(2019, 6, 2), 50, 150, 50.00, 50.00, null, 0.85206990, 42.60, 2338.31, 2.55, 0.00, null, - -2.55, 980.82); - checkInst(model, 153, 153, LocalDate.of(2019, 6, 3), 49, 151, 50.00, 50.00, null, 0.85116101, 42.56, 2290.81, 2.50, 0.00, null, - -2.50, 980.82); - checkInst(model, 154, 154, LocalDate.of(2019, 6, 4), 48, 152, 50.00, 50.00, null, 0.85025310, 42.51, 2243.26, 2.45, 0.00, null, - -2.45, 980.82); - checkInst(model, 155, 155, LocalDate.of(2019, 6, 5), 47, 153, 50.00, 50.00, null, 0.84934616, 42.47, 2195.65, 2.40, 0.00, null, - -2.40, 980.82); - checkInst(model, 156, 156, LocalDate.of(2019, 6, 6), 46, 154, 50.00, 50.00, null, 0.84844018, 42.42, 2148.00, 2.34, 0.00, null, - -2.34, 980.82); - checkInst(model, 157, 157, LocalDate.of(2019, 6, 7), 45, 155, 50.00, 50.00, null, 0.84753517, 42.38, 2100.29, 2.29, 0.00, null, - -2.29, 980.82); - checkInst(model, 158, 158, LocalDate.of(2019, 6, 8), 44, 156, 50.00, 50.00, null, 0.84663113, 42.33, 2052.53, 2.24, 0.00, null, - -2.24, 980.82); - checkInst(model, 159, 159, LocalDate.of(2019, 6, 9), 43, 157, 50.00, 50.00, null, 0.84572805, 42.29, 2004.73, 2.19, 0.00, null, - -2.19, 980.82); - checkInst(model, 160, 160, LocalDate.of(2019, 6, 10), 42, 158, 50.00, 50.00, null, 0.84482593, 42.24, 1956.87, 2.14, 0.00, null, - -2.14, 980.82); - checkInst(model, 161, 161, LocalDate.of(2019, 6, 11), 41, 159, 50.00, 50.00, null, 0.84392477, 42.20, 1908.96, 2.09, 0.00, null, - -2.09, 980.82); - checkInst(model, 162, 162, LocalDate.of(2019, 6, 12), 40, 160, 50.00, 50.00, null, 0.84302458, 42.15, 1860.99, 2.04, 0.00, null, - -2.04, 980.82); - checkInst(model, 163, 163, LocalDate.of(2019, 6, 13), 39, 161, 50.00, 50.00, null, 0.84212535, 42.11, 1812.98, 1.99, 0.00, null, - -1.99, 980.82); - checkInst(model, 164, 164, LocalDate.of(2019, 6, 14), 38, 162, 50.00, 50.00, null, 0.84122707, 42.06, 1764.92, 1.94, 0.00, null, - -1.94, 980.82); - checkInst(model, 165, 165, LocalDate.of(2019, 6, 15), 37, 163, 50.00, 50.00, null, 0.84032975, 42.02, 1716.80, 1.88, 0.00, null, - -1.88, 980.82); - checkInst(model, 166, 166, LocalDate.of(2019, 6, 16), 36, 164, 50.00, 50.00, null, 0.83943340, 41.97, 1668.64, 1.83, 0.00, null, - -1.83, 980.82); - checkInst(model, 167, 167, LocalDate.of(2019, 6, 17), 35, 165, 50.00, 50.00, null, 0.83853799, 41.93, 1620.42, 1.78, 0.00, null, - -1.78, 980.82); - checkInst(model, 168, 168, LocalDate.of(2019, 6, 18), 34, 166, 50.00, 50.00, null, 0.83764354, 41.88, 1572.15, 1.73, 0.00, null, - -1.73, 980.82); - checkInst(model, 169, 169, LocalDate.of(2019, 6, 19), 33, 167, 50.00, 50.00, null, 0.83675005, 41.84, 1523.83, 1.68, 0.00, null, - -1.68, 980.82); - checkInst(model, 170, 170, LocalDate.of(2019, 6, 20), 32, 168, 50.00, 50.00, null, 0.83585751, 41.79, 1475.45, 1.63, 0.00, null, - -1.63, 980.82); - checkInst(model, 171, 171, LocalDate.of(2019, 6, 21), 31, 169, 50.00, 50.00, null, 0.83496592, 41.75, 1427.03, 1.58, 0.00, null, - -1.58, 980.82); - checkInst(model, 172, 172, LocalDate.of(2019, 6, 22), 30, 170, 50.00, 50.00, null, 0.83407528, 41.70, 1378.55, 1.52, 0.00, null, - -1.52, 980.82); - checkInst(model, 173, 173, LocalDate.of(2019, 6, 23), 29, 171, 50.00, 50.00, null, 0.83318560, 41.66, 1330.02, 1.47, 0.00, null, - -1.47, 980.82); - checkInst(model, 174, 174, LocalDate.of(2019, 6, 24), 28, 172, 50.00, 50.00, null, 0.83229686, 41.61, 1281.45, 1.42, 0.00, null, - -1.42, 980.82); - checkInst(model, 175, 175, LocalDate.of(2019, 6, 25), 27, 173, 50.00, 50.00, null, 0.83140907, 41.57, 1232.81, 1.37, 0.00, null, - -1.37, 980.82); - checkInst(model, 176, 176, LocalDate.of(2019, 6, 26), 26, 174, 50.00, 50.00, null, 0.83052222, 41.53, 1184.13, 1.32, 0.00, null, - -1.32, 980.82); - checkInst(model, 177, 177, LocalDate.of(2019, 6, 27), 25, 175, 50.00, 50.00, null, 0.82963633, 41.48, 1135.39, 1.26, 0.00, null, - -1.26, 980.82); - checkInst(model, 178, 178, LocalDate.of(2019, 6, 28), 24, 176, 50.00, 50.00, null, 0.82875137, 41.44, 1086.61, 1.21, 0.00, null, - -1.21, 980.82); - checkInst(model, 179, 179, LocalDate.of(2019, 6, 29), 23, 177, 50.00, 50.00, null, 0.82786736, 41.39, 1037.77, 1.16, 0.00, null, - -1.16, 980.82); - checkInst(model, 180, 180, LocalDate.of(2019, 6, 30), 22, 178, 50.00, 50.00, null, 0.82698430, 41.35, 988.88, 1.11, 0.00, null, - -1.11, 980.82); - checkInst(model, 181, 181, LocalDate.of(2019, 7, 1), 21, 179, 50.00, 50.00, null, 0.82610217, 41.31, 939.93, 1.06, 0.00, null, - -1.06, 980.82); - checkInst(model, 182, 182, LocalDate.of(2019, 7, 2), 20, 180, 50.00, 50.00, null, 0.82522099, 41.26, 890.93, 1.00, 0.00, null, - -1.00, 980.82); - checkInst(model, 183, 183, LocalDate.of(2019, 7, 3), 19, 181, 50.00, 50.00, null, 0.82434075, 41.22, 841.89, 0.95, 0.00, null, - -0.95, 980.82); - checkInst(model, 184, 184, LocalDate.of(2019, 7, 4), 18, 182, 50.00, 50.00, null, 0.82346144, 41.17, 792.79, 0.90, 0.00, null, - -0.90, 980.82); - checkInst(model, 185, 185, LocalDate.of(2019, 7, 5), 17, 183, 50.00, 50.00, null, 0.82258308, 41.13, 743.63, 0.85, 0.00, null, - -0.85, 980.82); - checkInst(model, 186, 186, LocalDate.of(2019, 7, 6), 16, 184, 50.00, 50.00, null, 0.82170565, 41.09, 694.43, 0.79, 0.00, null, - -0.79, 980.82); - checkInst(model, 187, 187, LocalDate.of(2019, 7, 7), 15, 185, 50.00, 50.00, null, 0.82082916, 41.04, 645.17, 0.74, 0.00, null, - -0.74, 980.82); - checkInst(model, 188, 188, LocalDate.of(2019, 7, 8), 14, 186, 50.00, 50.00, null, 0.81995360, 41.00, 595.86, 0.69, 0.00, null, - -0.69, 980.82); - checkInst(model, 189, 189, LocalDate.of(2019, 7, 9), 13, 187, 50.00, 50.00, null, 0.81907897, 40.95, 546.49, 0.64, 0.00, null, - -0.64, 980.82); - checkInst(model, 190, 190, LocalDate.of(2019, 7, 10), 12, 188, 50.00, 50.00, null, 0.81820528, 40.91, 497.08, 0.58, 0.00, null, - -0.58, 980.82); - checkInst(model, 191, 191, LocalDate.of(2019, 7, 11), 11, 189, 50.00, 50.00, null, 0.81733252, 40.87, 447.61, 0.53, 0.00, null, - -0.53, 980.82); - checkInst(model, 192, 192, LocalDate.of(2019, 7, 12), 10, 190, 50.00, 50.00, null, 0.81646069, 40.82, 398.08, 0.48, 0.00, null, - -0.48, 980.82); - checkInst(model, 193, 193, LocalDate.of(2019, 7, 13), 9, 191, 50.00, 50.00, null, 0.81558979, 40.78, 348.51, 0.43, 0.00, null, - -0.43, 980.82); - checkInst(model, 194, 194, LocalDate.of(2019, 7, 14), 8, 192, 50.00, 50.00, null, 0.81471983, 40.74, 298.88, 0.37, 0.00, null, - -0.37, 980.82); - checkInst(model, 195, 195, LocalDate.of(2019, 7, 15), 7, 193, 50.00, 50.00, null, 0.81385078, 40.69, 249.20, 0.32, 0.00, null, - -0.32, 980.82); - checkInst(model, 196, 196, LocalDate.of(2019, 7, 16), 6, 194, 50.00, 50.00, null, 0.81298267, 40.65, 199.47, 0.27, 0.00, null, - -0.27, 980.82); - checkInst(model, 197, 197, LocalDate.of(2019, 7, 17), 5, 195, 50.00, 50.00, null, 0.81211548, 40.61, 149.68, 0.21, 0.00, null, - -0.21, 980.82); - checkInst(model, 198, 198, LocalDate.of(2019, 7, 18), 4, 196, 50.00, 50.00, null, 0.81124922, 40.56, 99.84, 0.16, 0.00, null, -0.16, - 980.82); - checkInst(model, 199, 199, LocalDate.of(2019, 7, 19), 3, 197, 50.00, 50.00, null, 0.81038388, 40.52, 49.95, 0.11, 0.00, null, -0.11, - 980.82); - checkInst(model, 200, 200, LocalDate.of(2019, 7, 20), 2, 198, 50.00, 50.00, null, 0.80951946, 40.48, 0.00, 0.05, 0.00, null, -0.05, - 980.82); + assertEquals(10, model.projectedPayments().size(), "disbursement + 9 regular (period 10 removed, forecast was 0)"); + + checkInst(model, 0, 0, EXPECTED_DISBURSEMENT_DATE, 0, -450.00, null, null, 1.00000000, -450.00, 450.00, null, null, null, null, + 50.00); + checkInst(model, 1, 1, LocalDate.of(2019, 1, 2), 0, 50.00, 50.00, 110.00, 1.00000000, 110.00, 408.83, 8.83, 18.30, 18.30, 9.47, + 31.70); + checkInst(model, 2, 2, LocalDate.of(2019, 1, 3), 1, 50.00, 50.00, null, 0.98074794, 49.04, 366.86, 8.03, 0.00, null, null, 31.70); + checkInst(model, 3, 3, LocalDate.of(2019, 1, 4), 2, 50.00, 50.00, null, 0.96186652, 48.09, 324.06, 7.20, 0.00, null, null, 31.70); + checkInst(model, 4, 4, LocalDate.of(2019, 1, 5), 3, 50.00, 50.00, null, 0.94334860, 47.17, 280.42, 6.36, 0.00, null, null, 31.70); + checkInst(model, 5, 5, LocalDate.of(2019, 1, 6), 4, 50.00, 50.00, null, 0.92518720, 46.26, 235.93, 5.50, 0.00, null, null, 31.70); + checkInst(model, 6, 6, LocalDate.of(2019, 1, 7), 5, 50.00, 50.00, null, 0.90737544, 45.37, 190.56, 4.63, 0.00, null, null, 31.70); + checkInst(model, 7, 7, LocalDate.of(2019, 1, 8), 6, 50.00, 50.00, null, 0.88990659, 44.50, 144.30, 3.74, 0.00, null, null, 31.70); + checkInst(model, 8, 8, LocalDate.of(2019, 1, 9), 7, 50.00, 50.00, null, 0.87277405, 43.64, 97.13, 2.83, 0.00, null, null, 31.70); + checkInst(model, 9, 9, LocalDate.of(2019, 1, 10), 8, 50.00, 40.00, null, 0.85597135, 34.24, 49.04, 1.91, 0.00, null, null, 31.70); } + // ============================================================================================== + // applyRateChange tests — rate change mid-lifecycle with date gaps + // ============================================================================================== + @Test - void testExcessPayment_term200_originationFee1000_netDisbursement9000_pay70_80() { + void testApplyRateChange_sameDayAsDisburse() { final ProjectedAmortizationScheduleModel model = generateModel(); + model.applyRateChange(new BigDecimal("0.15"), EXPECTED_DISBURSEMENT_DATE); - calculator.applyPayment(model, EXPECTED_DISBURSEMENT_DATE.plusDays(1), new BigDecimal("70")); - calculator.applyPayment(model, EXPECTED_DISBURSEMENT_DATE.plusDays(2), new BigDecimal("80")); - - checkInst(model, 0, 0, EXPECTED_DISBURSEMENT_DATE, 202, 0, -9000.00, null, null, 1.00000000, -9000.00, 9000.00, null, null, null, - null, 1000.00); - - checkInst(model, 1, 1, LocalDate.of(2019, 1, 2), 201, 0, 50.00, 50.00, 70.00, 1.00000000, 70.00, 8959.61, 9.61, 28.70, 13.44, 3.83, - 986.56); - checkInst(model, 2, 2, LocalDate.of(2019, 1, 3), 200, 0, 50.00, 50.00, 80.00, 1.00000000, 80.00, 8919.18, 9.57, 15.26, 15.26, 5.70, - 971.30); - - checkInst(model, 3, 3, LocalDate.of(2019, 1, 4), 199, 1, 50.00, 50.00, null, 0.99893332, 49.95, 8878.70, 9.52, 0.00, null, -9.52, - 971.30); - checkInst(model, 4, 4, LocalDate.of(2019, 1, 5), 198, 2, 50.00, 50.00, null, 0.99786779, 49.89, 8838.18, 9.48, 0.00, null, -9.48, - 971.30); - checkInst(model, 5, 5, LocalDate.of(2019, 1, 6), 197, 3, 50.00, 50.00, null, 0.99680339, 49.84, 8797.62, 9.44, 0.00, null, -9.44, - 971.30); - checkInst(model, 6, 6, LocalDate.of(2019, 1, 7), 196, 4, 50.00, 50.00, null, 0.99574012, 49.79, 8757.01, 9.39, 0.00, null, -9.39, - 971.30); - checkInst(model, 7, 7, LocalDate.of(2019, 1, 8), 195, 5, 50.00, 50.00, null, 0.99467799, 49.73, 8716.36, 9.35, 0.00, null, -9.35, - 971.30); - checkInst(model, 8, 8, LocalDate.of(2019, 1, 9), 194, 6, 50.00, 50.00, null, 0.99361699, 49.68, 8675.67, 9.31, 0.00, null, -9.31, - 971.30); - checkInst(model, 9, 9, LocalDate.of(2019, 1, 10), 193, 7, 50.00, 50.00, null, 0.99255712, 49.63, 8634.94, 9.26, 0.00, null, -9.26, - 971.30); - checkInst(model, 10, 10, LocalDate.of(2019, 1, 11), 192, 8, 50.00, 50.00, null, 0.99149839, 49.57, 8594.16, 9.22, 0.00, null, -9.22, - 971.30); - checkInst(model, 11, 11, LocalDate.of(2019, 1, 12), 191, 9, 50.00, 50.00, null, 0.99044078, 49.52, 8553.33, 9.18, 0.00, null, -9.18, - 971.30); - checkInst(model, 12, 12, LocalDate.of(2019, 1, 13), 190, 10, 50.00, 50.00, null, 0.98938430, 49.47, 8512.47, 9.13, 0.00, null, - -9.13, 971.30); - checkInst(model, 13, 13, LocalDate.of(2019, 1, 14), 189, 11, 50.00, 50.00, null, 0.98832895, 49.42, 8471.56, 9.09, 0.00, null, - -9.09, 971.30); - checkInst(model, 14, 14, LocalDate.of(2019, 1, 15), 188, 12, 50.00, 50.00, null, 0.98727472, 49.36, 8430.60, 9.05, 0.00, null, - -9.05, 971.30); - checkInst(model, 15, 15, LocalDate.of(2019, 1, 16), 187, 13, 50.00, 50.00, null, 0.98622162, 49.31, 8389.61, 9.00, 0.00, null, - -9.00, 971.30); - checkInst(model, 16, 16, LocalDate.of(2019, 1, 17), 186, 14, 50.00, 50.00, null, 0.98516964, 49.26, 8348.56, 8.96, 0.00, null, - -8.96, 971.30); - checkInst(model, 17, 17, LocalDate.of(2019, 1, 18), 185, 15, 50.00, 50.00, null, 0.98411879, 49.21, 8307.48, 8.91, 0.00, null, - -8.91, 971.30); - checkInst(model, 18, 18, LocalDate.of(2019, 1, 19), 184, 16, 50.00, 50.00, null, 0.98306905, 49.15, 8266.35, 8.87, 0.00, null, - -8.87, 971.30); - checkInst(model, 19, 19, LocalDate.of(2019, 1, 20), 183, 17, 50.00, 50.00, null, 0.98202044, 49.10, 8225.18, 8.83, 0.00, null, - -8.83, 971.30); - checkInst(model, 20, 20, LocalDate.of(2019, 1, 21), 182, 18, 50.00, 50.00, null, 0.98097294, 49.05, 8183.96, 8.78, 0.00, null, - -8.78, 971.30); - checkInst(model, 21, 21, LocalDate.of(2019, 1, 22), 181, 19, 50.00, 50.00, null, 0.97992656, 49.00, 8142.70, 8.74, 0.00, null, - -8.74, 971.30); - checkInst(model, 22, 22, LocalDate.of(2019, 1, 23), 180, 20, 50.00, 50.00, null, 0.97888129, 48.94, 8101.39, 8.69, 0.00, null, - -8.69, 971.30); - checkInst(model, 23, 23, LocalDate.of(2019, 1, 24), 179, 21, 50.00, 50.00, null, 0.97783715, 48.89, 8060.04, 8.65, 0.00, null, - -8.65, 971.30); - checkInst(model, 24, 24, LocalDate.of(2019, 1, 25), 178, 22, 50.00, 50.00, null, 0.97679411, 48.84, 8018.65, 8.61, 0.00, null, - -8.61, 971.30); - checkInst(model, 25, 25, LocalDate.of(2019, 1, 26), 177, 23, 50.00, 50.00, null, 0.97575219, 48.79, 7977.21, 8.56, 0.00, null, - -8.56, 971.30); - checkInst(model, 26, 26, LocalDate.of(2019, 1, 27), 176, 24, 50.00, 50.00, null, 0.97471138, 48.74, 7935.73, 8.52, 0.00, null, - -8.52, 971.30); - checkInst(model, 27, 27, LocalDate.of(2019, 1, 28), 175, 25, 50.00, 50.00, null, 0.97367168, 48.68, 7894.21, 8.47, 0.00, null, - -8.47, 971.30); - checkInst(model, 28, 28, LocalDate.of(2019, 1, 29), 174, 26, 50.00, 50.00, null, 0.97263309, 48.63, 7852.63, 8.43, 0.00, null, - -8.43, 971.30); - checkInst(model, 29, 29, LocalDate.of(2019, 1, 30), 173, 27, 50.00, 50.00, null, 0.97159560, 48.58, 7811.02, 8.39, 0.00, null, - -8.39, 971.30); - checkInst(model, 30, 30, LocalDate.of(2019, 1, 31), 172, 28, 50.00, 50.00, null, 0.97055922, 48.53, 7769.36, 8.34, 0.00, null, - -8.34, 971.30); - checkInst(model, 31, 31, LocalDate.of(2019, 2, 1), 171, 29, 50.00, 50.00, null, 0.96952395, 48.48, 7727.66, 8.30, 0.00, null, -8.30, - 971.30); - checkInst(model, 32, 32, LocalDate.of(2019, 2, 2), 170, 30, 50.00, 50.00, null, 0.96848979, 48.42, 7685.91, 8.25, 0.00, null, -8.25, - 971.30); - checkInst(model, 33, 33, LocalDate.of(2019, 2, 3), 169, 31, 50.00, 50.00, null, 0.96745672, 48.37, 7644.12, 8.21, 0.00, null, -8.21, - 971.30); - checkInst(model, 34, 34, LocalDate.of(2019, 2, 4), 168, 32, 50.00, 50.00, null, 0.96642476, 48.32, 7602.28, 8.16, 0.00, null, -8.16, - 971.30); - checkInst(model, 35, 35, LocalDate.of(2019, 2, 5), 167, 33, 50.00, 50.00, null, 0.96539390, 48.27, 7560.40, 8.12, 0.00, null, -8.12, - 971.30); - checkInst(model, 36, 36, LocalDate.of(2019, 2, 6), 166, 34, 50.00, 50.00, null, 0.96436413, 48.22, 7518.47, 8.07, 0.00, null, -8.07, - 971.30); - checkInst(model, 37, 37, LocalDate.of(2019, 2, 7), 165, 35, 50.00, 50.00, null, 0.96333547, 48.17, 7476.50, 8.03, 0.00, null, -8.03, - 971.30); - checkInst(model, 38, 38, LocalDate.of(2019, 2, 8), 164, 36, 50.00, 50.00, null, 0.96230790, 48.12, 7434.48, 7.98, 0.00, null, -7.98, - 971.30); - checkInst(model, 39, 39, LocalDate.of(2019, 2, 9), 163, 37, 50.00, 50.00, null, 0.96128143, 48.06, 7392.42, 7.94, 0.00, null, -7.94, - 971.30); - checkInst(model, 40, 40, LocalDate.of(2019, 2, 10), 162, 38, 50.00, 50.00, null, 0.96025606, 48.01, 7350.31, 7.89, 0.00, null, - -7.89, 971.30); - checkInst(model, 41, 41, LocalDate.of(2019, 2, 11), 161, 39, 50.00, 50.00, null, 0.95923178, 47.96, 7308.16, 7.85, 0.00, null, - -7.85, 971.30); - checkInst(model, 42, 42, LocalDate.of(2019, 2, 12), 160, 40, 50.00, 50.00, null, 0.95820859, 47.91, 7265.97, 7.80, 0.00, null, - -7.80, 971.30); - checkInst(model, 43, 43, LocalDate.of(2019, 2, 13), 159, 41, 50.00, 50.00, null, 0.95718649, 47.86, 7223.72, 7.76, 0.00, null, - -7.76, 971.30); - checkInst(model, 44, 44, LocalDate.of(2019, 2, 14), 158, 42, 50.00, 50.00, null, 0.95616548, 47.81, 7181.44, 7.71, 0.00, null, - -7.71, 971.30); - checkInst(model, 45, 45, LocalDate.of(2019, 2, 15), 157, 43, 50.00, 50.00, null, 0.95514557, 47.76, 7139.11, 7.67, 0.00, null, - -7.67, 971.30); - checkInst(model, 46, 46, LocalDate.of(2019, 2, 16), 156, 44, 50.00, 50.00, null, 0.95412674, 47.71, 7096.73, 7.62, 0.00, null, - -7.62, 971.30); - checkInst(model, 47, 47, LocalDate.of(2019, 2, 17), 155, 45, 50.00, 50.00, null, 0.95310899, 47.66, 7054.31, 7.58, 0.00, null, - -7.58, 971.30); - checkInst(model, 48, 48, LocalDate.of(2019, 2, 18), 154, 46, 50.00, 50.00, null, 0.95209233, 47.60, 7011.84, 7.53, 0.00, null, - -7.53, 971.30); - checkInst(model, 49, 49, LocalDate.of(2019, 2, 19), 153, 47, 50.00, 50.00, null, 0.95107676, 47.55, 6969.33, 7.49, 0.00, null, - -7.49, 971.30); - checkInst(model, 50, 50, LocalDate.of(2019, 2, 20), 152, 48, 50.00, 50.00, null, 0.95006227, 47.50, 6926.77, 7.44, 0.00, null, - -7.44, 971.30); - checkInst(model, 51, 51, LocalDate.of(2019, 2, 21), 151, 49, 50.00, 50.00, null, 0.94904886, 47.45, 6884.17, 7.40, 0.00, null, - -7.40, 971.30); - checkInst(model, 52, 52, LocalDate.of(2019, 2, 22), 150, 50, 50.00, 50.00, null, 0.94803653, 47.40, 6841.52, 7.35, 0.00, null, - -7.35, 971.30); - checkInst(model, 53, 53, LocalDate.of(2019, 2, 23), 149, 51, 50.00, 50.00, null, 0.94702529, 47.35, 6798.82, 7.31, 0.00, null, - -7.31, 971.30); - checkInst(model, 54, 54, LocalDate.of(2019, 2, 24), 148, 52, 50.00, 50.00, null, 0.94601512, 47.30, 6756.08, 7.26, 0.00, null, - -7.26, 971.30); - checkInst(model, 55, 55, LocalDate.of(2019, 2, 25), 147, 53, 50.00, 50.00, null, 0.94500603, 47.25, 6713.30, 7.21, 0.00, null, - -7.21, 971.30); - checkInst(model, 56, 56, LocalDate.of(2019, 2, 26), 146, 54, 50.00, 50.00, null, 0.94399801, 47.20, 6670.47, 7.17, 0.00, null, - -7.17, 971.30); - checkInst(model, 57, 57, LocalDate.of(2019, 2, 27), 145, 55, 50.00, 50.00, null, 0.94299107, 47.15, 6627.59, 7.12, 0.00, null, - -7.12, 971.30); - checkInst(model, 58, 58, LocalDate.of(2019, 2, 28), 144, 56, 50.00, 50.00, null, 0.94198521, 47.10, 6584.67, 7.08, 0.00, null, - -7.08, 971.30); - checkInst(model, 59, 59, LocalDate.of(2019, 3, 1), 143, 57, 50.00, 50.00, null, 0.94098042, 47.05, 6541.70, 7.03, 0.00, null, -7.03, - 971.30); - checkInst(model, 60, 60, LocalDate.of(2019, 3, 2), 142, 58, 50.00, 50.00, null, 0.93997669, 47.00, 6498.68, 6.99, 0.00, null, -6.99, - 971.30); - checkInst(model, 61, 61, LocalDate.of(2019, 3, 3), 141, 59, 50.00, 50.00, null, 0.93897404, 46.95, 6455.62, 6.94, 0.00, null, -6.94, - 971.30); - checkInst(model, 62, 62, LocalDate.of(2019, 3, 4), 140, 60, 50.00, 50.00, null, 0.93797246, 46.90, 6412.51, 6.89, 0.00, null, -6.89, - 971.30); - checkInst(model, 63, 63, LocalDate.of(2019, 3, 5), 139, 61, 50.00, 50.00, null, 0.93697195, 46.85, 6369.36, 6.85, 0.00, null, -6.85, - 971.30); - checkInst(model, 64, 64, LocalDate.of(2019, 3, 6), 138, 62, 50.00, 50.00, null, 0.93597251, 46.80, 6326.16, 6.80, 0.00, null, -6.80, - 971.30); - checkInst(model, 65, 65, LocalDate.of(2019, 3, 7), 137, 63, 50.00, 50.00, null, 0.93497413, 46.75, 6282.92, 6.76, 0.00, null, -6.76, - 971.30); - checkInst(model, 66, 66, LocalDate.of(2019, 3, 8), 136, 64, 50.00, 50.00, null, 0.93397681, 46.70, 6239.63, 6.71, 0.00, null, -6.71, - 971.30); - checkInst(model, 67, 67, LocalDate.of(2019, 3, 9), 135, 65, 50.00, 50.00, null, 0.93298056, 46.65, 6196.29, 6.66, 0.00, null, -6.66, - 971.30); - checkInst(model, 68, 68, LocalDate.of(2019, 3, 10), 134, 66, 50.00, 50.00, null, 0.93198538, 46.60, 6152.91, 6.62, 0.00, null, - -6.62, 971.30); - checkInst(model, 69, 69, LocalDate.of(2019, 3, 11), 133, 67, 50.00, 50.00, null, 0.93099125, 46.55, 6109.48, 6.57, 0.00, null, - -6.57, 971.30); - checkInst(model, 70, 70, LocalDate.of(2019, 3, 12), 132, 68, 50.00, 50.00, null, 0.92999818, 46.50, 6066.00, 6.52, 0.00, null, - -6.52, 971.30); - checkInst(model, 71, 71, LocalDate.of(2019, 3, 13), 131, 69, 50.00, 50.00, null, 0.92900618, 46.45, 6022.48, 6.48, 0.00, null, - -6.48, 971.30); - checkInst(model, 72, 72, LocalDate.of(2019, 3, 14), 130, 70, 50.00, 50.00, null, 0.92801523, 46.40, 5978.91, 6.43, 0.00, null, - -6.43, 971.30); - checkInst(model, 73, 73, LocalDate.of(2019, 3, 15), 129, 71, 50.00, 50.00, null, 0.92702534, 46.35, 5935.29, 6.38, 0.00, null, - -6.38, 971.30); - checkInst(model, 74, 74, LocalDate.of(2019, 3, 16), 128, 72, 50.00, 50.00, null, 0.92603650, 46.30, 5891.63, 6.34, 0.00, null, - -6.34, 971.30); - checkInst(model, 75, 75, LocalDate.of(2019, 3, 17), 127, 73, 50.00, 50.00, null, 0.92504872, 46.25, 5847.92, 6.29, 0.00, null, - -6.29, 971.30); - checkInst(model, 76, 76, LocalDate.of(2019, 3, 18), 126, 74, 50.00, 50.00, null, 0.92406200, 46.20, 5804.17, 6.24, 0.00, null, - -6.24, 971.30); - checkInst(model, 77, 77, LocalDate.of(2019, 3, 19), 125, 75, 50.00, 50.00, null, 0.92307632, 46.15, 5760.36, 6.20, 0.00, null, - -6.20, 971.30); - checkInst(model, 78, 78, LocalDate.of(2019, 3, 20), 124, 76, 50.00, 50.00, null, 0.92209170, 46.10, 5716.52, 6.15, 0.00, null, - -6.15, 971.30); - checkInst(model, 79, 79, LocalDate.of(2019, 3, 21), 123, 77, 50.00, 50.00, null, 0.92110813, 46.06, 5672.62, 6.10, 0.00, null, - -6.10, 971.30); - checkInst(model, 80, 80, LocalDate.of(2019, 3, 22), 122, 78, 50.00, 50.00, null, 0.92012560, 46.01, 5628.68, 6.06, 0.00, null, - -6.06, 971.30); - checkInst(model, 81, 81, LocalDate.of(2019, 3, 23), 121, 79, 50.00, 50.00, null, 0.91914413, 45.96, 5584.69, 6.01, 0.00, null, - -6.01, 971.30); - checkInst(model, 82, 82, LocalDate.of(2019, 3, 24), 120, 80, 50.00, 50.00, null, 0.91816370, 45.91, 5540.65, 5.96, 0.00, null, - -5.96, 971.30); - checkInst(model, 83, 83, LocalDate.of(2019, 3, 25), 119, 81, 50.00, 50.00, null, 0.91718432, 45.86, 5496.57, 5.92, 0.00, null, - -5.92, 971.30); - checkInst(model, 84, 84, LocalDate.of(2019, 3, 26), 118, 82, 50.00, 50.00, null, 0.91620598, 45.81, 5452.44, 5.87, 0.00, null, - -5.87, 971.30); - checkInst(model, 85, 85, LocalDate.of(2019, 3, 27), 117, 83, 50.00, 50.00, null, 0.91522868, 45.76, 5408.26, 5.82, 0.00, null, - -5.82, 971.30); - checkInst(model, 86, 86, LocalDate.of(2019, 3, 28), 116, 84, 50.00, 50.00, null, 0.91425243, 45.71, 5364.03, 5.78, 0.00, null, - -5.78, 971.30); - checkInst(model, 87, 87, LocalDate.of(2019, 3, 29), 115, 85, 50.00, 50.00, null, 0.91327722, 45.66, 5319.76, 5.73, 0.00, null, - -5.73, 971.30); - checkInst(model, 88, 88, LocalDate.of(2019, 3, 30), 114, 86, 50.00, 50.00, null, 0.91230305, 45.62, 5275.44, 5.68, 0.00, null, - -5.68, 971.30); - checkInst(model, 89, 89, LocalDate.of(2019, 3, 31), 113, 87, 50.00, 50.00, null, 0.91132992, 45.57, 5231.08, 5.63, 0.00, null, - -5.63, 971.30); - checkInst(model, 90, 90, LocalDate.of(2019, 4, 1), 112, 88, 50.00, 50.00, null, 0.91035783, 45.52, 5186.66, 5.59, 0.00, null, -5.59, - 971.30); - checkInst(model, 91, 91, LocalDate.of(2019, 4, 2), 111, 89, 50.00, 50.00, null, 0.90938677, 45.47, 5142.20, 5.54, 0.00, null, -5.54, - 971.30); - checkInst(model, 92, 92, LocalDate.of(2019, 4, 3), 110, 90, 50.00, 50.00, null, 0.90841675, 45.42, 5097.69, 5.49, 0.00, null, -5.49, - 971.30); - checkInst(model, 93, 93, LocalDate.of(2019, 4, 4), 109, 91, 50.00, 50.00, null, 0.90744776, 45.37, 5053.13, 5.44, 0.00, null, -5.44, - 971.30); - checkInst(model, 94, 94, LocalDate.of(2019, 4, 5), 108, 92, 50.00, 50.00, null, 0.90647981, 45.32, 5008.53, 5.40, 0.00, null, -5.40, - 971.30); - checkInst(model, 95, 95, LocalDate.of(2019, 4, 6), 107, 93, 50.00, 50.00, null, 0.90551289, 45.28, 4963.88, 5.35, 0.00, null, -5.35, - 971.30); - checkInst(model, 96, 96, LocalDate.of(2019, 4, 7), 106, 94, 50.00, 50.00, null, 0.90454700, 45.23, 4919.18, 5.30, 0.00, null, -5.30, - 971.30); - checkInst(model, 97, 97, LocalDate.of(2019, 4, 8), 105, 95, 50.00, 50.00, null, 0.90358215, 45.18, 4874.43, 5.25, 0.00, null, -5.25, - 971.30); - checkInst(model, 98, 98, LocalDate.of(2019, 4, 9), 104, 96, 50.00, 50.00, null, 0.90261832, 45.13, 4829.64, 5.20, 0.00, null, -5.20, - 971.30); - checkInst(model, 99, 99, LocalDate.of(2019, 4, 10), 103, 97, 50.00, 50.00, null, 0.90165552, 45.08, 4784.79, 5.16, 0.00, null, - -5.16, 971.30); - checkInst(model, 100, 100, LocalDate.of(2019, 4, 11), 102, 98, 50.00, 50.00, null, 0.90069374, 45.03, 4739.90, 5.11, 0.00, null, - -5.11, 971.30); - checkInst(model, 101, 101, LocalDate.of(2019, 4, 12), 101, 99, 50.00, 50.00, null, 0.89973299, 44.99, 4694.96, 5.06, 0.00, null, - -5.06, 971.30); - checkInst(model, 102, 102, LocalDate.of(2019, 4, 13), 100, 100, 50.00, 50.00, null, 0.89877327, 44.94, 4649.98, 5.01, 0.00, null, - -5.01, 971.30); - checkInst(model, 103, 103, LocalDate.of(2019, 4, 14), 99, 101, 50.00, 50.00, null, 0.89781457, 44.89, 4604.94, 4.97, 0.00, null, - -4.97, 971.30); - checkInst(model, 104, 104, LocalDate.of(2019, 4, 15), 98, 102, 50.00, 50.00, null, 0.89685689, 44.84, 4559.86, 4.92, 0.00, null, - -4.92, 971.30); - checkInst(model, 105, 105, LocalDate.of(2019, 4, 16), 97, 103, 50.00, 50.00, null, 0.89590024, 44.80, 4514.73, 4.87, 0.00, null, - -4.87, 971.30); - checkInst(model, 106, 106, LocalDate.of(2019, 4, 17), 96, 104, 50.00, 50.00, null, 0.89494460, 44.75, 4469.55, 4.82, 0.00, null, - -4.82, 971.30); - checkInst(model, 107, 107, LocalDate.of(2019, 4, 18), 95, 105, 50.00, 50.00, null, 0.89398999, 44.70, 4424.32, 4.77, 0.00, null, - -4.77, 971.30); - checkInst(model, 108, 108, LocalDate.of(2019, 4, 19), 94, 106, 50.00, 50.00, null, 0.89303639, 44.65, 4379.05, 4.72, 0.00, null, - -4.72, 971.30); - checkInst(model, 109, 109, LocalDate.of(2019, 4, 20), 93, 107, 50.00, 50.00, null, 0.89208381, 44.60, 4333.72, 4.68, 0.00, null, - -4.68, 971.30); - checkInst(model, 110, 110, LocalDate.of(2019, 4, 21), 92, 108, 50.00, 50.00, null, 0.89113225, 44.56, 4288.35, 4.63, 0.00, null, - -4.63, 971.30); - checkInst(model, 111, 111, LocalDate.of(2019, 4, 22), 91, 109, 50.00, 50.00, null, 0.89018170, 44.51, 4242.93, 4.58, 0.00, null, - -4.58, 971.30); - checkInst(model, 112, 112, LocalDate.of(2019, 4, 23), 90, 110, 50.00, 50.00, null, 0.88923216, 44.46, 4197.46, 4.53, 0.00, null, - -4.53, 971.30); - checkInst(model, 113, 113, LocalDate.of(2019, 4, 24), 89, 111, 50.00, 50.00, null, 0.88828364, 44.41, 4151.94, 4.48, 0.00, null, - -4.48, 971.30); - checkInst(model, 114, 114, LocalDate.of(2019, 4, 25), 88, 112, 50.00, 50.00, null, 0.88733613, 44.37, 4106.38, 4.43, 0.00, null, - -4.43, 971.30); - checkInst(model, 115, 115, LocalDate.of(2019, 4, 26), 87, 113, 50.00, 50.00, null, 0.88638963, 44.32, 4060.76, 4.38, 0.00, null, - -4.38, 971.30); - checkInst(model, 116, 116, LocalDate.of(2019, 4, 27), 86, 114, 50.00, 50.00, null, 0.88544414, 44.27, 4015.10, 4.34, 0.00, null, - -4.34, 971.30); - checkInst(model, 117, 117, LocalDate.of(2019, 4, 28), 85, 115, 50.00, 50.00, null, 0.88449966, 44.22, 3969.38, 4.29, 0.00, null, - -4.29, 971.30); - checkInst(model, 118, 118, LocalDate.of(2019, 4, 29), 84, 116, 50.00, 50.00, null, 0.88355619, 44.18, 3923.62, 4.24, 0.00, null, - -4.24, 971.30); - checkInst(model, 119, 119, LocalDate.of(2019, 4, 30), 83, 117, 50.00, 50.00, null, 0.88261372, 44.13, 3877.81, 4.19, 0.00, null, - -4.19, 971.30); - checkInst(model, 120, 120, LocalDate.of(2019, 5, 1), 82, 118, 50.00, 50.00, null, 0.88167226, 44.08, 3831.95, 4.14, 0.00, null, - -4.14, 971.30); - checkInst(model, 121, 121, LocalDate.of(2019, 5, 2), 81, 119, 50.00, 50.00, null, 0.88073180, 44.04, 3786.04, 4.09, 0.00, null, - -4.09, 971.30); - checkInst(model, 122, 122, LocalDate.of(2019, 5, 3), 80, 120, 50.00, 50.00, null, 0.87979234, 43.99, 3740.09, 4.04, 0.00, null, - -4.04, 971.30); - checkInst(model, 123, 123, LocalDate.of(2019, 5, 4), 79, 121, 50.00, 50.00, null, 0.87885389, 43.94, 3694.08, 3.99, 0.00, null, - -3.99, 971.30); - checkInst(model, 124, 124, LocalDate.of(2019, 5, 5), 78, 122, 50.00, 50.00, null, 0.87791644, 43.90, 3648.03, 3.94, 0.00, null, - -3.94, 971.30); - checkInst(model, 125, 125, LocalDate.of(2019, 5, 6), 77, 123, 50.00, 50.00, null, 0.87697999, 43.85, 3601.92, 3.90, 0.00, null, - -3.90, 971.30); - checkInst(model, 126, 126, LocalDate.of(2019, 5, 7), 76, 124, 50.00, 50.00, null, 0.87604453, 43.80, 3555.77, 3.85, 0.00, null, - -3.85, 971.30); - checkInst(model, 127, 127, LocalDate.of(2019, 5, 8), 75, 125, 50.00, 50.00, null, 0.87511008, 43.76, 3509.56, 3.80, 0.00, null, - -3.80, 971.30); - checkInst(model, 128, 128, LocalDate.of(2019, 5, 9), 74, 126, 50.00, 50.00, null, 0.87417662, 43.71, 3463.31, 3.75, 0.00, null, - -3.75, 971.30); - checkInst(model, 129, 129, LocalDate.of(2019, 5, 10), 73, 127, 50.00, 50.00, null, 0.87324416, 43.66, 3417.01, 3.70, 0.00, null, - -3.70, 971.30); - checkInst(model, 130, 130, LocalDate.of(2019, 5, 11), 72, 128, 50.00, 50.00, null, 0.87231269, 43.62, 3370.66, 3.65, 0.00, null, - -3.65, 971.30); - checkInst(model, 131, 131, LocalDate.of(2019, 5, 12), 71, 129, 50.00, 50.00, null, 0.87138221, 43.57, 3324.26, 3.60, 0.00, null, - -3.60, 971.30); - checkInst(model, 132, 132, LocalDate.of(2019, 5, 13), 70, 130, 50.00, 50.00, null, 0.87045273, 43.52, 3277.81, 3.55, 0.00, null, - -3.55, 971.30); - checkInst(model, 133, 133, LocalDate.of(2019, 5, 14), 69, 131, 50.00, 50.00, null, 0.86952424, 43.48, 3231.31, 3.50, 0.00, null, - -3.50, 971.30); - checkInst(model, 134, 134, LocalDate.of(2019, 5, 15), 68, 132, 50.00, 50.00, null, 0.86859674, 43.43, 3184.76, 3.45, 0.00, null, - -3.45, 971.30); - checkInst(model, 135, 135, LocalDate.of(2019, 5, 16), 67, 133, 50.00, 50.00, null, 0.86767023, 43.38, 3138.16, 3.40, 0.00, null, - -3.40, 971.30); - checkInst(model, 136, 136, LocalDate.of(2019, 5, 17), 66, 134, 50.00, 50.00, null, 0.86674471, 43.34, 3091.51, 3.35, 0.00, null, - -3.35, 971.30); - checkInst(model, 137, 137, LocalDate.of(2019, 5, 18), 65, 135, 50.00, 50.00, null, 0.86582017, 43.29, 3044.81, 3.30, 0.00, null, - -3.30, 971.30); - checkInst(model, 138, 138, LocalDate.of(2019, 5, 19), 64, 136, 50.00, 50.00, null, 0.86489662, 43.24, 2998.06, 3.25, 0.00, null, - -3.25, 971.30); - checkInst(model, 139, 139, LocalDate.of(2019, 5, 20), 63, 137, 50.00, 50.00, null, 0.86397406, 43.20, 2951.26, 3.20, 0.00, null, - -3.20, 971.30); - checkInst(model, 140, 140, LocalDate.of(2019, 5, 21), 62, 138, 50.00, 50.00, null, 0.86305248, 43.15, 2904.42, 3.15, 0.00, null, - -3.15, 971.30); - checkInst(model, 141, 141, LocalDate.of(2019, 5, 22), 61, 139, 50.00, 50.00, null, 0.86213188, 43.11, 2857.52, 3.10, 0.00, null, - -3.10, 971.30); - checkInst(model, 142, 142, LocalDate.of(2019, 5, 23), 60, 140, 50.00, 50.00, null, 0.86121227, 43.06, 2810.57, 3.05, 0.00, null, - -3.05, 971.30); - checkInst(model, 143, 143, LocalDate.of(2019, 5, 24), 59, 141, 50.00, 50.00, null, 0.86029363, 43.01, 2763.57, 3.00, 0.00, null, - -3.00, 971.30); - checkInst(model, 144, 144, LocalDate.of(2019, 5, 25), 58, 142, 50.00, 50.00, null, 0.85937598, 42.97, 2716.52, 2.95, 0.00, null, - -2.95, 971.30); - checkInst(model, 145, 145, LocalDate.of(2019, 5, 26), 57, 143, 50.00, 50.00, null, 0.85845930, 42.92, 2669.42, 2.90, 0.00, null, - -2.90, 971.30); - checkInst(model, 146, 146, LocalDate.of(2019, 5, 27), 56, 144, 50.00, 50.00, null, 0.85754361, 42.88, 2622.27, 2.85, 0.00, null, - -2.85, 971.30); - checkInst(model, 147, 147, LocalDate.of(2019, 5, 28), 55, 145, 50.00, 50.00, null, 0.85662889, 42.83, 2575.07, 2.80, 0.00, null, - -2.80, 971.30); - checkInst(model, 148, 148, LocalDate.of(2019, 5, 29), 54, 146, 50.00, 50.00, null, 0.85571514, 42.79, 2527.82, 2.75, 0.00, null, - -2.75, 971.30); - checkInst(model, 149, 149, LocalDate.of(2019, 5, 30), 53, 147, 50.00, 50.00, null, 0.85480237, 42.74, 2480.52, 2.70, 0.00, null, - -2.70, 971.30); - checkInst(model, 150, 150, LocalDate.of(2019, 5, 31), 52, 148, 50.00, 50.00, null, 0.85389057, 42.69, 2433.17, 2.65, 0.00, null, - -2.65, 971.30); - checkInst(model, 151, 151, LocalDate.of(2019, 6, 1), 51, 149, 50.00, 50.00, null, 0.85297975, 42.65, 2385.77, 2.60, 0.00, null, - -2.60, 971.30); - checkInst(model, 152, 152, LocalDate.of(2019, 6, 2), 50, 150, 50.00, 50.00, null, 0.85206990, 42.60, 2338.31, 2.55, 0.00, null, - -2.55, 971.30); - checkInst(model, 153, 153, LocalDate.of(2019, 6, 3), 49, 151, 50.00, 50.00, null, 0.85116101, 42.56, 2290.81, 2.50, 0.00, null, - -2.50, 971.30); - checkInst(model, 154, 154, LocalDate.of(2019, 6, 4), 48, 152, 50.00, 50.00, null, 0.85025310, 42.51, 2243.26, 2.45, 0.00, null, - -2.45, 971.30); - checkInst(model, 155, 155, LocalDate.of(2019, 6, 5), 47, 153, 50.00, 50.00, null, 0.84934616, 42.47, 2195.65, 2.40, 0.00, null, - -2.40, 971.30); - checkInst(model, 156, 156, LocalDate.of(2019, 6, 6), 46, 154, 50.00, 50.00, null, 0.84844018, 42.42, 2148.00, 2.34, 0.00, null, - -2.34, 971.30); - checkInst(model, 157, 157, LocalDate.of(2019, 6, 7), 45, 155, 50.00, 50.00, null, 0.84753517, 42.38, 2100.29, 2.29, 0.00, null, - -2.29, 971.30); - checkInst(model, 158, 158, LocalDate.of(2019, 6, 8), 44, 156, 50.00, 50.00, null, 0.84663113, 42.33, 2052.53, 2.24, 0.00, null, - -2.24, 971.30); - checkInst(model, 159, 159, LocalDate.of(2019, 6, 9), 43, 157, 50.00, 50.00, null, 0.84572805, 42.29, 2004.73, 2.19, 0.00, null, - -2.19, 971.30); - checkInst(model, 160, 160, LocalDate.of(2019, 6, 10), 42, 158, 50.00, 50.00, null, 0.84482593, 42.24, 1956.87, 2.14, 0.00, null, - -2.14, 971.30); - checkInst(model, 161, 161, LocalDate.of(2019, 6, 11), 41, 159, 50.00, 50.00, null, 0.84392477, 42.20, 1908.96, 2.09, 0.00, null, - -2.09, 971.30); - checkInst(model, 162, 162, LocalDate.of(2019, 6, 12), 40, 160, 50.00, 50.00, null, 0.84302458, 42.15, 1860.99, 2.04, 0.00, null, - -2.04, 971.30); - checkInst(model, 163, 163, LocalDate.of(2019, 6, 13), 39, 161, 50.00, 50.00, null, 0.84212535, 42.11, 1812.98, 1.99, 0.00, null, - -1.99, 971.30); - checkInst(model, 164, 164, LocalDate.of(2019, 6, 14), 38, 162, 50.00, 50.00, null, 0.84122707, 42.06, 1764.92, 1.94, 0.00, null, - -1.94, 971.30); - checkInst(model, 165, 165, LocalDate.of(2019, 6, 15), 37, 163, 50.00, 50.00, null, 0.84032975, 42.02, 1716.80, 1.88, 0.00, null, - -1.88, 971.30); - checkInst(model, 166, 166, LocalDate.of(2019, 6, 16), 36, 164, 50.00, 50.00, null, 0.83943340, 41.97, 1668.64, 1.83, 0.00, null, - -1.83, 971.30); - checkInst(model, 167, 167, LocalDate.of(2019, 6, 17), 35, 165, 50.00, 50.00, null, 0.83853799, 41.93, 1620.42, 1.78, 0.00, null, - -1.78, 971.30); - checkInst(model, 168, 168, LocalDate.of(2019, 6, 18), 34, 166, 50.00, 50.00, null, 0.83764354, 41.88, 1572.15, 1.73, 0.00, null, - -1.73, 971.30); - checkInst(model, 169, 169, LocalDate.of(2019, 6, 19), 33, 167, 50.00, 50.00, null, 0.83675005, 41.84, 1523.83, 1.68, 0.00, null, - -1.68, 971.30); - checkInst(model, 170, 170, LocalDate.of(2019, 6, 20), 32, 168, 50.00, 50.00, null, 0.83585751, 41.79, 1475.45, 1.63, 0.00, null, - -1.63, 971.30); - checkInst(model, 171, 171, LocalDate.of(2019, 6, 21), 31, 169, 50.00, 50.00, null, 0.83496592, 41.75, 1427.03, 1.58, 0.00, null, - -1.58, 971.30); - checkInst(model, 172, 172, LocalDate.of(2019, 6, 22), 30, 170, 50.00, 50.00, null, 0.83407528, 41.70, 1378.55, 1.52, 0.00, null, - -1.52, 971.30); - checkInst(model, 173, 173, LocalDate.of(2019, 6, 23), 29, 171, 50.00, 50.00, null, 0.83318560, 41.66, 1330.02, 1.47, 0.00, null, - -1.47, 971.30); - checkInst(model, 174, 174, LocalDate.of(2019, 6, 24), 28, 172, 50.00, 50.00, null, 0.83229686, 41.61, 1281.45, 1.42, 0.00, null, - -1.42, 971.30); - checkInst(model, 175, 175, LocalDate.of(2019, 6, 25), 27, 173, 50.00, 50.00, null, 0.83140907, 41.57, 1232.81, 1.37, 0.00, null, - -1.37, 971.30); - checkInst(model, 176, 176, LocalDate.of(2019, 6, 26), 26, 174, 50.00, 50.00, null, 0.83052222, 41.53, 1184.13, 1.32, 0.00, null, - -1.32, 971.30); - checkInst(model, 177, 177, LocalDate.of(2019, 6, 27), 25, 175, 50.00, 50.00, null, 0.82963633, 41.48, 1135.39, 1.26, 0.00, null, - -1.26, 971.30); - checkInst(model, 178, 178, LocalDate.of(2019, 6, 28), 24, 176, 50.00, 50.00, null, 0.82875137, 41.44, 1086.61, 1.21, 0.00, null, - -1.21, 971.30); - checkInst(model, 179, 179, LocalDate.of(2019, 6, 29), 23, 177, 50.00, 50.00, null, 0.82786736, 41.39, 1037.77, 1.16, 0.00, null, - -1.16, 971.30); - checkInst(model, 180, 180, LocalDate.of(2019, 6, 30), 22, 178, 50.00, 50.00, null, 0.82698430, 41.35, 988.88, 1.11, 0.00, null, - -1.11, 971.30); - checkInst(model, 181, 181, LocalDate.of(2019, 7, 1), 21, 179, 50.00, 50.00, null, 0.82610217, 41.31, 939.93, 1.06, 0.00, null, - -1.06, 971.30); - checkInst(model, 182, 182, LocalDate.of(2019, 7, 2), 20, 180, 50.00, 50.00, null, 0.82522099, 41.26, 890.93, 1.00, 0.00, null, - -1.00, 971.30); - checkInst(model, 183, 183, LocalDate.of(2019, 7, 3), 19, 181, 50.00, 50.00, null, 0.82434075, 41.22, 841.89, 0.95, 0.00, null, - -0.95, 971.30); - checkInst(model, 184, 184, LocalDate.of(2019, 7, 4), 18, 182, 50.00, 50.00, null, 0.82346144, 41.17, 792.79, 0.90, 0.00, null, - -0.90, 971.30); - checkInst(model, 185, 185, LocalDate.of(2019, 7, 5), 17, 183, 50.00, 50.00, null, 0.82258308, 41.13, 743.63, 0.85, 0.00, null, - -0.85, 971.30); - checkInst(model, 186, 186, LocalDate.of(2019, 7, 6), 16, 184, 50.00, 50.00, null, 0.82170565, 41.09, 694.43, 0.79, 0.00, null, - -0.79, 971.30); - checkInst(model, 187, 187, LocalDate.of(2019, 7, 7), 15, 185, 50.00, 50.00, null, 0.82082916, 41.04, 645.17, 0.74, 0.00, null, - -0.74, 971.30); - checkInst(model, 188, 188, LocalDate.of(2019, 7, 8), 14, 186, 50.00, 50.00, null, 0.81995360, 41.00, 595.86, 0.69, 0.00, null, - -0.69, 971.30); - checkInst(model, 189, 189, LocalDate.of(2019, 7, 9), 13, 187, 50.00, 50.00, null, 0.81907897, 40.95, 546.49, 0.64, 0.00, null, - -0.64, 971.30); - checkInst(model, 190, 190, LocalDate.of(2019, 7, 10), 12, 188, 50.00, 50.00, null, 0.81820528, 40.91, 497.08, 0.58, 0.00, null, - -0.58, 971.30); - checkInst(model, 191, 191, LocalDate.of(2019, 7, 11), 11, 189, 50.00, 50.00, null, 0.81733252, 40.87, 447.61, 0.53, 0.00, null, - -0.53, 971.30); - checkInst(model, 192, 192, LocalDate.of(2019, 7, 12), 10, 190, 50.00, 50.00, null, 0.81646069, 40.82, 398.08, 0.48, 0.00, null, - -0.48, 971.30); - checkInst(model, 193, 193, LocalDate.of(2019, 7, 13), 9, 191, 50.00, 50.00, null, 0.81558979, 40.78, 348.51, 0.43, 0.00, null, - -0.43, 971.30); - checkInst(model, 194, 194, LocalDate.of(2019, 7, 14), 8, 192, 50.00, 50.00, null, 0.81471983, 40.74, 298.88, 0.37, 0.00, null, - -0.37, 971.30); - checkInst(model, 195, 195, LocalDate.of(2019, 7, 15), 7, 193, 50.00, 50.00, null, 0.81385078, 40.69, 249.20, 0.32, 0.00, null, - -0.32, 971.30); - checkInst(model, 196, 196, LocalDate.of(2019, 7, 16), 6, 194, 50.00, 50.00, null, 0.81298267, 40.65, 199.47, 0.27, 0.00, null, - -0.27, 971.30); - checkInst(model, 197, 197, LocalDate.of(2019, 7, 17), 5, 195, 50.00, 50.00, null, 0.81211548, 40.61, 149.68, 0.21, 0.00, null, - -0.21, 971.30); - checkInst(model, 198, 198, LocalDate.of(2019, 7, 18), 4, 196, 50.00, 50.00, null, 0.81124922, 40.56, 99.84, 0.16, 0.00, null, -0.16, - 971.30); - checkInst(model, 199, 199, LocalDate.of(2019, 7, 19), 3, 197, 50.00, 50.00, null, 0.81038388, 40.52, 49.95, 0.11, 0.00, null, -0.11, - 971.30); - - assertEquals(200, model.payments().size(), "disbursement + 199 regular (period 200 removed, forecast was 0)"); + assertFalse(model.rateSegments().isEmpty()); + assertTrue(model.effectiveTotalTerm() > 0, "effective total term should be positive"); } @Test - void testLessPayment_term200_originationFee1000_netDisbursement9000_pay40() { + void testApplyRateChange_8daysAfterDisburse() { final ProjectedAmortizationScheduleModel model = generateModel(); - calculator.applyPayment(model, EXPECTED_DISBURSEMENT_DATE.plusDays(1), new BigDecimal("40")); + final LocalDate rateChangeDate = EXPECTED_DISBURSEMENT_DATE.plusDays(8); + model.applyRateChange(new BigDecimal("0.15"), rateChangeDate); - checkInst(model, 0, 0, EXPECTED_DISBURSEMENT_DATE, 201, 0, -9000.00, null, null, 1.00000000, -9000.00, 9000.00, null, null, null, - null, 1000.00); - - checkInst(model, 1, 1, LocalDate.of(2019, 1, 2), 200, 0, 50.00, 50.00, 40.00, 1.00000000, 40.00, 8959.61, 9.61, 7.69, 7.69, -1.92, - 992.31); - - checkInst(model, 2, 2, LocalDate.of(2019, 1, 3), 199, 1, 50.00, 50.00, null, 0.99893332, 49.95, 8919.18, 9.57, 0.00, null, -9.57, - 992.31); - checkInst(model, 3, 3, LocalDate.of(2019, 1, 4), 198, 2, 50.00, 50.00, null, 0.99786779, 49.89, 8878.70, 9.52, 0.00, null, -9.52, - 992.31); - checkInst(model, 4, 4, LocalDate.of(2019, 1, 5), 197, 3, 50.00, 50.00, null, 0.99680339, 49.84, 8838.18, 9.48, 0.00, null, -9.48, - 992.31); - checkInst(model, 5, 5, LocalDate.of(2019, 1, 6), 196, 4, 50.00, 50.00, null, 0.99574012, 49.79, 8797.62, 9.44, 0.00, null, -9.44, - 992.31); - checkInst(model, 6, 6, LocalDate.of(2019, 1, 7), 195, 5, 50.00, 50.00, null, 0.99467799, 49.73, 8757.01, 9.39, 0.00, null, -9.39, - 992.31); - checkInst(model, 7, 7, LocalDate.of(2019, 1, 8), 194, 6, 50.00, 50.00, null, 0.99361699, 49.68, 8716.36, 9.35, 0.00, null, -9.35, - 992.31); - checkInst(model, 8, 8, LocalDate.of(2019, 1, 9), 193, 7, 50.00, 50.00, null, 0.99255712, 49.63, 8675.67, 9.31, 0.00, null, -9.31, - 992.31); - checkInst(model, 9, 9, LocalDate.of(2019, 1, 10), 192, 8, 50.00, 50.00, null, 0.99149839, 49.57, 8634.94, 9.26, 0.00, null, -9.26, - 992.31); - checkInst(model, 10, 10, LocalDate.of(2019, 1, 11), 191, 9, 50.00, 50.00, null, 0.99044078, 49.52, 8594.16, 9.22, 0.00, null, -9.22, - 992.31); - checkInst(model, 11, 11, LocalDate.of(2019, 1, 12), 190, 10, 50.00, 50.00, null, 0.98938430, 49.47, 8553.33, 9.18, 0.00, null, - -9.18, 992.31); - checkInst(model, 12, 12, LocalDate.of(2019, 1, 13), 189, 11, 50.00, 50.00, null, 0.98832895, 49.42, 8512.47, 9.13, 0.00, null, - -9.13, 992.31); - checkInst(model, 13, 13, LocalDate.of(2019, 1, 14), 188, 12, 50.00, 50.00, null, 0.98727472, 49.36, 8471.56, 9.09, 0.00, null, - -9.09, 992.31); - checkInst(model, 14, 14, LocalDate.of(2019, 1, 15), 187, 13, 50.00, 50.00, null, 0.98622162, 49.31, 8430.60, 9.05, 0.00, null, - -9.05, 992.31); - checkInst(model, 15, 15, LocalDate.of(2019, 1, 16), 186, 14, 50.00, 50.00, null, 0.98516964, 49.26, 8389.61, 9.00, 0.00, null, - -9.00, 992.31); - checkInst(model, 16, 16, LocalDate.of(2019, 1, 17), 185, 15, 50.00, 50.00, null, 0.98411879, 49.21, 8348.56, 8.96, 0.00, null, - -8.96, 992.31); - checkInst(model, 17, 17, LocalDate.of(2019, 1, 18), 184, 16, 50.00, 50.00, null, 0.98306905, 49.15, 8307.48, 8.91, 0.00, null, - -8.91, 992.31); - checkInst(model, 18, 18, LocalDate.of(2019, 1, 19), 183, 17, 50.00, 50.00, null, 0.98202044, 49.10, 8266.35, 8.87, 0.00, null, - -8.87, 992.31); - checkInst(model, 19, 19, LocalDate.of(2019, 1, 20), 182, 18, 50.00, 50.00, null, 0.98097294, 49.05, 8225.18, 8.83, 0.00, null, - -8.83, 992.31); - checkInst(model, 20, 20, LocalDate.of(2019, 1, 21), 181, 19, 50.00, 50.00, null, 0.97992656, 49.00, 8183.96, 8.78, 0.00, null, - -8.78, 992.31); - checkInst(model, 21, 21, LocalDate.of(2019, 1, 22), 180, 20, 50.00, 50.00, null, 0.97888129, 48.94, 8142.70, 8.74, 0.00, null, - -8.74, 992.31); - checkInst(model, 22, 22, LocalDate.of(2019, 1, 23), 179, 21, 50.00, 50.00, null, 0.97783715, 48.89, 8101.39, 8.69, 0.00, null, - -8.69, 992.31); - checkInst(model, 23, 23, LocalDate.of(2019, 1, 24), 178, 22, 50.00, 50.00, null, 0.97679411, 48.84, 8060.04, 8.65, 0.00, null, - -8.65, 992.31); - checkInst(model, 24, 24, LocalDate.of(2019, 1, 25), 177, 23, 50.00, 50.00, null, 0.97575219, 48.79, 8018.65, 8.61, 0.00, null, - -8.61, 992.31); - checkInst(model, 25, 25, LocalDate.of(2019, 1, 26), 176, 24, 50.00, 50.00, null, 0.97471138, 48.74, 7977.21, 8.56, 0.00, null, - -8.56, 992.31); - checkInst(model, 26, 26, LocalDate.of(2019, 1, 27), 175, 25, 50.00, 50.00, null, 0.97367168, 48.68, 7935.73, 8.52, 0.00, null, - -8.52, 992.31); - checkInst(model, 27, 27, LocalDate.of(2019, 1, 28), 174, 26, 50.00, 50.00, null, 0.97263309, 48.63, 7894.21, 8.47, 0.00, null, - -8.47, 992.31); - checkInst(model, 28, 28, LocalDate.of(2019, 1, 29), 173, 27, 50.00, 50.00, null, 0.97159560, 48.58, 7852.63, 8.43, 0.00, null, - -8.43, 992.31); - checkInst(model, 29, 29, LocalDate.of(2019, 1, 30), 172, 28, 50.00, 50.00, null, 0.97055922, 48.53, 7811.02, 8.39, 0.00, null, - -8.39, 992.31); - checkInst(model, 30, 30, LocalDate.of(2019, 1, 31), 171, 29, 50.00, 50.00, null, 0.96952395, 48.48, 7769.36, 8.34, 0.00, null, - -8.34, 992.31); - checkInst(model, 31, 31, LocalDate.of(2019, 2, 1), 170, 30, 50.00, 50.00, null, 0.96848979, 48.42, 7727.66, 8.30, 0.00, null, -8.30, - 992.31); - checkInst(model, 32, 32, LocalDate.of(2019, 2, 2), 169, 31, 50.00, 50.00, null, 0.96745672, 48.37, 7685.91, 8.25, 0.00, null, -8.25, - 992.31); - checkInst(model, 33, 33, LocalDate.of(2019, 2, 3), 168, 32, 50.00, 50.00, null, 0.96642476, 48.32, 7644.12, 8.21, 0.00, null, -8.21, - 992.31); - checkInst(model, 34, 34, LocalDate.of(2019, 2, 4), 167, 33, 50.00, 50.00, null, 0.96539390, 48.27, 7602.28, 8.16, 0.00, null, -8.16, - 992.31); - checkInst(model, 35, 35, LocalDate.of(2019, 2, 5), 166, 34, 50.00, 50.00, null, 0.96436413, 48.22, 7560.40, 8.12, 0.00, null, -8.12, - 992.31); - checkInst(model, 36, 36, LocalDate.of(2019, 2, 6), 165, 35, 50.00, 50.00, null, 0.96333547, 48.17, 7518.47, 8.07, 0.00, null, -8.07, - 992.31); - checkInst(model, 37, 37, LocalDate.of(2019, 2, 7), 164, 36, 50.00, 50.00, null, 0.96230790, 48.12, 7476.50, 8.03, 0.00, null, -8.03, - 992.31); - checkInst(model, 38, 38, LocalDate.of(2019, 2, 8), 163, 37, 50.00, 50.00, null, 0.96128143, 48.06, 7434.48, 7.98, 0.00, null, -7.98, - 992.31); - checkInst(model, 39, 39, LocalDate.of(2019, 2, 9), 162, 38, 50.00, 50.00, null, 0.96025606, 48.01, 7392.42, 7.94, 0.00, null, -7.94, - 992.31); - checkInst(model, 40, 40, LocalDate.of(2019, 2, 10), 161, 39, 50.00, 50.00, null, 0.95923178, 47.96, 7350.31, 7.89, 0.00, null, - -7.89, 992.31); - checkInst(model, 41, 41, LocalDate.of(2019, 2, 11), 160, 40, 50.00, 50.00, null, 0.95820859, 47.91, 7308.16, 7.85, 0.00, null, - -7.85, 992.31); - checkInst(model, 42, 42, LocalDate.of(2019, 2, 12), 159, 41, 50.00, 50.00, null, 0.95718649, 47.86, 7265.97, 7.80, 0.00, null, - -7.80, 992.31); - checkInst(model, 43, 43, LocalDate.of(2019, 2, 13), 158, 42, 50.00, 50.00, null, 0.95616548, 47.81, 7223.72, 7.76, 0.00, null, - -7.76, 992.31); - checkInst(model, 44, 44, LocalDate.of(2019, 2, 14), 157, 43, 50.00, 50.00, null, 0.95514557, 47.76, 7181.44, 7.71, 0.00, null, - -7.71, 992.31); - checkInst(model, 45, 45, LocalDate.of(2019, 2, 15), 156, 44, 50.00, 50.00, null, 0.95412674, 47.71, 7139.11, 7.67, 0.00, null, - -7.67, 992.31); - checkInst(model, 46, 46, LocalDate.of(2019, 2, 16), 155, 45, 50.00, 50.00, null, 0.95310899, 47.66, 7096.73, 7.62, 0.00, null, - -7.62, 992.31); - checkInst(model, 47, 47, LocalDate.of(2019, 2, 17), 154, 46, 50.00, 50.00, null, 0.95209233, 47.60, 7054.31, 7.58, 0.00, null, - -7.58, 992.31); - checkInst(model, 48, 48, LocalDate.of(2019, 2, 18), 153, 47, 50.00, 50.00, null, 0.95107676, 47.55, 7011.84, 7.53, 0.00, null, - -7.53, 992.31); - checkInst(model, 49, 49, LocalDate.of(2019, 2, 19), 152, 48, 50.00, 50.00, null, 0.95006227, 47.50, 6969.33, 7.49, 0.00, null, - -7.49, 992.31); - checkInst(model, 50, 50, LocalDate.of(2019, 2, 20), 151, 49, 50.00, 50.00, null, 0.94904886, 47.45, 6926.77, 7.44, 0.00, null, - -7.44, 992.31); - checkInst(model, 51, 51, LocalDate.of(2019, 2, 21), 150, 50, 50.00, 50.00, null, 0.94803653, 47.40, 6884.17, 7.40, 0.00, null, - -7.40, 992.31); - checkInst(model, 52, 52, LocalDate.of(2019, 2, 22), 149, 51, 50.00, 50.00, null, 0.94702529, 47.35, 6841.52, 7.35, 0.00, null, - -7.35, 992.31); - checkInst(model, 53, 53, LocalDate.of(2019, 2, 23), 148, 52, 50.00, 50.00, null, 0.94601512, 47.30, 6798.82, 7.31, 0.00, null, - -7.31, 992.31); - checkInst(model, 54, 54, LocalDate.of(2019, 2, 24), 147, 53, 50.00, 50.00, null, 0.94500603, 47.25, 6756.08, 7.26, 0.00, null, - -7.26, 992.31); - checkInst(model, 55, 55, LocalDate.of(2019, 2, 25), 146, 54, 50.00, 50.00, null, 0.94399801, 47.20, 6713.30, 7.21, 0.00, null, - -7.21, 992.31); - checkInst(model, 56, 56, LocalDate.of(2019, 2, 26), 145, 55, 50.00, 50.00, null, 0.94299107, 47.15, 6670.47, 7.17, 0.00, null, - -7.17, 992.31); - checkInst(model, 57, 57, LocalDate.of(2019, 2, 27), 144, 56, 50.00, 50.00, null, 0.94198521, 47.10, 6627.59, 7.12, 0.00, null, - -7.12, 992.31); - checkInst(model, 58, 58, LocalDate.of(2019, 2, 28), 143, 57, 50.00, 50.00, null, 0.94098042, 47.05, 6584.67, 7.08, 0.00, null, - -7.08, 992.31); - checkInst(model, 59, 59, LocalDate.of(2019, 3, 1), 142, 58, 50.00, 50.00, null, 0.93997669, 47.00, 6541.70, 7.03, 0.00, null, -7.03, - 992.31); - checkInst(model, 60, 60, LocalDate.of(2019, 3, 2), 141, 59, 50.00, 50.00, null, 0.93897404, 46.95, 6498.68, 6.99, 0.00, null, -6.99, - 992.31); - checkInst(model, 61, 61, LocalDate.of(2019, 3, 3), 140, 60, 50.00, 50.00, null, 0.93797246, 46.90, 6455.62, 6.94, 0.00, null, -6.94, - 992.31); - checkInst(model, 62, 62, LocalDate.of(2019, 3, 4), 139, 61, 50.00, 50.00, null, 0.93697195, 46.85, 6412.51, 6.89, 0.00, null, -6.89, - 992.31); - checkInst(model, 63, 63, LocalDate.of(2019, 3, 5), 138, 62, 50.00, 50.00, null, 0.93597251, 46.80, 6369.36, 6.85, 0.00, null, -6.85, - 992.31); - checkInst(model, 64, 64, LocalDate.of(2019, 3, 6), 137, 63, 50.00, 50.00, null, 0.93497413, 46.75, 6326.16, 6.80, 0.00, null, -6.80, - 992.31); - checkInst(model, 65, 65, LocalDate.of(2019, 3, 7), 136, 64, 50.00, 50.00, null, 0.93397681, 46.70, 6282.92, 6.76, 0.00, null, -6.76, - 992.31); - checkInst(model, 66, 66, LocalDate.of(2019, 3, 8), 135, 65, 50.00, 50.00, null, 0.93298056, 46.65, 6239.63, 6.71, 0.00, null, -6.71, - 992.31); - checkInst(model, 67, 67, LocalDate.of(2019, 3, 9), 134, 66, 50.00, 50.00, null, 0.93198538, 46.60, 6196.29, 6.66, 0.00, null, -6.66, - 992.31); - checkInst(model, 68, 68, LocalDate.of(2019, 3, 10), 133, 67, 50.00, 50.00, null, 0.93099125, 46.55, 6152.91, 6.62, 0.00, null, - -6.62, 992.31); - checkInst(model, 69, 69, LocalDate.of(2019, 3, 11), 132, 68, 50.00, 50.00, null, 0.92999818, 46.50, 6109.48, 6.57, 0.00, null, - -6.57, 992.31); - checkInst(model, 70, 70, LocalDate.of(2019, 3, 12), 131, 69, 50.00, 50.00, null, 0.92900618, 46.45, 6066.00, 6.52, 0.00, null, - -6.52, 992.31); - checkInst(model, 71, 71, LocalDate.of(2019, 3, 13), 130, 70, 50.00, 50.00, null, 0.92801523, 46.40, 6022.48, 6.48, 0.00, null, - -6.48, 992.31); - checkInst(model, 72, 72, LocalDate.of(2019, 3, 14), 129, 71, 50.00, 50.00, null, 0.92702534, 46.35, 5978.91, 6.43, 0.00, null, - -6.43, 992.31); - checkInst(model, 73, 73, LocalDate.of(2019, 3, 15), 128, 72, 50.00, 50.00, null, 0.92603650, 46.30, 5935.29, 6.38, 0.00, null, - -6.38, 992.31); - checkInst(model, 74, 74, LocalDate.of(2019, 3, 16), 127, 73, 50.00, 50.00, null, 0.92504872, 46.25, 5891.63, 6.34, 0.00, null, - -6.34, 992.31); - checkInst(model, 75, 75, LocalDate.of(2019, 3, 17), 126, 74, 50.00, 50.00, null, 0.92406200, 46.20, 5847.92, 6.29, 0.00, null, - -6.29, 992.31); - checkInst(model, 76, 76, LocalDate.of(2019, 3, 18), 125, 75, 50.00, 50.00, null, 0.92307632, 46.15, 5804.17, 6.24, 0.00, null, - -6.24, 992.31); - checkInst(model, 77, 77, LocalDate.of(2019, 3, 19), 124, 76, 50.00, 50.00, null, 0.92209170, 46.10, 5760.36, 6.20, 0.00, null, - -6.20, 992.31); - checkInst(model, 78, 78, LocalDate.of(2019, 3, 20), 123, 77, 50.00, 50.00, null, 0.92110813, 46.06, 5716.52, 6.15, 0.00, null, - -6.15, 992.31); - checkInst(model, 79, 79, LocalDate.of(2019, 3, 21), 122, 78, 50.00, 50.00, null, 0.92012560, 46.01, 5672.62, 6.10, 0.00, null, - -6.10, 992.31); - checkInst(model, 80, 80, LocalDate.of(2019, 3, 22), 121, 79, 50.00, 50.00, null, 0.91914413, 45.96, 5628.68, 6.06, 0.00, null, - -6.06, 992.31); - checkInst(model, 81, 81, LocalDate.of(2019, 3, 23), 120, 80, 50.00, 50.00, null, 0.91816370, 45.91, 5584.69, 6.01, 0.00, null, - -6.01, 992.31); - checkInst(model, 82, 82, LocalDate.of(2019, 3, 24), 119, 81, 50.00, 50.00, null, 0.91718432, 45.86, 5540.65, 5.96, 0.00, null, - -5.96, 992.31); - checkInst(model, 83, 83, LocalDate.of(2019, 3, 25), 118, 82, 50.00, 50.00, null, 0.91620598, 45.81, 5496.57, 5.92, 0.00, null, - -5.92, 992.31); - checkInst(model, 84, 84, LocalDate.of(2019, 3, 26), 117, 83, 50.00, 50.00, null, 0.91522868, 45.76, 5452.44, 5.87, 0.00, null, - -5.87, 992.31); - checkInst(model, 85, 85, LocalDate.of(2019, 3, 27), 116, 84, 50.00, 50.00, null, 0.91425243, 45.71, 5408.26, 5.82, 0.00, null, - -5.82, 992.31); - checkInst(model, 86, 86, LocalDate.of(2019, 3, 28), 115, 85, 50.00, 50.00, null, 0.91327722, 45.66, 5364.03, 5.78, 0.00, null, - -5.78, 992.31); - checkInst(model, 87, 87, LocalDate.of(2019, 3, 29), 114, 86, 50.00, 50.00, null, 0.91230305, 45.62, 5319.76, 5.73, 0.00, null, - -5.73, 992.31); - checkInst(model, 88, 88, LocalDate.of(2019, 3, 30), 113, 87, 50.00, 50.00, null, 0.91132992, 45.57, 5275.44, 5.68, 0.00, null, - -5.68, 992.31); - checkInst(model, 89, 89, LocalDate.of(2019, 3, 31), 112, 88, 50.00, 50.00, null, 0.91035783, 45.52, 5231.08, 5.63, 0.00, null, - -5.63, 992.31); - checkInst(model, 90, 90, LocalDate.of(2019, 4, 1), 111, 89, 50.00, 50.00, null, 0.90938677, 45.47, 5186.66, 5.59, 0.00, null, -5.59, - 992.31); - checkInst(model, 91, 91, LocalDate.of(2019, 4, 2), 110, 90, 50.00, 50.00, null, 0.90841675, 45.42, 5142.20, 5.54, 0.00, null, -5.54, - 992.31); - checkInst(model, 92, 92, LocalDate.of(2019, 4, 3), 109, 91, 50.00, 50.00, null, 0.90744776, 45.37, 5097.69, 5.49, 0.00, null, -5.49, - 992.31); - checkInst(model, 93, 93, LocalDate.of(2019, 4, 4), 108, 92, 50.00, 50.00, null, 0.90647981, 45.32, 5053.13, 5.44, 0.00, null, -5.44, - 992.31); - checkInst(model, 94, 94, LocalDate.of(2019, 4, 5), 107, 93, 50.00, 50.00, null, 0.90551289, 45.28, 5008.53, 5.40, 0.00, null, -5.40, - 992.31); - checkInst(model, 95, 95, LocalDate.of(2019, 4, 6), 106, 94, 50.00, 50.00, null, 0.90454700, 45.23, 4963.88, 5.35, 0.00, null, -5.35, - 992.31); - checkInst(model, 96, 96, LocalDate.of(2019, 4, 7), 105, 95, 50.00, 50.00, null, 0.90358215, 45.18, 4919.18, 5.30, 0.00, null, -5.30, - 992.31); - checkInst(model, 97, 97, LocalDate.of(2019, 4, 8), 104, 96, 50.00, 50.00, null, 0.90261832, 45.13, 4874.43, 5.25, 0.00, null, -5.25, - 992.31); - checkInst(model, 98, 98, LocalDate.of(2019, 4, 9), 103, 97, 50.00, 50.00, null, 0.90165552, 45.08, 4829.64, 5.20, 0.00, null, -5.20, - 992.31); - checkInst(model, 99, 99, LocalDate.of(2019, 4, 10), 102, 98, 50.00, 50.00, null, 0.90069374, 45.03, 4784.79, 5.16, 0.00, null, - -5.16, 992.31); - checkInst(model, 100, 100, LocalDate.of(2019, 4, 11), 101, 99, 50.00, 50.00, null, 0.89973299, 44.99, 4739.90, 5.11, 0.00, null, - -5.11, 992.31); - checkInst(model, 101, 101, LocalDate.of(2019, 4, 12), 100, 100, 50.00, 50.00, null, 0.89877327, 44.94, 4694.96, 5.06, 0.00, null, - -5.06, 992.31); - checkInst(model, 102, 102, LocalDate.of(2019, 4, 13), 99, 101, 50.00, 50.00, null, 0.89781457, 44.89, 4649.98, 5.01, 0.00, null, - -5.01, 992.31); - checkInst(model, 103, 103, LocalDate.of(2019, 4, 14), 98, 102, 50.00, 50.00, null, 0.89685689, 44.84, 4604.94, 4.97, 0.00, null, - -4.97, 992.31); - checkInst(model, 104, 104, LocalDate.of(2019, 4, 15), 97, 103, 50.00, 50.00, null, 0.89590024, 44.80, 4559.86, 4.92, 0.00, null, - -4.92, 992.31); - checkInst(model, 105, 105, LocalDate.of(2019, 4, 16), 96, 104, 50.00, 50.00, null, 0.89494460, 44.75, 4514.73, 4.87, 0.00, null, - -4.87, 992.31); - checkInst(model, 106, 106, LocalDate.of(2019, 4, 17), 95, 105, 50.00, 50.00, null, 0.89398999, 44.70, 4469.55, 4.82, 0.00, null, - -4.82, 992.31); - checkInst(model, 107, 107, LocalDate.of(2019, 4, 18), 94, 106, 50.00, 50.00, null, 0.89303639, 44.65, 4424.32, 4.77, 0.00, null, - -4.77, 992.31); - checkInst(model, 108, 108, LocalDate.of(2019, 4, 19), 93, 107, 50.00, 50.00, null, 0.89208381, 44.60, 4379.05, 4.72, 0.00, null, - -4.72, 992.31); - checkInst(model, 109, 109, LocalDate.of(2019, 4, 20), 92, 108, 50.00, 50.00, null, 0.89113225, 44.56, 4333.72, 4.68, 0.00, null, - -4.68, 992.31); - checkInst(model, 110, 110, LocalDate.of(2019, 4, 21), 91, 109, 50.00, 50.00, null, 0.89018170, 44.51, 4288.35, 4.63, 0.00, null, - -4.63, 992.31); - checkInst(model, 111, 111, LocalDate.of(2019, 4, 22), 90, 110, 50.00, 50.00, null, 0.88923216, 44.46, 4242.93, 4.58, 0.00, null, - -4.58, 992.31); - checkInst(model, 112, 112, LocalDate.of(2019, 4, 23), 89, 111, 50.00, 50.00, null, 0.88828364, 44.41, 4197.46, 4.53, 0.00, null, - -4.53, 992.31); - checkInst(model, 113, 113, LocalDate.of(2019, 4, 24), 88, 112, 50.00, 50.00, null, 0.88733613, 44.37, 4151.94, 4.48, 0.00, null, - -4.48, 992.31); - checkInst(model, 114, 114, LocalDate.of(2019, 4, 25), 87, 113, 50.00, 50.00, null, 0.88638963, 44.32, 4106.38, 4.43, 0.00, null, - -4.43, 992.31); - checkInst(model, 115, 115, LocalDate.of(2019, 4, 26), 86, 114, 50.00, 50.00, null, 0.88544414, 44.27, 4060.76, 4.38, 0.00, null, - -4.38, 992.31); - checkInst(model, 116, 116, LocalDate.of(2019, 4, 27), 85, 115, 50.00, 50.00, null, 0.88449966, 44.22, 4015.10, 4.34, 0.00, null, - -4.34, 992.31); - checkInst(model, 117, 117, LocalDate.of(2019, 4, 28), 84, 116, 50.00, 50.00, null, 0.88355619, 44.18, 3969.38, 4.29, 0.00, null, - -4.29, 992.31); - checkInst(model, 118, 118, LocalDate.of(2019, 4, 29), 83, 117, 50.00, 50.00, null, 0.88261372, 44.13, 3923.62, 4.24, 0.00, null, - -4.24, 992.31); - checkInst(model, 119, 119, LocalDate.of(2019, 4, 30), 82, 118, 50.00, 50.00, null, 0.88167226, 44.08, 3877.81, 4.19, 0.00, null, - -4.19, 992.31); - checkInst(model, 120, 120, LocalDate.of(2019, 5, 1), 81, 119, 50.00, 50.00, null, 0.88073180, 44.04, 3831.95, 4.14, 0.00, null, - -4.14, 992.31); - checkInst(model, 121, 121, LocalDate.of(2019, 5, 2), 80, 120, 50.00, 50.00, null, 0.87979234, 43.99, 3786.04, 4.09, 0.00, null, - -4.09, 992.31); - checkInst(model, 122, 122, LocalDate.of(2019, 5, 3), 79, 121, 50.00, 50.00, null, 0.87885389, 43.94, 3740.09, 4.04, 0.00, null, - -4.04, 992.31); - checkInst(model, 123, 123, LocalDate.of(2019, 5, 4), 78, 122, 50.00, 50.00, null, 0.87791644, 43.90, 3694.08, 3.99, 0.00, null, - -3.99, 992.31); - checkInst(model, 124, 124, LocalDate.of(2019, 5, 5), 77, 123, 50.00, 50.00, null, 0.87697999, 43.85, 3648.03, 3.94, 0.00, null, - -3.94, 992.31); - checkInst(model, 125, 125, LocalDate.of(2019, 5, 6), 76, 124, 50.00, 50.00, null, 0.87604453, 43.80, 3601.92, 3.90, 0.00, null, - -3.90, 992.31); - checkInst(model, 126, 126, LocalDate.of(2019, 5, 7), 75, 125, 50.00, 50.00, null, 0.87511008, 43.76, 3555.77, 3.85, 0.00, null, - -3.85, 992.31); - checkInst(model, 127, 127, LocalDate.of(2019, 5, 8), 74, 126, 50.00, 50.00, null, 0.87417662, 43.71, 3509.56, 3.80, 0.00, null, - -3.80, 992.31); - checkInst(model, 128, 128, LocalDate.of(2019, 5, 9), 73, 127, 50.00, 50.00, null, 0.87324416, 43.66, 3463.31, 3.75, 0.00, null, - -3.75, 992.31); - checkInst(model, 129, 129, LocalDate.of(2019, 5, 10), 72, 128, 50.00, 50.00, null, 0.87231269, 43.62, 3417.01, 3.70, 0.00, null, - -3.70, 992.31); - checkInst(model, 130, 130, LocalDate.of(2019, 5, 11), 71, 129, 50.00, 50.00, null, 0.87138221, 43.57, 3370.66, 3.65, 0.00, null, - -3.65, 992.31); - checkInst(model, 131, 131, LocalDate.of(2019, 5, 12), 70, 130, 50.00, 50.00, null, 0.87045273, 43.52, 3324.26, 3.60, 0.00, null, - -3.60, 992.31); - checkInst(model, 132, 132, LocalDate.of(2019, 5, 13), 69, 131, 50.00, 50.00, null, 0.86952424, 43.48, 3277.81, 3.55, 0.00, null, - -3.55, 992.31); - checkInst(model, 133, 133, LocalDate.of(2019, 5, 14), 68, 132, 50.00, 50.00, null, 0.86859674, 43.43, 3231.31, 3.50, 0.00, null, - -3.50, 992.31); - checkInst(model, 134, 134, LocalDate.of(2019, 5, 15), 67, 133, 50.00, 50.00, null, 0.86767023, 43.38, 3184.76, 3.45, 0.00, null, - -3.45, 992.31); - checkInst(model, 135, 135, LocalDate.of(2019, 5, 16), 66, 134, 50.00, 50.00, null, 0.86674471, 43.34, 3138.16, 3.40, 0.00, null, - -3.40, 992.31); - checkInst(model, 136, 136, LocalDate.of(2019, 5, 17), 65, 135, 50.00, 50.00, null, 0.86582017, 43.29, 3091.51, 3.35, 0.00, null, - -3.35, 992.31); - checkInst(model, 137, 137, LocalDate.of(2019, 5, 18), 64, 136, 50.00, 50.00, null, 0.86489662, 43.24, 3044.81, 3.30, 0.00, null, - -3.30, 992.31); - checkInst(model, 138, 138, LocalDate.of(2019, 5, 19), 63, 137, 50.00, 50.00, null, 0.86397406, 43.20, 2998.06, 3.25, 0.00, null, - -3.25, 992.31); - checkInst(model, 139, 139, LocalDate.of(2019, 5, 20), 62, 138, 50.00, 50.00, null, 0.86305248, 43.15, 2951.26, 3.20, 0.00, null, - -3.20, 992.31); - checkInst(model, 140, 140, LocalDate.of(2019, 5, 21), 61, 139, 50.00, 50.00, null, 0.86213188, 43.11, 2904.42, 3.15, 0.00, null, - -3.15, 992.31); - checkInst(model, 141, 141, LocalDate.of(2019, 5, 22), 60, 140, 50.00, 50.00, null, 0.86121227, 43.06, 2857.52, 3.10, 0.00, null, - -3.10, 992.31); - checkInst(model, 142, 142, LocalDate.of(2019, 5, 23), 59, 141, 50.00, 50.00, null, 0.86029363, 43.01, 2810.57, 3.05, 0.00, null, - -3.05, 992.31); - checkInst(model, 143, 143, LocalDate.of(2019, 5, 24), 58, 142, 50.00, 50.00, null, 0.85937598, 42.97, 2763.57, 3.00, 0.00, null, - -3.00, 992.31); - checkInst(model, 144, 144, LocalDate.of(2019, 5, 25), 57, 143, 50.00, 50.00, null, 0.85845930, 42.92, 2716.52, 2.95, 0.00, null, - -2.95, 992.31); - checkInst(model, 145, 145, LocalDate.of(2019, 5, 26), 56, 144, 50.00, 50.00, null, 0.85754361, 42.88, 2669.42, 2.90, 0.00, null, - -2.90, 992.31); - checkInst(model, 146, 146, LocalDate.of(2019, 5, 27), 55, 145, 50.00, 50.00, null, 0.85662889, 42.83, 2622.27, 2.85, 0.00, null, - -2.85, 992.31); - checkInst(model, 147, 147, LocalDate.of(2019, 5, 28), 54, 146, 50.00, 50.00, null, 0.85571514, 42.79, 2575.07, 2.80, 0.00, null, - -2.80, 992.31); - checkInst(model, 148, 148, LocalDate.of(2019, 5, 29), 53, 147, 50.00, 50.00, null, 0.85480237, 42.74, 2527.82, 2.75, 0.00, null, - -2.75, 992.31); - checkInst(model, 149, 149, LocalDate.of(2019, 5, 30), 52, 148, 50.00, 50.00, null, 0.85389057, 42.69, 2480.52, 2.70, 0.00, null, - -2.70, 992.31); - checkInst(model, 150, 150, LocalDate.of(2019, 5, 31), 51, 149, 50.00, 50.00, null, 0.85297975, 42.65, 2433.17, 2.65, 0.00, null, - -2.65, 992.31); - checkInst(model, 151, 151, LocalDate.of(2019, 6, 1), 50, 150, 50.00, 50.00, null, 0.85206990, 42.60, 2385.77, 2.60, 0.00, null, - -2.60, 992.31); - checkInst(model, 152, 152, LocalDate.of(2019, 6, 2), 49, 151, 50.00, 50.00, null, 0.85116101, 42.56, 2338.31, 2.55, 0.00, null, - -2.55, 992.31); - checkInst(model, 153, 153, LocalDate.of(2019, 6, 3), 48, 152, 50.00, 50.00, null, 0.85025310, 42.51, 2290.81, 2.50, 0.00, null, - -2.50, 992.31); - checkInst(model, 154, 154, LocalDate.of(2019, 6, 4), 47, 153, 50.00, 50.00, null, 0.84934616, 42.47, 2243.26, 2.45, 0.00, null, - -2.45, 992.31); - checkInst(model, 155, 155, LocalDate.of(2019, 6, 5), 46, 154, 50.00, 50.00, null, 0.84844018, 42.42, 2195.65, 2.40, 0.00, null, - -2.40, 992.31); - checkInst(model, 156, 156, LocalDate.of(2019, 6, 6), 45, 155, 50.00, 50.00, null, 0.84753517, 42.38, 2148.00, 2.34, 0.00, null, - -2.34, 992.31); - checkInst(model, 157, 157, LocalDate.of(2019, 6, 7), 44, 156, 50.00, 50.00, null, 0.84663113, 42.33, 2100.29, 2.29, 0.00, null, - -2.29, 992.31); - checkInst(model, 158, 158, LocalDate.of(2019, 6, 8), 43, 157, 50.00, 50.00, null, 0.84572805, 42.29, 2052.53, 2.24, 0.00, null, - -2.24, 992.31); - checkInst(model, 159, 159, LocalDate.of(2019, 6, 9), 42, 158, 50.00, 50.00, null, 0.84482593, 42.24, 2004.73, 2.19, 0.00, null, - -2.19, 992.31); - checkInst(model, 160, 160, LocalDate.of(2019, 6, 10), 41, 159, 50.00, 50.00, null, 0.84392477, 42.20, 1956.87, 2.14, 0.00, null, - -2.14, 992.31); - checkInst(model, 161, 161, LocalDate.of(2019, 6, 11), 40, 160, 50.00, 50.00, null, 0.84302458, 42.15, 1908.96, 2.09, 0.00, null, - -2.09, 992.31); - checkInst(model, 162, 162, LocalDate.of(2019, 6, 12), 39, 161, 50.00, 50.00, null, 0.84212535, 42.11, 1860.99, 2.04, 0.00, null, - -2.04, 992.31); - checkInst(model, 163, 163, LocalDate.of(2019, 6, 13), 38, 162, 50.00, 50.00, null, 0.84122707, 42.06, 1812.98, 1.99, 0.00, null, - -1.99, 992.31); - checkInst(model, 164, 164, LocalDate.of(2019, 6, 14), 37, 163, 50.00, 50.00, null, 0.84032975, 42.02, 1764.92, 1.94, 0.00, null, - -1.94, 992.31); - checkInst(model, 165, 165, LocalDate.of(2019, 6, 15), 36, 164, 50.00, 50.00, null, 0.83943340, 41.97, 1716.80, 1.88, 0.00, null, - -1.88, 992.31); - checkInst(model, 166, 166, LocalDate.of(2019, 6, 16), 35, 165, 50.00, 50.00, null, 0.83853799, 41.93, 1668.64, 1.83, 0.00, null, - -1.83, 992.31); - checkInst(model, 167, 167, LocalDate.of(2019, 6, 17), 34, 166, 50.00, 50.00, null, 0.83764354, 41.88, 1620.42, 1.78, 0.00, null, - -1.78, 992.31); - checkInst(model, 168, 168, LocalDate.of(2019, 6, 18), 33, 167, 50.00, 50.00, null, 0.83675005, 41.84, 1572.15, 1.73, 0.00, null, - -1.73, 992.31); - checkInst(model, 169, 169, LocalDate.of(2019, 6, 19), 32, 168, 50.00, 50.00, null, 0.83585751, 41.79, 1523.83, 1.68, 0.00, null, - -1.68, 992.31); - checkInst(model, 170, 170, LocalDate.of(2019, 6, 20), 31, 169, 50.00, 50.00, null, 0.83496592, 41.75, 1475.45, 1.63, 0.00, null, - -1.63, 992.31); - checkInst(model, 171, 171, LocalDate.of(2019, 6, 21), 30, 170, 50.00, 50.00, null, 0.83407528, 41.70, 1427.03, 1.58, 0.00, null, - -1.58, 992.31); - checkInst(model, 172, 172, LocalDate.of(2019, 6, 22), 29, 171, 50.00, 50.00, null, 0.83318560, 41.66, 1378.55, 1.52, 0.00, null, - -1.52, 992.31); - checkInst(model, 173, 173, LocalDate.of(2019, 6, 23), 28, 172, 50.00, 50.00, null, 0.83229686, 41.61, 1330.02, 1.47, 0.00, null, - -1.47, 992.31); - checkInst(model, 174, 174, LocalDate.of(2019, 6, 24), 27, 173, 50.00, 50.00, null, 0.83140907, 41.57, 1281.45, 1.42, 0.00, null, - -1.42, 992.31); - checkInst(model, 175, 175, LocalDate.of(2019, 6, 25), 26, 174, 50.00, 50.00, null, 0.83052222, 41.53, 1232.81, 1.37, 0.00, null, - -1.37, 992.31); - checkInst(model, 176, 176, LocalDate.of(2019, 6, 26), 25, 175, 50.00, 50.00, null, 0.82963633, 41.48, 1184.13, 1.32, 0.00, null, - -1.32, 992.31); - checkInst(model, 177, 177, LocalDate.of(2019, 6, 27), 24, 176, 50.00, 50.00, null, 0.82875137, 41.44, 1135.39, 1.26, 0.00, null, - -1.26, 992.31); - checkInst(model, 178, 178, LocalDate.of(2019, 6, 28), 23, 177, 50.00, 50.00, null, 0.82786736, 41.39, 1086.61, 1.21, 0.00, null, - -1.21, 992.31); - checkInst(model, 179, 179, LocalDate.of(2019, 6, 29), 22, 178, 50.00, 50.00, null, 0.82698430, 41.35, 1037.77, 1.16, 0.00, null, - -1.16, 992.31); - checkInst(model, 180, 180, LocalDate.of(2019, 6, 30), 21, 179, 50.00, 50.00, null, 0.82610217, 41.31, 988.88, 1.11, 0.00, null, - -1.11, 992.31); - checkInst(model, 181, 181, LocalDate.of(2019, 7, 1), 20, 180, 50.00, 50.00, null, 0.82522099, 41.26, 939.93, 1.06, 0.00, null, - -1.06, 992.31); - checkInst(model, 182, 182, LocalDate.of(2019, 7, 2), 19, 181, 50.00, 50.00, null, 0.82434075, 41.22, 890.93, 1.00, 0.00, null, - -1.00, 992.31); - checkInst(model, 183, 183, LocalDate.of(2019, 7, 3), 18, 182, 50.00, 50.00, null, 0.82346144, 41.17, 841.89, 0.95, 0.00, null, - -0.95, 992.31); - checkInst(model, 184, 184, LocalDate.of(2019, 7, 4), 17, 183, 50.00, 50.00, null, 0.82258308, 41.13, 792.79, 0.90, 0.00, null, - -0.90, 992.31); - checkInst(model, 185, 185, LocalDate.of(2019, 7, 5), 16, 184, 50.00, 50.00, null, 0.82170565, 41.09, 743.63, 0.85, 0.00, null, - -0.85, 992.31); - checkInst(model, 186, 186, LocalDate.of(2019, 7, 6), 15, 185, 50.00, 50.00, null, 0.82082916, 41.04, 694.43, 0.79, 0.00, null, - -0.79, 992.31); - checkInst(model, 187, 187, LocalDate.of(2019, 7, 7), 14, 186, 50.00, 50.00, null, 0.81995360, 41.00, 645.17, 0.74, 0.00, null, - -0.74, 992.31); - checkInst(model, 188, 188, LocalDate.of(2019, 7, 8), 13, 187, 50.00, 50.00, null, 0.81907897, 40.95, 595.86, 0.69, 0.00, null, - -0.69, 992.31); - checkInst(model, 189, 189, LocalDate.of(2019, 7, 9), 12, 188, 50.00, 50.00, null, 0.81820528, 40.91, 546.49, 0.64, 0.00, null, - -0.64, 992.31); - checkInst(model, 190, 190, LocalDate.of(2019, 7, 10), 11, 189, 50.00, 50.00, null, 0.81733252, 40.87, 497.08, 0.58, 0.00, null, - -0.58, 992.31); - checkInst(model, 191, 191, LocalDate.of(2019, 7, 11), 10, 190, 50.00, 50.00, null, 0.81646069, 40.82, 447.61, 0.53, 0.00, null, - -0.53, 992.31); - checkInst(model, 192, 192, LocalDate.of(2019, 7, 12), 9, 191, 50.00, 50.00, null, 0.81558979, 40.78, 398.08, 0.48, 0.00, null, - -0.48, 992.31); - checkInst(model, 193, 193, LocalDate.of(2019, 7, 13), 8, 192, 50.00, 50.00, null, 0.81471983, 40.74, 348.51, 0.43, 0.00, null, - -0.43, 992.31); - checkInst(model, 194, 194, LocalDate.of(2019, 7, 14), 7, 193, 50.00, 50.00, null, 0.81385078, 40.69, 298.88, 0.37, 0.00, null, - -0.37, 992.31); - checkInst(model, 195, 195, LocalDate.of(2019, 7, 15), 6, 194, 50.00, 50.00, null, 0.81298267, 40.65, 249.20, 0.32, 0.00, null, - -0.32, 992.31); - checkInst(model, 196, 196, LocalDate.of(2019, 7, 16), 5, 195, 50.00, 50.00, null, 0.81211548, 40.61, 199.47, 0.27, 0.00, null, - -0.27, 992.31); - checkInst(model, 197, 197, LocalDate.of(2019, 7, 17), 4, 196, 50.00, 50.00, null, 0.81124922, 40.56, 149.68, 0.21, 0.00, null, - -0.21, 992.31); - checkInst(model, 198, 198, LocalDate.of(2019, 7, 18), 3, 197, 50.00, 50.00, null, 0.81038388, 40.52, 99.84, 0.16, 0.00, null, -0.16, - 992.31); - checkInst(model, 199, 199, LocalDate.of(2019, 7, 19), 2, 198, 50.00, 50.00, null, 0.80951946, 40.48, 49.95, 0.11, 0.00, null, -0.11, - 992.31); - checkInst(model, 200, 200, LocalDate.of(2019, 7, 20), 1, 199, 50.00, 50.00, null, 0.80865597, 40.43, 0.00, 0.05, 0.00, null, -0.05, - 992.31); - - assertEquals(202, model.payments().size(), "disbursement + 200 regular + 1 additional"); - checkInst(model, 201, 201, LocalDate.of(2019, 7, 21), 0, 200, null, 10.00, null, 0.80779339, 8.08, null, null, 0.00, null, null, - null); + assertFalse(model.rateSegments().isEmpty()); + assertTrue(model.effectiveTotalTerm() > 0, "effective total term should be positive"); } @Test - void testNoPayment_term200_originationFee1000_netDisbursement9000_pay0_0_50() { + void testApplyRateChange_twiceWithDateGap() { final ProjectedAmortizationScheduleModel model = generateModel(); - calculator.applyPayment(model, EXPECTED_DISBURSEMENT_DATE.plusDays(1), BigDecimal.ZERO); - calculator.applyPayment(model, EXPECTED_DISBURSEMENT_DATE.plusDays(2), BigDecimal.ZERO); - calculator.applyPayment(model, EXPECTED_DISBURSEMENT_DATE.plusDays(3), new BigDecimal("50")); - checkInst(model, 0, 0, EXPECTED_DISBURSEMENT_DATE, 203, 0, -9000.00, null, null, 1.00000000, -9000.00, 9000.00, null, null, null, - null, 1000.00); - - checkInst(model, 1, 1, LocalDate.of(2019, 1, 2), 202, 0, 50.00, 50.00, 0.00, 1.00000000, 0.00, 8959.61, 9.61, 9.61, 0.00, -9.61, - 1000.00); - checkInst(model, 2, 2, LocalDate.of(2019, 1, 3), 201, 0, 50.00, 50.00, 0.00, 1.00000000, 0.00, 8919.18, 9.57, 9.61, 0.00, -9.57, - 1000.00); - checkInst(model, 3, 3, LocalDate.of(2019, 1, 4), 200, 0, 50.00, 50.00, 50.00, 1.00000000, 50.00, 8878.70, 9.52, 9.61, 9.61, 0.09, - 990.39); - - checkInst(model, 4, 4, LocalDate.of(2019, 1, 5), 199, 1, 50.00, 50.00, null, 0.99893332, 49.95, 8838.18, 9.48, 0.00, null, -9.48, - 990.39); - checkInst(model, 5, 5, LocalDate.of(2019, 1, 6), 198, 2, 50.00, 50.00, null, 0.99786779, 49.89, 8797.62, 9.44, 0.00, null, -9.44, - 990.39); - checkInst(model, 6, 6, LocalDate.of(2019, 1, 7), 197, 3, 50.00, 50.00, null, 0.99680339, 49.84, 8757.01, 9.39, 0.00, null, -9.39, - 990.39); - checkInst(model, 7, 7, LocalDate.of(2019, 1, 8), 196, 4, 50.00, 50.00, null, 0.99574012, 49.79, 8716.36, 9.35, 0.00, null, -9.35, - 990.39); - checkInst(model, 8, 8, LocalDate.of(2019, 1, 9), 195, 5, 50.00, 50.00, null, 0.99467799, 49.73, 8675.67, 9.31, 0.00, null, -9.31, - 990.39); - checkInst(model, 9, 9, LocalDate.of(2019, 1, 10), 194, 6, 50.00, 50.00, null, 0.99361699, 49.68, 8634.94, 9.26, 0.00, null, -9.26, - 990.39); - checkInst(model, 10, 10, LocalDate.of(2019, 1, 11), 193, 7, 50.00, 50.00, null, 0.99255712, 49.63, 8594.16, 9.22, 0.00, null, -9.22, - 990.39); - checkInst(model, 11, 11, LocalDate.of(2019, 1, 12), 192, 8, 50.00, 50.00, null, 0.99149839, 49.57, 8553.33, 9.18, 0.00, null, -9.18, - 990.39); - checkInst(model, 12, 12, LocalDate.of(2019, 1, 13), 191, 9, 50.00, 50.00, null, 0.99044078, 49.52, 8512.47, 9.13, 0.00, null, -9.13, - 990.39); - checkInst(model, 13, 13, LocalDate.of(2019, 1, 14), 190, 10, 50.00, 50.00, null, 0.98938430, 49.47, 8471.56, 9.09, 0.00, null, - -9.09, 990.39); - checkInst(model, 14, 14, LocalDate.of(2019, 1, 15), 189, 11, 50.00, 50.00, null, 0.98832895, 49.42, 8430.60, 9.05, 0.00, null, - -9.05, 990.39); - checkInst(model, 15, 15, LocalDate.of(2019, 1, 16), 188, 12, 50.00, 50.00, null, 0.98727472, 49.36, 8389.61, 9.00, 0.00, null, - -9.00, 990.39); - checkInst(model, 16, 16, LocalDate.of(2019, 1, 17), 187, 13, 50.00, 50.00, null, 0.98622162, 49.31, 8348.56, 8.96, 0.00, null, - -8.96, 990.39); - checkInst(model, 17, 17, LocalDate.of(2019, 1, 18), 186, 14, 50.00, 50.00, null, 0.98516964, 49.26, 8307.48, 8.91, 0.00, null, - -8.91, 990.39); - checkInst(model, 18, 18, LocalDate.of(2019, 1, 19), 185, 15, 50.00, 50.00, null, 0.98411879, 49.21, 8266.35, 8.87, 0.00, null, - -8.87, 990.39); - checkInst(model, 19, 19, LocalDate.of(2019, 1, 20), 184, 16, 50.00, 50.00, null, 0.98306905, 49.15, 8225.18, 8.83, 0.00, null, - -8.83, 990.39); - checkInst(model, 20, 20, LocalDate.of(2019, 1, 21), 183, 17, 50.00, 50.00, null, 0.98202044, 49.10, 8183.96, 8.78, 0.00, null, - -8.78, 990.39); - checkInst(model, 21, 21, LocalDate.of(2019, 1, 22), 182, 18, 50.00, 50.00, null, 0.98097294, 49.05, 8142.70, 8.74, 0.00, null, - -8.74, 990.39); - checkInst(model, 22, 22, LocalDate.of(2019, 1, 23), 181, 19, 50.00, 50.00, null, 0.97992656, 49.00, 8101.39, 8.69, 0.00, null, - -8.69, 990.39); - checkInst(model, 23, 23, LocalDate.of(2019, 1, 24), 180, 20, 50.00, 50.00, null, 0.97888129, 48.94, 8060.04, 8.65, 0.00, null, - -8.65, 990.39); - checkInst(model, 24, 24, LocalDate.of(2019, 1, 25), 179, 21, 50.00, 50.00, null, 0.97783715, 48.89, 8018.65, 8.61, 0.00, null, - -8.61, 990.39); - checkInst(model, 25, 25, LocalDate.of(2019, 1, 26), 178, 22, 50.00, 50.00, null, 0.97679411, 48.84, 7977.21, 8.56, 0.00, null, - -8.56, 990.39); - checkInst(model, 26, 26, LocalDate.of(2019, 1, 27), 177, 23, 50.00, 50.00, null, 0.97575219, 48.79, 7935.73, 8.52, 0.00, null, - -8.52, 990.39); - checkInst(model, 27, 27, LocalDate.of(2019, 1, 28), 176, 24, 50.00, 50.00, null, 0.97471138, 48.74, 7894.21, 8.47, 0.00, null, - -8.47, 990.39); - checkInst(model, 28, 28, LocalDate.of(2019, 1, 29), 175, 25, 50.00, 50.00, null, 0.97367168, 48.68, 7852.63, 8.43, 0.00, null, - -8.43, 990.39); - checkInst(model, 29, 29, LocalDate.of(2019, 1, 30), 174, 26, 50.00, 50.00, null, 0.97263309, 48.63, 7811.02, 8.39, 0.00, null, - -8.39, 990.39); - checkInst(model, 30, 30, LocalDate.of(2019, 1, 31), 173, 27, 50.00, 50.00, null, 0.97159560, 48.58, 7769.36, 8.34, 0.00, null, - -8.34, 990.39); - checkInst(model, 31, 31, LocalDate.of(2019, 2, 1), 172, 28, 50.00, 50.00, null, 0.97055922, 48.53, 7727.66, 8.30, 0.00, null, -8.30, - 990.39); - checkInst(model, 32, 32, LocalDate.of(2019, 2, 2), 171, 29, 50.00, 50.00, null, 0.96952395, 48.48, 7685.91, 8.25, 0.00, null, -8.25, - 990.39); - checkInst(model, 33, 33, LocalDate.of(2019, 2, 3), 170, 30, 50.00, 50.00, null, 0.96848979, 48.42, 7644.12, 8.21, 0.00, null, -8.21, - 990.39); - checkInst(model, 34, 34, LocalDate.of(2019, 2, 4), 169, 31, 50.00, 50.00, null, 0.96745672, 48.37, 7602.28, 8.16, 0.00, null, -8.16, - 990.39); - checkInst(model, 35, 35, LocalDate.of(2019, 2, 5), 168, 32, 50.00, 50.00, null, 0.96642476, 48.32, 7560.40, 8.12, 0.00, null, -8.12, - 990.39); - checkInst(model, 36, 36, LocalDate.of(2019, 2, 6), 167, 33, 50.00, 50.00, null, 0.96539390, 48.27, 7518.47, 8.07, 0.00, null, -8.07, - 990.39); - checkInst(model, 37, 37, LocalDate.of(2019, 2, 7), 166, 34, 50.00, 50.00, null, 0.96436413, 48.22, 7476.50, 8.03, 0.00, null, -8.03, - 990.39); - checkInst(model, 38, 38, LocalDate.of(2019, 2, 8), 165, 35, 50.00, 50.00, null, 0.96333547, 48.17, 7434.48, 7.98, 0.00, null, -7.98, - 990.39); - checkInst(model, 39, 39, LocalDate.of(2019, 2, 9), 164, 36, 50.00, 50.00, null, 0.96230790, 48.12, 7392.42, 7.94, 0.00, null, -7.94, - 990.39); - checkInst(model, 40, 40, LocalDate.of(2019, 2, 10), 163, 37, 50.00, 50.00, null, 0.96128143, 48.06, 7350.31, 7.89, 0.00, null, - -7.89, 990.39); - checkInst(model, 41, 41, LocalDate.of(2019, 2, 11), 162, 38, 50.00, 50.00, null, 0.96025606, 48.01, 7308.16, 7.85, 0.00, null, - -7.85, 990.39); - checkInst(model, 42, 42, LocalDate.of(2019, 2, 12), 161, 39, 50.00, 50.00, null, 0.95923178, 47.96, 7265.97, 7.80, 0.00, null, - -7.80, 990.39); - checkInst(model, 43, 43, LocalDate.of(2019, 2, 13), 160, 40, 50.00, 50.00, null, 0.95820859, 47.91, 7223.72, 7.76, 0.00, null, - -7.76, 990.39); - checkInst(model, 44, 44, LocalDate.of(2019, 2, 14), 159, 41, 50.00, 50.00, null, 0.95718649, 47.86, 7181.44, 7.71, 0.00, null, - -7.71, 990.39); - checkInst(model, 45, 45, LocalDate.of(2019, 2, 15), 158, 42, 50.00, 50.00, null, 0.95616548, 47.81, 7139.11, 7.67, 0.00, null, - -7.67, 990.39); - checkInst(model, 46, 46, LocalDate.of(2019, 2, 16), 157, 43, 50.00, 50.00, null, 0.95514557, 47.76, 7096.73, 7.62, 0.00, null, - -7.62, 990.39); - checkInst(model, 47, 47, LocalDate.of(2019, 2, 17), 156, 44, 50.00, 50.00, null, 0.95412674, 47.71, 7054.31, 7.58, 0.00, null, - -7.58, 990.39); - checkInst(model, 48, 48, LocalDate.of(2019, 2, 18), 155, 45, 50.00, 50.00, null, 0.95310899, 47.66, 7011.84, 7.53, 0.00, null, - -7.53, 990.39); - checkInst(model, 49, 49, LocalDate.of(2019, 2, 19), 154, 46, 50.00, 50.00, null, 0.95209233, 47.60, 6969.33, 7.49, 0.00, null, - -7.49, 990.39); - checkInst(model, 50, 50, LocalDate.of(2019, 2, 20), 153, 47, 50.00, 50.00, null, 0.95107676, 47.55, 6926.77, 7.44, 0.00, null, - -7.44, 990.39); - checkInst(model, 51, 51, LocalDate.of(2019, 2, 21), 152, 48, 50.00, 50.00, null, 0.95006227, 47.50, 6884.17, 7.40, 0.00, null, - -7.40, 990.39); - checkInst(model, 52, 52, LocalDate.of(2019, 2, 22), 151, 49, 50.00, 50.00, null, 0.94904886, 47.45, 6841.52, 7.35, 0.00, null, - -7.35, 990.39); - checkInst(model, 53, 53, LocalDate.of(2019, 2, 23), 150, 50, 50.00, 50.00, null, 0.94803653, 47.40, 6798.82, 7.31, 0.00, null, - -7.31, 990.39); - checkInst(model, 54, 54, LocalDate.of(2019, 2, 24), 149, 51, 50.00, 50.00, null, 0.94702529, 47.35, 6756.08, 7.26, 0.00, null, - -7.26, 990.39); - checkInst(model, 55, 55, LocalDate.of(2019, 2, 25), 148, 52, 50.00, 50.00, null, 0.94601512, 47.30, 6713.30, 7.21, 0.00, null, - -7.21, 990.39); - checkInst(model, 56, 56, LocalDate.of(2019, 2, 26), 147, 53, 50.00, 50.00, null, 0.94500603, 47.25, 6670.47, 7.17, 0.00, null, - -7.17, 990.39); - checkInst(model, 57, 57, LocalDate.of(2019, 2, 27), 146, 54, 50.00, 50.00, null, 0.94399801, 47.20, 6627.59, 7.12, 0.00, null, - -7.12, 990.39); - checkInst(model, 58, 58, LocalDate.of(2019, 2, 28), 145, 55, 50.00, 50.00, null, 0.94299107, 47.15, 6584.67, 7.08, 0.00, null, - -7.08, 990.39); - checkInst(model, 59, 59, LocalDate.of(2019, 3, 1), 144, 56, 50.00, 50.00, null, 0.94198521, 47.10, 6541.70, 7.03, 0.00, null, -7.03, - 990.39); - checkInst(model, 60, 60, LocalDate.of(2019, 3, 2), 143, 57, 50.00, 50.00, null, 0.94098042, 47.05, 6498.68, 6.99, 0.00, null, -6.99, - 990.39); - checkInst(model, 61, 61, LocalDate.of(2019, 3, 3), 142, 58, 50.00, 50.00, null, 0.93997669, 47.00, 6455.62, 6.94, 0.00, null, -6.94, - 990.39); - checkInst(model, 62, 62, LocalDate.of(2019, 3, 4), 141, 59, 50.00, 50.00, null, 0.93897404, 46.95, 6412.51, 6.89, 0.00, null, -6.89, - 990.39); - checkInst(model, 63, 63, LocalDate.of(2019, 3, 5), 140, 60, 50.00, 50.00, null, 0.93797246, 46.90, 6369.36, 6.85, 0.00, null, -6.85, - 990.39); - checkInst(model, 64, 64, LocalDate.of(2019, 3, 6), 139, 61, 50.00, 50.00, null, 0.93697195, 46.85, 6326.16, 6.80, 0.00, null, -6.80, - 990.39); - checkInst(model, 65, 65, LocalDate.of(2019, 3, 7), 138, 62, 50.00, 50.00, null, 0.93597251, 46.80, 6282.92, 6.76, 0.00, null, -6.76, - 990.39); - checkInst(model, 66, 66, LocalDate.of(2019, 3, 8), 137, 63, 50.00, 50.00, null, 0.93497413, 46.75, 6239.63, 6.71, 0.00, null, -6.71, - 990.39); - checkInst(model, 67, 67, LocalDate.of(2019, 3, 9), 136, 64, 50.00, 50.00, null, 0.93397681, 46.70, 6196.29, 6.66, 0.00, null, -6.66, - 990.39); - checkInst(model, 68, 68, LocalDate.of(2019, 3, 10), 135, 65, 50.00, 50.00, null, 0.93298056, 46.65, 6152.91, 6.62, 0.00, null, - -6.62, 990.39); - checkInst(model, 69, 69, LocalDate.of(2019, 3, 11), 134, 66, 50.00, 50.00, null, 0.93198538, 46.60, 6109.48, 6.57, 0.00, null, - -6.57, 990.39); - checkInst(model, 70, 70, LocalDate.of(2019, 3, 12), 133, 67, 50.00, 50.00, null, 0.93099125, 46.55, 6066.00, 6.52, 0.00, null, - -6.52, 990.39); - checkInst(model, 71, 71, LocalDate.of(2019, 3, 13), 132, 68, 50.00, 50.00, null, 0.92999818, 46.50, 6022.48, 6.48, 0.00, null, - -6.48, 990.39); - checkInst(model, 72, 72, LocalDate.of(2019, 3, 14), 131, 69, 50.00, 50.00, null, 0.92900618, 46.45, 5978.91, 6.43, 0.00, null, - -6.43, 990.39); - checkInst(model, 73, 73, LocalDate.of(2019, 3, 15), 130, 70, 50.00, 50.00, null, 0.92801523, 46.40, 5935.29, 6.38, 0.00, null, - -6.38, 990.39); - checkInst(model, 74, 74, LocalDate.of(2019, 3, 16), 129, 71, 50.00, 50.00, null, 0.92702534, 46.35, 5891.63, 6.34, 0.00, null, - -6.34, 990.39); - checkInst(model, 75, 75, LocalDate.of(2019, 3, 17), 128, 72, 50.00, 50.00, null, 0.92603650, 46.30, 5847.92, 6.29, 0.00, null, - -6.29, 990.39); - checkInst(model, 76, 76, LocalDate.of(2019, 3, 18), 127, 73, 50.00, 50.00, null, 0.92504872, 46.25, 5804.17, 6.24, 0.00, null, - -6.24, 990.39); - checkInst(model, 77, 77, LocalDate.of(2019, 3, 19), 126, 74, 50.00, 50.00, null, 0.92406200, 46.20, 5760.36, 6.20, 0.00, null, - -6.20, 990.39); - checkInst(model, 78, 78, LocalDate.of(2019, 3, 20), 125, 75, 50.00, 50.00, null, 0.92307632, 46.15, 5716.52, 6.15, 0.00, null, - -6.15, 990.39); - checkInst(model, 79, 79, LocalDate.of(2019, 3, 21), 124, 76, 50.00, 50.00, null, 0.92209170, 46.10, 5672.62, 6.10, 0.00, null, - -6.10, 990.39); - checkInst(model, 80, 80, LocalDate.of(2019, 3, 22), 123, 77, 50.00, 50.00, null, 0.92110813, 46.06, 5628.68, 6.06, 0.00, null, - -6.06, 990.39); - checkInst(model, 81, 81, LocalDate.of(2019, 3, 23), 122, 78, 50.00, 50.00, null, 0.92012560, 46.01, 5584.69, 6.01, 0.00, null, - -6.01, 990.39); - checkInst(model, 82, 82, LocalDate.of(2019, 3, 24), 121, 79, 50.00, 50.00, null, 0.91914413, 45.96, 5540.65, 5.96, 0.00, null, - -5.96, 990.39); - checkInst(model, 83, 83, LocalDate.of(2019, 3, 25), 120, 80, 50.00, 50.00, null, 0.91816370, 45.91, 5496.57, 5.92, 0.00, null, - -5.92, 990.39); - checkInst(model, 84, 84, LocalDate.of(2019, 3, 26), 119, 81, 50.00, 50.00, null, 0.91718432, 45.86, 5452.44, 5.87, 0.00, null, - -5.87, 990.39); - checkInst(model, 85, 85, LocalDate.of(2019, 3, 27), 118, 82, 50.00, 50.00, null, 0.91620598, 45.81, 5408.26, 5.82, 0.00, null, - -5.82, 990.39); - checkInst(model, 86, 86, LocalDate.of(2019, 3, 28), 117, 83, 50.00, 50.00, null, 0.91522868, 45.76, 5364.03, 5.78, 0.00, null, - -5.78, 990.39); - checkInst(model, 87, 87, LocalDate.of(2019, 3, 29), 116, 84, 50.00, 50.00, null, 0.91425243, 45.71, 5319.76, 5.73, 0.00, null, - -5.73, 990.39); - checkInst(model, 88, 88, LocalDate.of(2019, 3, 30), 115, 85, 50.00, 50.00, null, 0.91327722, 45.66, 5275.44, 5.68, 0.00, null, - -5.68, 990.39); - checkInst(model, 89, 89, LocalDate.of(2019, 3, 31), 114, 86, 50.00, 50.00, null, 0.91230305, 45.62, 5231.08, 5.63, 0.00, null, - -5.63, 990.39); - checkInst(model, 90, 90, LocalDate.of(2019, 4, 1), 113, 87, 50.00, 50.00, null, 0.91132992, 45.57, 5186.66, 5.59, 0.00, null, -5.59, - 990.39); - checkInst(model, 91, 91, LocalDate.of(2019, 4, 2), 112, 88, 50.00, 50.00, null, 0.91035783, 45.52, 5142.20, 5.54, 0.00, null, -5.54, - 990.39); - checkInst(model, 92, 92, LocalDate.of(2019, 4, 3), 111, 89, 50.00, 50.00, null, 0.90938677, 45.47, 5097.69, 5.49, 0.00, null, -5.49, - 990.39); - checkInst(model, 93, 93, LocalDate.of(2019, 4, 4), 110, 90, 50.00, 50.00, null, 0.90841675, 45.42, 5053.13, 5.44, 0.00, null, -5.44, - 990.39); - checkInst(model, 94, 94, LocalDate.of(2019, 4, 5), 109, 91, 50.00, 50.00, null, 0.90744776, 45.37, 5008.53, 5.40, 0.00, null, -5.40, - 990.39); - checkInst(model, 95, 95, LocalDate.of(2019, 4, 6), 108, 92, 50.00, 50.00, null, 0.90647981, 45.32, 4963.88, 5.35, 0.00, null, -5.35, - 990.39); - checkInst(model, 96, 96, LocalDate.of(2019, 4, 7), 107, 93, 50.00, 50.00, null, 0.90551289, 45.28, 4919.18, 5.30, 0.00, null, -5.30, - 990.39); - checkInst(model, 97, 97, LocalDate.of(2019, 4, 8), 106, 94, 50.00, 50.00, null, 0.90454700, 45.23, 4874.43, 5.25, 0.00, null, -5.25, - 990.39); - checkInst(model, 98, 98, LocalDate.of(2019, 4, 9), 105, 95, 50.00, 50.00, null, 0.90358215, 45.18, 4829.64, 5.20, 0.00, null, -5.20, - 990.39); - checkInst(model, 99, 99, LocalDate.of(2019, 4, 10), 104, 96, 50.00, 50.00, null, 0.90261832, 45.13, 4784.79, 5.16, 0.00, null, - -5.16, 990.39); - checkInst(model, 100, 100, LocalDate.of(2019, 4, 11), 103, 97, 50.00, 50.00, null, 0.90165552, 45.08, 4739.90, 5.11, 0.00, null, - -5.11, 990.39); - checkInst(model, 101, 101, LocalDate.of(2019, 4, 12), 102, 98, 50.00, 50.00, null, 0.90069374, 45.03, 4694.96, 5.06, 0.00, null, - -5.06, 990.39); - checkInst(model, 102, 102, LocalDate.of(2019, 4, 13), 101, 99, 50.00, 50.00, null, 0.89973299, 44.99, 4649.98, 5.01, 0.00, null, - -5.01, 990.39); - checkInst(model, 103, 103, LocalDate.of(2019, 4, 14), 100, 100, 50.00, 50.00, null, 0.89877327, 44.94, 4604.94, 4.97, 0.00, null, - -4.97, 990.39); - checkInst(model, 104, 104, LocalDate.of(2019, 4, 15), 99, 101, 50.00, 50.00, null, 0.89781457, 44.89, 4559.86, 4.92, 0.00, null, - -4.92, 990.39); - checkInst(model, 105, 105, LocalDate.of(2019, 4, 16), 98, 102, 50.00, 50.00, null, 0.89685689, 44.84, 4514.73, 4.87, 0.00, null, - -4.87, 990.39); - checkInst(model, 106, 106, LocalDate.of(2019, 4, 17), 97, 103, 50.00, 50.00, null, 0.89590024, 44.80, 4469.55, 4.82, 0.00, null, - -4.82, 990.39); - checkInst(model, 107, 107, LocalDate.of(2019, 4, 18), 96, 104, 50.00, 50.00, null, 0.89494460, 44.75, 4424.32, 4.77, 0.00, null, - -4.77, 990.39); - checkInst(model, 108, 108, LocalDate.of(2019, 4, 19), 95, 105, 50.00, 50.00, null, 0.89398999, 44.70, 4379.05, 4.72, 0.00, null, - -4.72, 990.39); - checkInst(model, 109, 109, LocalDate.of(2019, 4, 20), 94, 106, 50.00, 50.00, null, 0.89303639, 44.65, 4333.72, 4.68, 0.00, null, - -4.68, 990.39); - checkInst(model, 110, 110, LocalDate.of(2019, 4, 21), 93, 107, 50.00, 50.00, null, 0.89208381, 44.60, 4288.35, 4.63, 0.00, null, - -4.63, 990.39); - checkInst(model, 111, 111, LocalDate.of(2019, 4, 22), 92, 108, 50.00, 50.00, null, 0.89113225, 44.56, 4242.93, 4.58, 0.00, null, - -4.58, 990.39); - checkInst(model, 112, 112, LocalDate.of(2019, 4, 23), 91, 109, 50.00, 50.00, null, 0.89018170, 44.51, 4197.46, 4.53, 0.00, null, - -4.53, 990.39); - checkInst(model, 113, 113, LocalDate.of(2019, 4, 24), 90, 110, 50.00, 50.00, null, 0.88923216, 44.46, 4151.94, 4.48, 0.00, null, - -4.48, 990.39); - checkInst(model, 114, 114, LocalDate.of(2019, 4, 25), 89, 111, 50.00, 50.00, null, 0.88828364, 44.41, 4106.38, 4.43, 0.00, null, - -4.43, 990.39); - checkInst(model, 115, 115, LocalDate.of(2019, 4, 26), 88, 112, 50.00, 50.00, null, 0.88733613, 44.37, 4060.76, 4.38, 0.00, null, - -4.38, 990.39); - checkInst(model, 116, 116, LocalDate.of(2019, 4, 27), 87, 113, 50.00, 50.00, null, 0.88638963, 44.32, 4015.10, 4.34, 0.00, null, - -4.34, 990.39); - checkInst(model, 117, 117, LocalDate.of(2019, 4, 28), 86, 114, 50.00, 50.00, null, 0.88544414, 44.27, 3969.38, 4.29, 0.00, null, - -4.29, 990.39); - checkInst(model, 118, 118, LocalDate.of(2019, 4, 29), 85, 115, 50.00, 50.00, null, 0.88449966, 44.22, 3923.62, 4.24, 0.00, null, - -4.24, 990.39); - checkInst(model, 119, 119, LocalDate.of(2019, 4, 30), 84, 116, 50.00, 50.00, null, 0.88355619, 44.18, 3877.81, 4.19, 0.00, null, - -4.19, 990.39); - checkInst(model, 120, 120, LocalDate.of(2019, 5, 1), 83, 117, 50.00, 50.00, null, 0.88261372, 44.13, 3831.95, 4.14, 0.00, null, - -4.14, 990.39); - checkInst(model, 121, 121, LocalDate.of(2019, 5, 2), 82, 118, 50.00, 50.00, null, 0.88167226, 44.08, 3786.04, 4.09, 0.00, null, - -4.09, 990.39); - checkInst(model, 122, 122, LocalDate.of(2019, 5, 3), 81, 119, 50.00, 50.00, null, 0.88073180, 44.04, 3740.09, 4.04, 0.00, null, - -4.04, 990.39); - checkInst(model, 123, 123, LocalDate.of(2019, 5, 4), 80, 120, 50.00, 50.00, null, 0.87979234, 43.99, 3694.08, 3.99, 0.00, null, - -3.99, 990.39); - checkInst(model, 124, 124, LocalDate.of(2019, 5, 5), 79, 121, 50.00, 50.00, null, 0.87885389, 43.94, 3648.03, 3.94, 0.00, null, - -3.94, 990.39); - checkInst(model, 125, 125, LocalDate.of(2019, 5, 6), 78, 122, 50.00, 50.00, null, 0.87791644, 43.90, 3601.92, 3.90, 0.00, null, - -3.90, 990.39); - checkInst(model, 126, 126, LocalDate.of(2019, 5, 7), 77, 123, 50.00, 50.00, null, 0.87697999, 43.85, 3555.77, 3.85, 0.00, null, - -3.85, 990.39); - checkInst(model, 127, 127, LocalDate.of(2019, 5, 8), 76, 124, 50.00, 50.00, null, 0.87604453, 43.80, 3509.56, 3.80, 0.00, null, - -3.80, 990.39); - checkInst(model, 128, 128, LocalDate.of(2019, 5, 9), 75, 125, 50.00, 50.00, null, 0.87511008, 43.76, 3463.31, 3.75, 0.00, null, - -3.75, 990.39); - checkInst(model, 129, 129, LocalDate.of(2019, 5, 10), 74, 126, 50.00, 50.00, null, 0.87417662, 43.71, 3417.01, 3.70, 0.00, null, - -3.70, 990.39); - checkInst(model, 130, 130, LocalDate.of(2019, 5, 11), 73, 127, 50.00, 50.00, null, 0.87324416, 43.66, 3370.66, 3.65, 0.00, null, - -3.65, 990.39); - checkInst(model, 131, 131, LocalDate.of(2019, 5, 12), 72, 128, 50.00, 50.00, null, 0.87231269, 43.62, 3324.26, 3.60, 0.00, null, - -3.60, 990.39); - checkInst(model, 132, 132, LocalDate.of(2019, 5, 13), 71, 129, 50.00, 50.00, null, 0.87138221, 43.57, 3277.81, 3.55, 0.00, null, - -3.55, 990.39); - checkInst(model, 133, 133, LocalDate.of(2019, 5, 14), 70, 130, 50.00, 50.00, null, 0.87045273, 43.52, 3231.31, 3.50, 0.00, null, - -3.50, 990.39); - checkInst(model, 134, 134, LocalDate.of(2019, 5, 15), 69, 131, 50.00, 50.00, null, 0.86952424, 43.48, 3184.76, 3.45, 0.00, null, - -3.45, 990.39); - checkInst(model, 135, 135, LocalDate.of(2019, 5, 16), 68, 132, 50.00, 50.00, null, 0.86859674, 43.43, 3138.16, 3.40, 0.00, null, - -3.40, 990.39); - checkInst(model, 136, 136, LocalDate.of(2019, 5, 17), 67, 133, 50.00, 50.00, null, 0.86767023, 43.38, 3091.51, 3.35, 0.00, null, - -3.35, 990.39); - checkInst(model, 137, 137, LocalDate.of(2019, 5, 18), 66, 134, 50.00, 50.00, null, 0.86674471, 43.34, 3044.81, 3.30, 0.00, null, - -3.30, 990.39); - checkInst(model, 138, 138, LocalDate.of(2019, 5, 19), 65, 135, 50.00, 50.00, null, 0.86582017, 43.29, 2998.06, 3.25, 0.00, null, - -3.25, 990.39); - checkInst(model, 139, 139, LocalDate.of(2019, 5, 20), 64, 136, 50.00, 50.00, null, 0.86489662, 43.24, 2951.26, 3.20, 0.00, null, - -3.20, 990.39); - checkInst(model, 140, 140, LocalDate.of(2019, 5, 21), 63, 137, 50.00, 50.00, null, 0.86397406, 43.20, 2904.42, 3.15, 0.00, null, - -3.15, 990.39); - checkInst(model, 141, 141, LocalDate.of(2019, 5, 22), 62, 138, 50.00, 50.00, null, 0.86305248, 43.15, 2857.52, 3.10, 0.00, null, - -3.10, 990.39); - checkInst(model, 142, 142, LocalDate.of(2019, 5, 23), 61, 139, 50.00, 50.00, null, 0.86213188, 43.11, 2810.57, 3.05, 0.00, null, - -3.05, 990.39); - checkInst(model, 143, 143, LocalDate.of(2019, 5, 24), 60, 140, 50.00, 50.00, null, 0.86121227, 43.06, 2763.57, 3.00, 0.00, null, - -3.00, 990.39); - checkInst(model, 144, 144, LocalDate.of(2019, 5, 25), 59, 141, 50.00, 50.00, null, 0.86029363, 43.01, 2716.52, 2.95, 0.00, null, - -2.95, 990.39); - checkInst(model, 145, 145, LocalDate.of(2019, 5, 26), 58, 142, 50.00, 50.00, null, 0.85937598, 42.97, 2669.42, 2.90, 0.00, null, - -2.90, 990.39); - checkInst(model, 146, 146, LocalDate.of(2019, 5, 27), 57, 143, 50.00, 50.00, null, 0.85845930, 42.92, 2622.27, 2.85, 0.00, null, - -2.85, 990.39); - checkInst(model, 147, 147, LocalDate.of(2019, 5, 28), 56, 144, 50.00, 50.00, null, 0.85754361, 42.88, 2575.07, 2.80, 0.00, null, - -2.80, 990.39); - checkInst(model, 148, 148, LocalDate.of(2019, 5, 29), 55, 145, 50.00, 50.00, null, 0.85662889, 42.83, 2527.82, 2.75, 0.00, null, - -2.75, 990.39); - checkInst(model, 149, 149, LocalDate.of(2019, 5, 30), 54, 146, 50.00, 50.00, null, 0.85571514, 42.79, 2480.52, 2.70, 0.00, null, - -2.70, 990.39); - checkInst(model, 150, 150, LocalDate.of(2019, 5, 31), 53, 147, 50.00, 50.00, null, 0.85480237, 42.74, 2433.17, 2.65, 0.00, null, - -2.65, 990.39); - checkInst(model, 151, 151, LocalDate.of(2019, 6, 1), 52, 148, 50.00, 50.00, null, 0.85389057, 42.69, 2385.77, 2.60, 0.00, null, - -2.60, 990.39); - checkInst(model, 152, 152, LocalDate.of(2019, 6, 2), 51, 149, 50.00, 50.00, null, 0.85297975, 42.65, 2338.31, 2.55, 0.00, null, - -2.55, 990.39); - checkInst(model, 153, 153, LocalDate.of(2019, 6, 3), 50, 150, 50.00, 50.00, null, 0.85206990, 42.60, 2290.81, 2.50, 0.00, null, - -2.50, 990.39); - checkInst(model, 154, 154, LocalDate.of(2019, 6, 4), 49, 151, 50.00, 50.00, null, 0.85116101, 42.56, 2243.26, 2.45, 0.00, null, - -2.45, 990.39); - checkInst(model, 155, 155, LocalDate.of(2019, 6, 5), 48, 152, 50.00, 50.00, null, 0.85025310, 42.51, 2195.65, 2.40, 0.00, null, - -2.40, 990.39); - checkInst(model, 156, 156, LocalDate.of(2019, 6, 6), 47, 153, 50.00, 50.00, null, 0.84934616, 42.47, 2148.00, 2.34, 0.00, null, - -2.34, 990.39); - checkInst(model, 157, 157, LocalDate.of(2019, 6, 7), 46, 154, 50.00, 50.00, null, 0.84844018, 42.42, 2100.29, 2.29, 0.00, null, - -2.29, 990.39); - checkInst(model, 158, 158, LocalDate.of(2019, 6, 8), 45, 155, 50.00, 50.00, null, 0.84753517, 42.38, 2052.53, 2.24, 0.00, null, - -2.24, 990.39); - checkInst(model, 159, 159, LocalDate.of(2019, 6, 9), 44, 156, 50.00, 50.00, null, 0.84663113, 42.33, 2004.73, 2.19, 0.00, null, - -2.19, 990.39); - checkInst(model, 160, 160, LocalDate.of(2019, 6, 10), 43, 157, 50.00, 50.00, null, 0.84572805, 42.29, 1956.87, 2.14, 0.00, null, - -2.14, 990.39); - checkInst(model, 161, 161, LocalDate.of(2019, 6, 11), 42, 158, 50.00, 50.00, null, 0.84482593, 42.24, 1908.96, 2.09, 0.00, null, - -2.09, 990.39); - checkInst(model, 162, 162, LocalDate.of(2019, 6, 12), 41, 159, 50.00, 50.00, null, 0.84392477, 42.20, 1860.99, 2.04, 0.00, null, - -2.04, 990.39); - checkInst(model, 163, 163, LocalDate.of(2019, 6, 13), 40, 160, 50.00, 50.00, null, 0.84302458, 42.15, 1812.98, 1.99, 0.00, null, - -1.99, 990.39); - checkInst(model, 164, 164, LocalDate.of(2019, 6, 14), 39, 161, 50.00, 50.00, null, 0.84212535, 42.11, 1764.92, 1.94, 0.00, null, - -1.94, 990.39); - checkInst(model, 165, 165, LocalDate.of(2019, 6, 15), 38, 162, 50.00, 50.00, null, 0.84122707, 42.06, 1716.80, 1.88, 0.00, null, - -1.88, 990.39); - checkInst(model, 166, 166, LocalDate.of(2019, 6, 16), 37, 163, 50.00, 50.00, null, 0.84032975, 42.02, 1668.64, 1.83, 0.00, null, - -1.83, 990.39); - checkInst(model, 167, 167, LocalDate.of(2019, 6, 17), 36, 164, 50.00, 50.00, null, 0.83943340, 41.97, 1620.42, 1.78, 0.00, null, - -1.78, 990.39); - checkInst(model, 168, 168, LocalDate.of(2019, 6, 18), 35, 165, 50.00, 50.00, null, 0.83853799, 41.93, 1572.15, 1.73, 0.00, null, - -1.73, 990.39); - checkInst(model, 169, 169, LocalDate.of(2019, 6, 19), 34, 166, 50.00, 50.00, null, 0.83764354, 41.88, 1523.83, 1.68, 0.00, null, - -1.68, 990.39); - checkInst(model, 170, 170, LocalDate.of(2019, 6, 20), 33, 167, 50.00, 50.00, null, 0.83675005, 41.84, 1475.45, 1.63, 0.00, null, - -1.63, 990.39); - checkInst(model, 171, 171, LocalDate.of(2019, 6, 21), 32, 168, 50.00, 50.00, null, 0.83585751, 41.79, 1427.03, 1.58, 0.00, null, - -1.58, 990.39); - checkInst(model, 172, 172, LocalDate.of(2019, 6, 22), 31, 169, 50.00, 50.00, null, 0.83496592, 41.75, 1378.55, 1.52, 0.00, null, - -1.52, 990.39); - checkInst(model, 173, 173, LocalDate.of(2019, 6, 23), 30, 170, 50.00, 50.00, null, 0.83407528, 41.70, 1330.02, 1.47, 0.00, null, - -1.47, 990.39); - checkInst(model, 174, 174, LocalDate.of(2019, 6, 24), 29, 171, 50.00, 50.00, null, 0.83318560, 41.66, 1281.45, 1.42, 0.00, null, - -1.42, 990.39); - checkInst(model, 175, 175, LocalDate.of(2019, 6, 25), 28, 172, 50.00, 50.00, null, 0.83229686, 41.61, 1232.81, 1.37, 0.00, null, - -1.37, 990.39); - checkInst(model, 176, 176, LocalDate.of(2019, 6, 26), 27, 173, 50.00, 50.00, null, 0.83140907, 41.57, 1184.13, 1.32, 0.00, null, - -1.32, 990.39); - checkInst(model, 177, 177, LocalDate.of(2019, 6, 27), 26, 174, 50.00, 50.00, null, 0.83052222, 41.53, 1135.39, 1.26, 0.00, null, - -1.26, 990.39); - checkInst(model, 178, 178, LocalDate.of(2019, 6, 28), 25, 175, 50.00, 50.00, null, 0.82963633, 41.48, 1086.61, 1.21, 0.00, null, - -1.21, 990.39); - checkInst(model, 179, 179, LocalDate.of(2019, 6, 29), 24, 176, 50.00, 50.00, null, 0.82875137, 41.44, 1037.77, 1.16, 0.00, null, - -1.16, 990.39); - checkInst(model, 180, 180, LocalDate.of(2019, 6, 30), 23, 177, 50.00, 50.00, null, 0.82786736, 41.39, 988.88, 1.11, 0.00, null, - -1.11, 990.39); - checkInst(model, 181, 181, LocalDate.of(2019, 7, 1), 22, 178, 50.00, 50.00, null, 0.82698430, 41.35, 939.93, 1.06, 0.00, null, - -1.06, 990.39); - checkInst(model, 182, 182, LocalDate.of(2019, 7, 2), 21, 179, 50.00, 50.00, null, 0.82610217, 41.31, 890.93, 1.00, 0.00, null, - -1.00, 990.39); - checkInst(model, 183, 183, LocalDate.of(2019, 7, 3), 20, 180, 50.00, 50.00, null, 0.82522099, 41.26, 841.89, 0.95, 0.00, null, - -0.95, 990.39); - checkInst(model, 184, 184, LocalDate.of(2019, 7, 4), 19, 181, 50.00, 50.00, null, 0.82434075, 41.22, 792.79, 0.90, 0.00, null, - -0.90, 990.39); - checkInst(model, 185, 185, LocalDate.of(2019, 7, 5), 18, 182, 50.00, 50.00, null, 0.82346144, 41.17, 743.63, 0.85, 0.00, null, - -0.85, 990.39); - checkInst(model, 186, 186, LocalDate.of(2019, 7, 6), 17, 183, 50.00, 50.00, null, 0.82258308, 41.13, 694.43, 0.79, 0.00, null, - -0.79, 990.39); - checkInst(model, 187, 187, LocalDate.of(2019, 7, 7), 16, 184, 50.00, 50.00, null, 0.82170565, 41.09, 645.17, 0.74, 0.00, null, - -0.74, 990.39); - checkInst(model, 188, 188, LocalDate.of(2019, 7, 8), 15, 185, 50.00, 50.00, null, 0.82082916, 41.04, 595.86, 0.69, 0.00, null, - -0.69, 990.39); - checkInst(model, 189, 189, LocalDate.of(2019, 7, 9), 14, 186, 50.00, 50.00, null, 0.81995360, 41.00, 546.49, 0.64, 0.00, null, - -0.64, 990.39); - checkInst(model, 190, 190, LocalDate.of(2019, 7, 10), 13, 187, 50.00, 50.00, null, 0.81907897, 40.95, 497.08, 0.58, 0.00, null, - -0.58, 990.39); - checkInst(model, 191, 191, LocalDate.of(2019, 7, 11), 12, 188, 50.00, 50.00, null, 0.81820528, 40.91, 447.61, 0.53, 0.00, null, - -0.53, 990.39); - checkInst(model, 192, 192, LocalDate.of(2019, 7, 12), 11, 189, 50.00, 50.00, null, 0.81733252, 40.87, 398.08, 0.48, 0.00, null, - -0.48, 990.39); - checkInst(model, 193, 193, LocalDate.of(2019, 7, 13), 10, 190, 50.00, 50.00, null, 0.81646069, 40.82, 348.51, 0.43, 0.00, null, - -0.43, 990.39); - checkInst(model, 194, 194, LocalDate.of(2019, 7, 14), 9, 191, 50.00, 50.00, null, 0.81558979, 40.78, 298.88, 0.37, 0.00, null, - -0.37, 990.39); - checkInst(model, 195, 195, LocalDate.of(2019, 7, 15), 8, 192, 50.00, 50.00, null, 0.81471983, 40.74, 249.20, 0.32, 0.00, null, - -0.32, 990.39); - checkInst(model, 196, 196, LocalDate.of(2019, 7, 16), 7, 193, 50.00, 50.00, null, 0.81385078, 40.69, 199.47, 0.27, 0.00, null, - -0.27, 990.39); - checkInst(model, 197, 197, LocalDate.of(2019, 7, 17), 6, 194, 50.00, 50.00, null, 0.81298267, 40.65, 149.68, 0.21, 0.00, null, - -0.21, 990.39); - checkInst(model, 198, 198, LocalDate.of(2019, 7, 18), 5, 195, 50.00, 50.00, null, 0.81211548, 40.61, 99.84, 0.16, 0.00, null, -0.16, - 990.39); - checkInst(model, 199, 199, LocalDate.of(2019, 7, 19), 4, 196, 50.00, 50.00, null, 0.81124922, 40.56, 49.95, 0.11, 0.00, null, -0.11, - 990.39); - checkInst(model, 200, 200, LocalDate.of(2019, 7, 20), 3, 197, 50.00, 50.00, null, 0.81038388, 40.52, 0.00, 0.05, 0.00, null, -0.05, - 990.39); - - assertEquals(203, model.payments().size(), "disbursement + 200 regular + 2 additional"); - checkInst(model, 201, 201, LocalDate.of(2019, 7, 21), 2, 198, null, 50.00, null, 0.80951946, 40.48, null, null, 0.00, null, null, - null); - checkInst(model, 202, 202, LocalDate.of(2019, 7, 22), 1, 199, null, 50.00, null, 0.80865597, 40.43, null, null, 0.00, null, null, - null); + model.applyRateChange(new BigDecimal("0.15"), EXPECTED_DISBURSEMENT_DATE); + assertNotNull(model.rateSegments()); + assertFalse(model.rateSegments().isEmpty()); + + final LocalDate secondChangeDate = EXPECTED_DISBURSEMENT_DATE.plusDays(8); + model.applyRateChange(new BigDecimal("0.11"), secondChangeDate); + + assertTrue(model.effectiveTotalTerm() > 0, "effective total term should be positive"); } @Test - void testLessPayment_term10_originationFee50_netDisbursement450_pay40() { - final BigDecimal smallOriginationFee = new BigDecimal("50"); - final BigDecimal smallNetDisbursement = new BigDecimal("450"); - final ProjectedAmortizationScheduleModel initial = calculator.generateModel(smallOriginationFee, smallNetDisbursement, TPV, RATE, - DAY_COUNT, EXPECTED_DISBURSEMENT_DATE, MC, CURRENCY); - final ProjectedAmortizationScheduleModel model = calculator.addDisbursement(initial, smallOriginationFee, smallNetDisbursement, - EXPECTED_DISBURSEMENT_DATE); - - calculator.applyPayment(model, EXPECTED_DISBURSEMENT_DATE.plusDays(1), new BigDecimal("40")); + void testApplyRateChange_twiceWithDateGapAndPayment() { + final ProjectedAmortizationScheduleModel model = generateModel(); - assertEquals(12, model.payments().size(), "disbursement + 10 regular + 1 tail"); + model.applyPayment(EXPECTED_DISBURSEMENT_DATE.plusDays(1), new BigDecimal("500")); - checkInst(model, 0, 0, EXPECTED_DISBURSEMENT_DATE, 11, 0, -450.00, null, null, 1.00000000, -450.00, 450.00, null, null, null, null, - 50.00); + final LocalDate rateChangeDate = EXPECTED_DISBURSEMENT_DATE.plusDays(8); + model.applyRateChange(new BigDecimal("0.15"), rateChangeDate); - checkInst(model, 1, 1, LocalDate.of(2019, 1, 2), 10, 0, 50.00, 50.00, 40.00, 1.00000000, 40.00, 408.83, 8.83, 7.07, 7.07, -1.77, - 42.93); - checkInst(model, 2, 2, LocalDate.of(2019, 1, 3), 9, 1, 50.00, 50.00, null, 0.98074794, 49.04, 366.86, 8.03, 0.00, null, -8.03, - 42.93); - checkInst(model, 3, 3, LocalDate.of(2019, 1, 4), 8, 2, 50.00, 50.00, null, 0.96186652, 48.09, 324.06, 7.20, 0.00, null, -7.20, - 42.93); - checkInst(model, 4, 4, LocalDate.of(2019, 1, 5), 7, 3, 50.00, 50.00, null, 0.94334860, 47.17, 280.42, 6.36, 0.00, null, -6.36, - 42.93); - checkInst(model, 5, 5, LocalDate.of(2019, 1, 6), 6, 4, 50.00, 50.00, null, 0.92518720, 46.26, 235.93, 5.50, 0.00, null, -5.50, - 42.93); - checkInst(model, 6, 6, LocalDate.of(2019, 1, 7), 5, 5, 50.00, 50.00, null, 0.90737544, 45.37, 190.56, 4.63, 0.00, null, -4.63, - 42.93); - checkInst(model, 7, 7, LocalDate.of(2019, 1, 8), 4, 6, 50.00, 50.00, null, 0.88990659, 44.50, 144.30, 3.74, 0.00, null, -3.74, - 42.93); - checkInst(model, 8, 8, LocalDate.of(2019, 1, 9), 3, 7, 50.00, 50.00, null, 0.87277405, 43.64, 97.13, 2.83, 0.00, null, -2.83, - 42.93); - checkInst(model, 9, 9, LocalDate.of(2019, 1, 10), 2, 8, 50.00, 50.00, null, 0.85597135, 42.80, 49.04, 1.91, 0.00, null, -1.91, - 42.93); - checkInst(model, 10, 10, LocalDate.of(2019, 1, 11), 1, 9, 50.00, 50.00, null, 0.83949214, 41.97, 0.00, 0.96, 0.00, null, -0.96, - 42.93); - - checkInst(model, 11, 11, LocalDate.of(2019, 1, 12), 0, 10, null, 10.00, null, 0.82333018, 8.23, null, null, 0.00, null, null, null); + assertTrue(model.effectiveTotalTerm() > 0, "effective total term should be positive"); } @Test - void testExcessPayment_term10_originationFee50_netDisbursement450_pay110() { - final BigDecimal smallOriginationFee = new BigDecimal("50"); - final BigDecimal smallNetDisbursement = new BigDecimal("450"); - final ProjectedAmortizationScheduleModel initial = calculator.generateModel(smallOriginationFee, smallNetDisbursement, TPV, RATE, - DAY_COUNT, EXPECTED_DISBURSEMENT_DATE, MC, CURRENCY); - final ProjectedAmortizationScheduleModel model = calculator.addDisbursement(initial, smallOriginationFee, smallNetDisbursement, - EXPECTED_DISBURSEMENT_DATE); + void testApplyRateChange_nearEndOfTerm() { + final ProjectedAmortizationScheduleModel model = generateModel(); + final LocalDate rateChangeDate = EXPECTED_DISBURSEMENT_DATE.plusDays(195); + model.applyRateChange(new BigDecimal("0.15"), rateChangeDate); - calculator.applyPayment(model, EXPECTED_DISBURSEMENT_DATE.plusDays(1), new BigDecimal("110")); + assertTrue(model.effectiveTotalTerm() > 0, "effective total term should be positive"); + } - assertEquals(10, model.payments().size(), "disbursement + 9 regular (period 10 removed, forecast was 0)"); + @Test + void testApplyRateChange_pastEndOfTerm() { + final ProjectedAmortizationScheduleModel model = generateModel(); + final int originalTerm = model.originalPaymentNumber(); + final LocalDate rateChangeDate = EXPECTED_DISBURSEMENT_DATE.plusDays(250); - checkInst(model, 0, 0, EXPECTED_DISBURSEMENT_DATE, 11, 0, -450.00, null, null, 1.00000000, -450.00, 450.00, null, null, null, null, - 50.00); + model.applyRateChange(new BigDecimal("0.15"), rateChangeDate); - checkInst(model, 1, 1, LocalDate.of(2019, 1, 2), 10, 0, 50.00, 50.00, 110.00, 1.00000000, 110.00, 408.83, 8.83, 18.30, 18.30, 9.47, - 31.70); - checkInst(model, 2, 2, LocalDate.of(2019, 1, 3), 9, 1, 50.00, 50.00, null, 0.98074794, 49.04, 366.86, 8.03, 0.00, null, -8.03, - 31.70); - checkInst(model, 3, 3, LocalDate.of(2019, 1, 4), 8, 2, 50.00, 50.00, null, 0.96186652, 48.09, 324.06, 7.20, 0.00, null, -7.20, - 31.70); - checkInst(model, 4, 4, LocalDate.of(2019, 1, 5), 7, 3, 50.00, 50.00, null, 0.94334860, 47.17, 280.42, 6.36, 0.00, null, -6.36, - 31.70); - checkInst(model, 5, 5, LocalDate.of(2019, 1, 6), 6, 4, 50.00, 50.00, null, 0.92518720, 46.26, 235.93, 5.50, 0.00, null, -5.50, - 31.70); - checkInst(model, 6, 6, LocalDate.of(2019, 1, 7), 5, 5, 50.00, 50.00, null, 0.90737544, 45.37, 190.56, 4.63, 0.00, null, -4.63, - 31.70); - checkInst(model, 7, 7, LocalDate.of(2019, 1, 8), 4, 6, 50.00, 50.00, null, 0.88990659, 44.50, 144.30, 3.74, 0.00, null, -3.74, - 31.70); - checkInst(model, 8, 8, LocalDate.of(2019, 1, 9), 3, 7, 50.00, 50.00, null, 0.87277405, 43.64, 97.13, 2.83, 0.00, null, -2.83, - 31.70); - checkInst(model, 9, 9, LocalDate.of(2019, 1, 10), 2, 8, 50.00, 40.00, null, 0.85597135, 34.24, 49.04, 1.91, 0.00, null, -1.91, - 31.70); + // Past-term rate change should succeed — segment starts clamped at originalPaymentNumber + assertFalse(model.rateSegments().isEmpty(), "should have a rate segment"); + assertEquals(originalTerm, model.rateSegments().getFirst().startDayIndex(), + "segment should start at base originalPaymentNumber when past-term"); + assertTrue(model.effectiveTotalTerm() > originalTerm, "effective term should extend beyond base term"); + } + + @Test + void testApplyRateChange_beforeDisburseDate() { + final ProjectedAmortizationScheduleModel model = generateModel(); + assertThrows(IllegalArgumentException.class, () -> { + model.applyRateChange(new BigDecimal("0.15"), EXPECTED_DISBURSEMENT_DATE.minusDays(1)); + }); } private ProjectedAmortizationScheduleModel generateModel() { - final ProjectedAmortizationScheduleModel model = calculator.generateModel(ORIGINATION_FEE, NET_DISBURSEMENT, TPV, RATE, DAY_COUNT, + final ProjectedAmortizationScheduleModel model = calculator.generateModel(DISCOUNT_FEE, NET_DISBURSEMENT, TPV, RATE, DAY_COUNT, EXPECTED_DISBURSEMENT_DATE, MC, CURRENCY); - return calculator.addDisbursement(model, ORIGINATION_FEE, NET_DISBURSEMENT, EXPECTED_DISBURSEMENT_DATE); + return calculator.addDisbursement(model, DISCOUNT_FEE, NET_DISBURSEMENT, EXPECTED_DISBURSEMENT_DATE); } private void checkInst(final ProjectedAmortizationScheduleModel model, final int index, final int expectedNo, - final LocalDate expectedDate, final long expectedCount, final long expectedPaymentsLeft, final Double expectedPayment, + final LocalDate expectedDate, final long expectedPaymentsLeft, final Double expectedPayment, final Double expectedForecastPayment, final Double expectedActualPayment, final Double expectedDiscountFactor, final Double expectedNpvValue, final Double expectedBalance, final Double expectedAmortization, final Double expectedNetAmortization, final Double expectedActualAmortization, final Double expectedIncomeModification, final Double expectedDeferredBalance) { - final ProjectedPayment inst = model.payments().get(index); + final ProjectedPayment inst = model.projectedPayments().get(index); final String p = "inst " + expectedNo + ": "; assertEquals(expectedNo, inst.paymentNo(), p + "paymentNo"); assertEquals(expectedDate, inst.date(), p + "date"); - assertEquals(expectedCount, inst.count(), p + "count"); assertEquals(expectedPaymentsLeft, inst.paymentsLeft(), p + "paymentsLeft"); assertMoneyValue(expectedPayment, inst.expectedPaymentAmount(), 2, p + "expectedPayment"); assertMoneyValue(expectedForecastPayment, inst.forecastPaymentAmount(), 2, p + "forecastPayment"); @@ -2287,7 +2338,7 @@ private void checkInst(final ProjectedAmortizationScheduleModel model, final int assertMoneyValue(expectedNpvValue, inst.npvValue(), 2, p + "npvValue"); assertMoneyValue(expectedBalance, inst.balance(), 2, p + "balance"); assertMoneyValue(expectedAmortization, inst.expectedAmortizationAmount(), 2, p + "expectedAmortization"); - assertMoneyValue(expectedNetAmortization, inst.netAmortizationAmount(), 2, p + "netAmortization"); + assertMoneyValue(expectedNetAmortization, inst.totalAmortizedAmount(), 2, p + "netAmortization"); assertMoneyValue(expectedActualAmortization, inst.actualAmortizationAmount(), 2, p + "actualAmort"); assertMoneyValue(expectedIncomeModification, inst.incomeModification(), 2, p + "incomeModification"); assertMoneyValue(expectedDeferredBalance, inst.deferredBalance(), 2, p + "deferredBalance"); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/WorkingCapitalLoanDisbursementTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/WorkingCapitalLoanDisbursementTest.java index 2eb888e2d6b..9e33796fcc7 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/WorkingCapitalLoanDisbursementTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/WorkingCapitalLoanDisbursementTest.java @@ -1181,7 +1181,7 @@ public void testDisburseGeneratesAmortizationSchedule() { final JsonObject schedule = retrieveAmortizationScheduleByLoanId(loanId); assertDateEquals(disbursementDate, schedule.get("expectedDisbursementDate")); assertEqualBigDecimal(disbursementAmount, schedule.get("netDisbursementAmount")); - assertEqualBigDecimal(discountAmount, schedule.get("originationFeeAmount")); + assertEqualBigDecimal(discountAmount, schedule.get("discountFeeAmount")); assertTrue(schedule.has("payments") && schedule.get("payments").isJsonArray(), "Schedule should contain payments"); assertFalse(schedule.getAsJsonArray("payments").isEmpty(), "Schedule payments should not be empty after disburse"); } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/WorkingCapitalLoanRepaymentTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/WorkingCapitalLoanRepaymentTest.java index d2beb865e40..92b3816d2b5 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/WorkingCapitalLoanRepaymentTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/WorkingCapitalLoanRepaymentTest.java @@ -313,7 +313,7 @@ public void testRepaymentAmortizationMatchesProvidedReferenceSchedule() { } final JsonObject schedule = JsonParser.parseString(loanHelper.retrieveAmortizationScheduleByLoanIdRaw(loanId)).getAsJsonObject(); - assertEqualBigDecimal(BigDecimal.valueOf(1000), schedule.get("originationFeeAmount")); + assertEqualBigDecimal(BigDecimal.valueOf(1000), schedule.get("discountFeeAmount")); assertEqualBigDecimal(BigDecimal.valueOf(9000), schedule.get("netDisbursementAmount")); assertEqualBigDecimal(BigDecimal.valueOf(100000), schedule.get("totalPaymentValue")); assertEqualBigDecimal(new BigDecimal("0.18"), schedule.get("periodPaymentRate")); @@ -330,10 +330,9 @@ public void testRepaymentAmortizationMatchesProvidedReferenceSchedule() { final List expectedRows = buildExpectedScheduleRows(); assertEquals(expectedRows.size(), paymentByDate.size(), "Fixture and API schedule row count differ"); for (ExpectedScheduleRow row : expectedRows) { - assertScheduleRow(paymentByDate.get(row.paymentDate()), row.count(), row.paymentsLeft(), row.expectedPaymentAmount(), - row.forecastPaymentAmount(), row.actualPaymentAmount(), row.discountFactor(), row.npvValue(), row.balance(), - row.expectedAmortization(), row.netAmortization(), row.actualAmortization(), row.incomeModification(), - row.deferredBalance(), row.paymentDate().toString()); + assertScheduleRow(paymentByDate.get(row.paymentDate()), row.expectedPaymentAmount(), row.actualPaymentAmount(), + row.discountFactor(), row.npvValue(), row.balance(), row.expectedAmortization(), row.actualAmortization(), + row.incomeModification(), row.deferredBalance(), row.paymentDate().toString()); } } @@ -361,25 +360,19 @@ private static void assertEqualBigDecimal(final BigDecimal expected, final JsonE assertEquals(0, expected.compareTo(actual.getAsJsonPrimitive().getAsBigDecimal())); } - private static void assertScheduleRow(final JsonObject payment, final String expectedCount, final String expectedPaymentsLeft, - final String expectedPaymentAmount, final String expectedForecastPaymentAmount, final String expectedActualPaymentAmount, - final String expectedDiscountFactor, final String expectedNpvValue, final String expectedBalance, - final String expectedExpectedAmortization, final String expectedNetAmortization, final String expectedActualAmortization, + private static void assertScheduleRow(final JsonObject payment, final String expectedPaymentAmount, + final String expectedActualPaymentAmount, final String expectedDiscountFactor, final String expectedNpvValue, + final String expectedBalance, final String expectedExpectedAmortization, final String expectedActualAmortization, final String expectedIncomeModification, final String expectedDeferredBalance, final String rowDateLabel) { assertNotNull(payment, "Expected payment row to exist for date " + rowDateLabel); - assertEquals(Long.parseLong(expectedCount), payment.get("count").getAsLong(), "Mismatch for count at " + rowDateLabel); - assertEquals(Long.parseLong(expectedPaymentsLeft), payment.get("paymentsLeft").getAsLong(), - "Mismatch for paymentsLeft at " + rowDateLabel); assertAmount(payment, "expectedPaymentAmount", expectedPaymentAmount, rowDateLabel); - assertAmount(payment, "forecastPaymentAmount", expectedForecastPaymentAmount, rowDateLabel); assertAmountOrNull(payment, "actualPaymentAmount", expectedActualPaymentAmount, rowDateLabel); assertDiscountFactor(payment, expectedDiscountFactor, rowDateLabel); assertAmount(payment, "npvValue", expectedNpvValue, rowDateLabel); assertAmount(payment, "balance", expectedBalance, rowDateLabel); assertAmount(payment, "expectedAmortizationAmount", expectedExpectedAmortization, rowDateLabel); assertAmountOrNull(payment, "actualAmortizationAmount", expectedActualAmortization, rowDateLabel); - assertAmount(payment, "netAmortizationAmount", expectedNetAmortization, rowDateLabel); - assertAmount(payment, "incomeModification", expectedIncomeModification, rowDateLabel); + assertAmountOrNull(payment, "incomeModification", expectedIncomeModification, rowDateLabel); assertAmount(payment, "deferredBalance", expectedDeferredBalance, rowDateLabel); } @@ -423,423 +416,224 @@ private static LocalDate parseDate(final JsonElement dateElement) { } private static List buildExpectedScheduleRows() { - return List.of(expectedRow("1/1/2019", "203", "0", "-9000", null, null, "1", "-9000", "9000", null, null, null, null, "1000"), - expectedRow("1/2/2019", "202", "0", "50", "50", "50", "1", "50", "8959.61", "9.61", "28.70", "9.61", "0.00", "990.39"), - expectedRow("1/3/2019", "201", "0", "50", "50", "50", "1", "50", "8919.18", "9.57", "19.09", "9.57", "0.00", "980.82"), - expectedRow("1/4/2019", "200", "0", "50", "50", "50", "1", "50", "8878.70", "9.52", "9.52", "9.52", "0.00", "971.30"), - expectedRow("1/5/2019", "199", "1", "50", "50", null, "0.9989333245", "49.94666623", "8838.18", "9.48", "9.52", null, - "-9.48", "971.30"), - expectedRow("1/6/2019", "198", "2", "50", "50", null, "0.9978677868", "49.89338934", "8797.62", "9.44", "9.52", null, - "-9.44", "971.30"), - expectedRow("1/7/2019", "197", "3", "50", "50", null, "0.9968033857", "49.84016929", "8757.01", "9.39", "9.52", null, - "-9.39", "971.30"), - expectedRow("1/8/2019", "196", "4", "50", "50", null, "0.99574012", "49.787006", "8716.36", "9.35", "9.52", null, "-9.35", - "971.30"), - expectedRow("1/9/2019", "195", "5", "50", "50", null, "0.9946779885", "49.73389942", "8675.67", "9.31", "9.52", null, - "-9.31", "971.30"), - expectedRow("1/10/2019", "194", "6", "50", "50", null, "0.9936169898", "49.68084949", "8634.94", "9.26", "9.52", null, - "-9.26", "971.30"), - expectedRow("1/11/2019", "193", "7", "50", "50", null, "0.992557123", "49.62785615", "8594.16", "9.22", "9.52", null, - "-9.22", "971.30"), - expectedRow("1/12/2019", "192", "8", "50", "50", null, "0.9914983866", "49.57491933", "8553.33", "9.18", "9.52", null, - "-9.18", "971.30"), - expectedRow("1/13/2019", "191", "9", "50", "50", null, "0.9904407796", "49.52203898", "8512.47", "9.13", "9.52", null, - "-9.13", "971.30"), - expectedRow("1/14/2019", "190", "10", "50", "50", null, "0.9893843007", "49.46921504", "8471.56", "9.09", "9.52", null, - "-9.09", "971.30"), - expectedRow("1/15/2019", "189", "11", "50", "50", null, "0.9883289487", "49.41644744", "8430.60", "9.05", "9.52", null, - "-9.05", "971.30"), - expectedRow("1/16/2019", "188", "12", "50", "50", null, "0.9872747225", "49.36373612", "8389.61", "9.00", "9.52", null, - "-9.00", "971.30"), - expectedRow("1/17/2019", "187", "13", "50", "50", null, "0.9862216208", "49.31108104", "8348.56", "8.96", "9.52", null, - "-8.96", "971.30"), - expectedRow("1/18/2019", "186", "14", "50", "50", null, "0.9851696423", "49.25848212", "8307.48", "8.91", "9.52", null, - "-8.91", "971.30"), - expectedRow("1/19/2019", "185", "15", "50", "50", null, "0.984118786", "49.2059393", "8266.35", "8.87", "9.52", null, - "-8.87", "971.30"), - expectedRow("1/20/2019", "184", "16", "50", "50", null, "0.9830690507", "49.15345253", "8225.18", "8.83", "9.52", null, - "-8.83", "971.30"), - expectedRow("1/21/2019", "183", "17", "50", "50", null, "0.982020435", "49.10102175", "8183.96", "8.78", "9.52", null, - "-8.78", "971.30"), - expectedRow("1/22/2019", "182", "18", "50", "50", null, "0.9809729379", "49.0486469", "8142.70", "8.74", "9.52", null, - "-8.74", "971.30"), - expectedRow("1/23/2019", "181", "19", "50", "50", null, "0.9799265581", "48.99632791", "8101.39", "8.69", "9.52", null, - "-8.69", "971.30"), - expectedRow("1/24/2019", "180", "20", "50", "50", null, "0.9788812945", "48.94406473", "8060.04", "8.65", "9.52", null, - "-8.65", "971.30"), - expectedRow("1/25/2019", "179", "21", "50", "50", null, "0.9778371458", "48.89185729", "8018.65", "8.61", "9.52", null, - "-8.61", "971.30"), - expectedRow("1/26/2019", "178", "22", "50", "50", null, "0.9767941109", "48.83970555", "7977.21", "8.56", "9.52", null, - "-8.56", "971.30"), - expectedRow("1/27/2019", "177", "23", "50", "50", null, "0.9757521886", "48.78760943", "7935.73", "8.52", "9.52", null, - "-8.52", "971.30"), - expectedRow("1/28/2019", "176", "24", "50", "50", null, "0.9747113777", "48.73556888", "7894.21", "8.47", "9.52", null, - "-8.47", "971.30"), - expectedRow("1/29/2019", "175", "25", "50", "50", null, "0.973671677", "48.68358385", "7852.63", "8.43", "9.52", null, - "-8.43", "971.30"), - expectedRow("1/30/2019", "174", "26", "50", "50", null, "0.9726330853", "48.63165426", "7811.02", "8.39", "9.52", null, - "-8.39", "971.30"), - expectedRow("1/31/2019", "173", "27", "50", "50", null, "0.9715956014", "48.57978007", "7769.36", "8.34", "9.52", null, - "-8.34", "971.30"), - expectedRow("2/1/2019", "172", "28", "50", "50", null, "0.9705592242", "48.52796121", "7727.66", "8.30", "9.52", null, - "-8.30", "971.30"), - expectedRow("2/2/2019", "171", "29", "50", "50", null, "0.9695239525", "48.47619762", "7685.91", "8.25", "9.52", null, - "-8.25", "971.30"), - expectedRow("2/3/2019", "170", "30", "50", "50", null, "0.968489785", "48.42448925", "7644.12", "8.21", "9.52", null, - "-8.21", "971.30"), - expectedRow("2/4/2019", "169", "31", "50", "50", null, "0.9674567207", "48.37283604", "7602.28", "8.16", "9.52", null, - "-8.16", "971.30"), - expectedRow("2/5/2019", "168", "32", "50", "50", null, "0.9664247584", "48.32123792", "7560.40", "8.12", "9.52", null, - "-8.12", "971.30"), - expectedRow("2/6/2019", "167", "33", "50", "50", null, "0.9653938968", "48.26969484", "7518.47", "8.07", "9.52", null, - "-8.07", "971.30"), - expectedRow("2/7/2019", "166", "34", "50", "50", null, "0.9643641348", "48.21820674", "7476.50", "8.03", "9.52", null, - "-8.03", "971.30"), - expectedRow("2/8/2019", "165", "35", "50", "50", null, "0.9633354712", "48.16677356", "7434.48", "7.98", "9.52", null, - "-7.98", "971.30"), - expectedRow("2/9/2019", "164", "36", "50", "50", null, "0.9623079049", "48.11539525", "7392.42", "7.94", "9.52", null, - "-7.94", "971.30"), - expectedRow("2/10/2019", "163", "37", "50", "50", null, "0.9612814347", "48.06407173", "7350.31", "7.89", "9.52", null, - "-7.89", "971.30"), - expectedRow("2/11/2019", "162", "38", "50", "50", null, "0.9602560593", "48.01280297", "7308.16", "7.85", "9.52", null, - "-7.85", "971.30"), - expectedRow("2/12/2019", "161", "39", "50", "50", null, "0.9592317777", "47.96158889", "7265.97", "7.80", "9.52", null, - "-7.80", "971.30"), - expectedRow("2/13/2019", "160", "40", "50", "50", null, "0.9582085887", "47.91042944", "7223.72", "7.76", "9.52", null, - "-7.76", "971.30"), - expectedRow("2/14/2019", "159", "41", "50", "50", null, "0.9571864911", "47.85932456", "7181.44", "7.71", "9.52", null, - "-7.71", "971.30"), - expectedRow("2/15/2019", "158", "42", "50", "50", null, "0.9561654838", "47.80827419", "7139.11", "7.67", "9.52", null, - "-7.67", "971.30"), - expectedRow("2/16/2019", "157", "43", "50", "50", null, "0.9551455655", "47.75727827", "7096.73", "7.62", "9.52", null, - "-7.62", "971.30"), - expectedRow("2/17/2019", "156", "44", "50", "50", null, "0.9541267351", "47.70633676", "7054.31", "7.58", "9.52", null, - "-7.58", "971.30"), - expectedRow("2/18/2019", "155", "45", "50", "50", null, "0.9531089916", "47.65544958", "7011.84", "7.53", "9.52", null, - "-7.53", "971.30"), - expectedRow("2/19/2019", "154", "46", "50", "50", null, "0.9520923336", "47.60461668", "6969.33", "7.49", "9.52", null, - "-7.49", "971.30"), - expectedRow("2/20/2019", "153", "47", "50", "50", null, "0.95107676", "47.553838", "6926.77", "7.44", "9.52", null, "-7.44", - "971.30"), - expectedRow("2/21/2019", "152", "48", "50", "50", null, "0.9500622698", "47.50311349", "6884.17", "7.40", "9.52", null, - "-7.40", "971.30"), - expectedRow("2/22/2019", "151", "49", "50", "50", null, "0.9490488616", "47.45244308", "6841.52", "7.35", "9.52", null, - "-7.35", "971.30"), - expectedRow("2/23/2019", "150", "50", "50", "50", null, "0.9480365345", "47.40182672", "6798.82", "7.31", "9.52", null, - "-7.31", "971.30"), - expectedRow("2/24/2019", "149", "51", "50", "50", null, "0.9470252872", "47.35126436", "6756.08", "7.26", "9.52", null, - "-7.26", "971.30"), - expectedRow("2/25/2019", "148", "52", "50", "50", null, "0.9460151185", "47.30075593", "6713.30", "7.21", "9.52", null, - "-7.21", "971.30"), - expectedRow("2/26/2019", "147", "53", "50", "50", null, "0.9450060274", "47.25030137", "6670.47", "7.17", "9.52", null, - "-7.17", "971.30"), - expectedRow("2/27/2019", "146", "54", "50", "50", null, "0.9439980126", "47.19990063", "6627.59", "7.12", "9.52", null, - "-7.12", "971.30"), - expectedRow("2/28/2019", "145", "55", "50", "50", null, "0.9429910731", "47.14955366", "6584.67", "7.08", "9.52", null, - "-7.08", "971.30"), - expectedRow("3/1/2019", "144", "56", "50", "50", null, "0.9419852077", "47.09926038", "6541.70", "7.03", "9.52", null, - "-7.03", "971.30"), - expectedRow("3/2/2019", "143", "57", "50", "50", null, "0.9409804151", "47.04902076", "6498.68", "6.99", "9.52", null, - "-6.99", "971.30"), - expectedRow("3/3/2019", "142", "58", "50", "50", null, "0.9399766944", "46.99883472", "6455.62", "6.94", "9.52", null, - "-6.94", "971.30"), - expectedRow("3/4/2019", "141", "59", "50", "50", null, "0.9389740443", "46.94870222", "6412.51", "6.89", "9.52", null, - "-6.89", "971.30"), - expectedRow("3/5/2019", "140", "60", "50", "50", null, "0.9379724637", "46.89862319", "6369.36", "6.85", "9.52", null, - "-6.85", "971.30"), - expectedRow("3/6/2019", "139", "61", "50", "50", null, "0.9369719515", "46.84859758", "6326.16", "6.80", "9.52", null, - "-6.80", "971.30"), - expectedRow("3/7/2019", "138", "62", "50", "50", null, "0.9359725065", "46.79862533", "6282.92", "6.76", "9.52", null, - "-6.76", "971.30"), - expectedRow("3/8/2019", "137", "63", "50", "50", null, "0.9349741276", "46.74870638", "6239.63", "6.71", "9.52", null, - "-6.71", "971.30"), - expectedRow("3/9/2019", "136", "64", "50", "50", null, "0.9339768136", "46.69884068", "6196.29", "6.66", "9.52", null, - "-6.66", "971.30"), - expectedRow("3/10/2019", "135", "65", "50", "50", null, "0.9329805635", "46.64902817", "6152.91", "6.62", "9.52", null, - "-6.62", "971.30"), - expectedRow("3/11/2019", "134", "66", "50", "50", null, "0.931985376", "46.5992688", "6109.48", "6.57", "9.52", null, - "-6.57", "971.30"), - expectedRow("3/12/2019", "133", "67", "50", "50", null, "0.93099125", "46.5495625", "6066.00", "6.52", "9.52", null, - "-6.52", "971.30"), - expectedRow("3/13/2019", "132", "68", "50", "50", null, "0.9299981845", "46.49990922", "6022.48", "6.48", "9.52", null, - "-6.48", "971.30"), - expectedRow("3/14/2019", "131", "69", "50", "50", null, "0.9290061782", "46.45030891", "5978.91", "6.43", "9.52", null, - "-6.43", "971.30"), - expectedRow("3/15/2019", "130", "70", "50", "50", null, "0.9280152301", "46.40076151", "5935.29", "6.38", "9.52", null, - "-6.38", "971.30"), - expectedRow("3/16/2019", "129", "71", "50", "50", null, "0.927025339", "46.35126695", "5891.63", "6.34", "9.52", null, - "-6.34", "971.30"), - expectedRow("3/17/2019", "128", "72", "50", "50", null, "0.9260365038", "46.30182519", "5847.92", "6.29", "9.52", null, - "-6.29", "971.30"), - expectedRow("3/18/2019", "127", "73", "50", "50", null, "0.9250487234", "46.25243617", "5804.17", "6.24", "9.52", null, - "-6.24", "971.30"), - expectedRow("3/19/2019", "126", "74", "50", "50", null, "0.9240619966", "46.20309983", "5760.36", "6.20", "9.52", null, - "-6.20", "971.30"), - expectedRow("3/20/2019", "125", "75", "50", "50", null, "0.9230763224", "46.15381612", "5716.52", "6.15", "9.52", null, - "-6.15", "971.30"), - expectedRow("3/21/2019", "124", "76", "50", "50", null, "0.9220916995", "46.10458497", "5672.62", "6.10", "9.52", null, - "-6.10", "971.30"), - expectedRow("3/22/2019", "123", "77", "50", "50", null, "0.9211081269", "46.05540634", "5628.68", "6.06", "9.52", null, - "-6.06", "971.30"), - expectedRow("3/23/2019", "122", "78", "50", "50", null, "0.9201256034", "46.00628017", "5584.69", "6.01", "9.52", null, - "-6.01", "971.30"), - expectedRow("3/24/2019", "121", "79", "50", "50", null, "0.919144128", "45.9572064", "5540.65", "5.96", "9.52", null, - "-5.96", "971.30"), - expectedRow("3/25/2019", "120", "80", "50", "50", null, "0.9181636995", "45.90818498", "5496.57", "5.92", "9.52", null, - "-5.92", "971.30"), - expectedRow("3/26/2019", "119", "81", "50", "50", null, "0.9171843168", "45.85921584", "5452.44", "5.87", "9.52", null, - "-5.87", "971.30"), - expectedRow("3/27/2019", "118", "82", "50", "50", null, "0.9162059788", "45.81029894", "5408.26", "5.82", "9.52", null, - "-5.82", "971.30"), - expectedRow("3/28/2019", "117", "83", "50", "50", null, "0.9152286843", "45.76143422", "5364.03", "5.78", "9.52", null, - "-5.78", "971.30"), - expectedRow("3/29/2019", "116", "84", "50", "50", null, "0.9142524323", "45.71262162", "5319.76", "5.73", "9.52", null, - "-5.73", "971.30"), - expectedRow("3/30/2019", "115", "85", "50", "50", null, "0.9132772217", "45.66386108", "5275.44", "5.68", "9.52", null, - "-5.68", "971.30"), - expectedRow("3/31/2019", "114", "86", "50", "50", null, "0.9123030513", "45.61515256", "5231.08", "5.63", "9.52", null, - "-5.63", "971.30"), - expectedRow("4/1/2019", "113", "87", "50", "50", null, "0.91132992", "45.566496", "5186.66", "5.59", "9.52", null, "-5.59", - "971.30"), - expectedRow("4/2/2019", "112", "88", "50", "50", null, "0.9103578267", "45.51789134", "5142.20", "5.54", "9.52", null, - "-5.54", "971.30"), - expectedRow("4/3/2019", "111", "89", "50", "50", null, "0.9093867703", "45.46933852", "5097.69", "5.49", "9.52", null, - "-5.49", "971.30"), - expectedRow("4/4/2019", "110", "90", "50", "50", null, "0.9084167498", "45.42083749", "5053.13", "5.44", "9.52", null, - "-5.44", "971.30"), - expectedRow("4/5/2019", "109", "91", "50", "50", null, "0.9074477639", "45.3723882", "5008.53", "5.40", "9.52", null, - "-5.40", "971.30"), - expectedRow("4/6/2019", "108", "92", "50", "50", null, "0.9064798116", "45.32399058", "4963.88", "5.35", "9.52", null, - "-5.35", "971.30"), - expectedRow("4/7/2019", "107", "93", "50", "50", null, "0.9055128918", "45.27564459", "4919.18", "5.30", "9.52", null, - "-5.30", "971.30"), - expectedRow("4/8/2019", "106", "94", "50", "50", null, "0.9045470035", "45.22735017", "4874.43", "5.25", "9.52", null, - "-5.25", "971.30"), - expectedRow("4/9/2019", "105", "95", "50", "50", null, "0.9035821453", "45.17910727", "4829.64", "5.20", "9.52", null, - "-5.20", "971.30"), - expectedRow("4/10/2019", "104", "96", "50", "50", null, "0.9026183164", "45.13091582", "4784.79", "5.16", "9.52", null, - "-5.16", "971.30"), - expectedRow("4/11/2019", "103", "97", "50", "50", null, "0.9016555156", "45.08277578", "4739.90", "5.11", "9.52", null, - "-5.11", "971.30"), - expectedRow("4/12/2019", "102", "98", "50", "50", null, "0.9006937418", "45.03468709", "4694.96", "5.06", "9.52", null, - "-5.06", "971.30"), - expectedRow("4/13/2019", "101", "99", "50", "50", null, "0.8997329939", "44.98664969", "4649.98", "5.01", "9.52", null, - "-5.01", "971.30"), - expectedRow("4/14/2019", "100", "100", "50", "50", null, "0.8987732707", "44.93866354", "4604.94", "4.97", "9.52", null, - "-4.97", "971.30"), - expectedRow("4/15/2019", "99", "101", "50", "50", null, "0.8978145713", "44.89072857", "4559.86", "4.92", "9.52", null, - "-4.92", "971.30"), - expectedRow("4/16/2019", "98", "102", "50", "50", null, "0.8968568945", "44.84284473", "4514.73", "4.87", "9.52", null, - "-4.87", "971.30"), - expectedRow("4/17/2019", "97", "103", "50", "50", null, "0.8959002393", "44.79501196", "4469.55", "4.82", "9.52", null, - "-4.82", "971.30"), - expectedRow("4/18/2019", "96", "104", "50", "50", null, "0.8949446045", "44.74723022", "4424.32", "4.77", "9.52", null, - "-4.77", "971.30"), - expectedRow("4/19/2019", "95", "105", "50", "50", null, "0.893989989", "44.69949945", "4379.05", "4.72", "9.52", null, - "-4.72", "971.30"), - expectedRow("4/20/2019", "94", "106", "50", "50", null, "0.8930363918", "44.65181959", "4333.72", "4.68", "9.52", null, - "-4.68", "971.30"), - expectedRow("4/21/2019", "93", "107", "50", "50", null, "0.8920838118", "44.60419059", "4288.35", "4.63", "9.52", null, - "-4.63", "971.30"), - expectedRow("4/22/2019", "92", "108", "50", "50", null, "0.8911322479", "44.55661239", "4242.93", "4.58", "9.52", null, - "-4.58", "971.30"), - expectedRow("4/23/2019", "91", "109", "50", "50", null, "0.890181699", "44.50908495", "4197.46", "4.53", "9.52", null, - "-4.53", "971.30"), - expectedRow("4/24/2019", "90", "110", "50", "50", null, "0.889232164", "44.4616082", "4151.94", "4.48", "9.52", null, - "-4.48", "971.30"), - expectedRow("4/25/2019", "89", "111", "50", "50", null, "0.8882836418", "44.41418209", "4106.38", "4.43", "9.52", null, - "-4.43", "971.30"), - expectedRow("4/26/2019", "88", "112", "50", "50", null, "0.8873361315", "44.36680657", "4060.76", "4.38", "9.52", null, - "-4.38", "971.30"), - expectedRow("4/27/2019", "87", "113", "50", "50", null, "0.8863896318", "44.31948159", "4015.10", "4.34", "9.52", null, - "-4.34", "971.30"), - expectedRow("4/28/2019", "86", "114", "50", "50", null, "0.8854441417", "44.27220708", "3969.38", "4.29", "9.52", null, - "-4.29", "971.30"), - expectedRow("4/29/2019", "85", "115", "50", "50", null, "0.8844996601", "44.22498301", "3923.62", "4.24", "9.52", null, - "-4.24", "971.30"), - expectedRow("4/30/2019", "84", "116", "50", "50", null, "0.883556186", "44.1778093", "3877.81", "4.19", "9.52", null, - "-4.19", "971.30"), - expectedRow("5/1/2019", "83", "117", "50", "50", null, "0.8826137183", "44.13068592", "3831.95", "4.14", "9.52", null, - "-4.14", "971.30"), - expectedRow("5/2/2019", "82", "118", "50", "50", null, "0.8816722559", "44.0836128", "3786.04", "4.09", "9.52", null, - "-4.09", "971.30"), - expectedRow("5/3/2019", "81", "119", "50", "50", null, "0.8807317977", "44.03658989", "3740.09", "4.04", "9.52", null, - "-4.04", "971.30"), - expectedRow("5/4/2019", "80", "120", "50", "50", null, "0.8797923427", "43.98961714", "3694.08", "3.99", "9.52", null, - "-3.99", "971.30"), - expectedRow("5/5/2019", "79", "121", "50", "50", null, "0.8788538898", "43.94269449", "3648.03", "3.94", "9.52", null, - "-3.94", "971.30"), - expectedRow("5/6/2019", "78", "122", "50", "50", null, "0.8779164379", "43.8958219", "3601.92", "3.90", "9.52", null, - "-3.90", "971.30"), - expectedRow("5/7/2019", "77", "123", "50", "50", null, "0.876979986", "43.8489993", "3555.77", "3.85", "9.52", null, - "-3.85", "971.30"), - expectedRow("5/8/2019", "76", "124", "50", "50", null, "0.8760445329", "43.80222665", "3509.56", "3.80", "9.52", null, - "-3.80", "971.30"), - expectedRow("5/9/2019", "75", "125", "50", "50", null, "0.8751100777", "43.75550389", "3463.31", "3.75", "9.52", null, - "-3.75", "971.30"), - expectedRow("5/10/2019", "74", "126", "50", "50", null, "0.8741766193", "43.70883096", "3417.01", "3.70", "9.52", null, - "-3.70", "971.30"), - expectedRow("5/11/2019", "73", "127", "50", "50", null, "0.8732441565", "43.66220782", "3370.66", "3.65", "9.52", null, - "-3.65", "971.30"), - expectedRow("5/12/2019", "72", "128", "50", "50", null, "0.8723126884", "43.61563442", "3324.26", "3.60", "9.52", null, - "-3.60", "971.30"), - expectedRow("5/13/2019", "71", "129", "50", "50", null, "0.8713822138", "43.56911069", "3277.81", "3.55", "9.52", null, - "-3.55", "971.30"), - expectedRow("5/14/2019", "70", "130", "50", "50", null, "0.8704527318", "43.52263659", "3231.31", "3.50", "9.52", null, - "-3.50", "971.30"), - expectedRow("5/15/2019", "69", "131", "50", "50", null, "0.8695242412", "43.47621206", "3184.76", "3.45", "9.52", null, - "-3.45", "971.30"), - expectedRow("5/16/2019", "68", "132", "50", "50", null, "0.868596741", "43.42983705", "3138.16", "3.40", "9.52", null, - "-3.40", "971.30"), - expectedRow("5/17/2019", "67", "133", "50", "50", null, "0.8676702302", "43.38351151", "3091.51", "3.35", "9.52", null, - "-3.35", "971.30"), - expectedRow("5/18/2019", "66", "134", "50", "50", null, "0.8667447076", "43.33723538", "3044.81", "3.30", "9.52", null, - "-3.30", "971.30"), - expectedRow("5/19/2019", "65", "135", "50", "50", null, "0.8658201723", "43.29100861", "2998.06", "3.25", "9.52", null, - "-3.25", "971.30"), - expectedRow("5/20/2019", "64", "136", "50", "50", null, "0.8648966231", "43.24483116", "2951.26", "3.20", "9.52", null, - "-3.20", "971.30"), - expectedRow("5/21/2019", "63", "137", "50", "50", null, "0.8639740591", "43.19870296", "2904.42", "3.15", "9.52", null, - "-3.15", "971.30"), - expectedRow("5/22/2019", "62", "138", "50", "50", null, "0.8630524792", "43.15262396", "2857.52", "3.10", "9.52", null, - "-3.10", "971.30"), - expectedRow("5/23/2019", "61", "139", "50", "50", null, "0.8621318823", "43.10659411", "2810.57", "3.05", "9.52", null, - "-3.05", "971.30"), - expectedRow("5/24/2019", "60", "140", "50", "50", null, "0.8612122673", "43.06061337", "2763.57", "3.00", "9.52", null, - "-3.00", "971.30"), - expectedRow("5/25/2019", "59", "141", "50", "50", null, "0.8602936333", "43.01468167", "2716.52", "2.95", "9.52", null, - "-2.95", "971.30"), - expectedRow("5/26/2019", "58", "142", "50", "50", null, "0.8593759792", "42.96879896", "2669.42", "2.90", "9.52", null, - "-2.90", "971.30"), - expectedRow("5/27/2019", "57", "143", "50", "50", null, "0.8584593039", "42.9229652", "2622.27", "2.85", "9.52", null, - "-2.85", "971.30"), - expectedRow("5/28/2019", "56", "144", "50", "50", null, "0.8575436064", "42.87718032", "2575.07", "2.80", "9.52", null, - "-2.80", "971.30"), - expectedRow("5/29/2019", "55", "145", "50", "50", null, "0.8566288857", "42.83144429", "2527.82", "2.75", "9.52", null, - "-2.75", "971.30"), - expectedRow("5/30/2019", "54", "146", "50", "50", null, "0.8557151407", "42.78575703", "2480.52", "2.70", "9.52", null, - "-2.70", "971.30"), - expectedRow("5/31/2019", "53", "147", "50", "50", null, "0.8548023703", "42.74011852", "2433.17", "2.65", "9.52", null, - "-2.65", "971.30"), - expectedRow("6/1/2019", "52", "148", "50", "50", null, "0.8538905736", "42.69452868", "2385.77", "2.60", "9.52", null, - "-2.60", "971.30"), - expectedRow("6/2/2019", "51", "149", "50", "50", null, "0.8529797495", "42.64898747", "2338.31", "2.55", "9.52", null, - "-2.55", "971.30"), - expectedRow("6/3/2019", "50", "150", "50", "50", null, "0.8520698969", "42.60349484", "2290.81", "2.50", "9.52", null, - "-2.50", "971.30"), - expectedRow("6/4/2019", "49", "151", "50", "50", null, "0.8511610148", "42.55805074", "2243.26", "2.45", "9.52", null, - "-2.45", "971.30"), - expectedRow("6/5/2019", "48", "152", "50", "50", null, "0.8502531022", "42.51265511", "2195.65", "2.40", "9.52", null, - "-2.40", "971.30"), - expectedRow("6/6/2019", "47", "153", "50", "50", null, "0.8493461581", "42.46730791", "2148.00", "2.34", "9.52", null, - "-2.34", "971.30"), - expectedRow("6/7/2019", "46", "154", "50", "50", null, "0.8484401814", "42.42200907", "2100.29", "2.29", "9.52", null, - "-2.29", "971.30"), - expectedRow("6/8/2019", "45", "155", "50", "50", null, "0.8475351711", "42.37675855", "2052.53", "2.24", "9.52", null, - "-2.24", "971.30"), - expectedRow("6/9/2019", "44", "156", "50", "50", null, "0.8466311261", "42.3315563", "2004.73", "2.19", "9.52", null, - "-2.19", "971.30"), - expectedRow("6/10/2019", "43", "157", "50", "50", null, "0.8457280454", "42.28640227", "1956.87", "2.14", "9.52", null, - "-2.14", "971.30"), - expectedRow("6/11/2019", "42", "158", "50", "50", null, "0.844825928", "42.2412964", "1908.96", "2.09", "9.52", null, - "-2.09", "971.30"), - expectedRow("6/12/2019", "41", "159", "50", "50", null, "0.8439247729", "42.19623865", "1860.99", "2.04", "9.52", null, - "-2.04", "971.30"), - expectedRow("6/13/2019", "40", "160", "50", "50", null, "0.8430245791", "42.15122895", "1812.98", "1.99", "9.52", null, - "-1.99", "971.30"), - expectedRow("6/14/2019", "39", "161", "50", "50", null, "0.8421253454", "42.10626727", "1764.92", "1.94", "9.52", null, - "-1.94", "971.30"), - expectedRow("6/15/2019", "38", "162", "50", "50", null, "0.841227071", "42.06135355", "1716.80", "1.88", "9.52", null, - "-1.88", "971.30"), - expectedRow("6/16/2019", "37", "163", "50", "50", null, "0.8403297547", "42.01648774", "1668.64", "1.83", "9.52", null, - "-1.83", "971.30"), - expectedRow("6/17/2019", "36", "164", "50", "50", null, "0.8394333956", "41.97166978", "1620.42", "1.78", "9.52", null, - "-1.78", "971.30"), - expectedRow("6/18/2019", "35", "165", "50", "50", null, "0.8385379925", "41.92689963", "1572.15", "1.73", "9.52", null, - "-1.73", "971.30"), - expectedRow("6/19/2019", "34", "166", "50", "50", null, "0.8376435446", "41.88217723", "1523.83", "1.68", "9.52", null, - "-1.68", "971.30"), - expectedRow("6/20/2019", "33", "167", "50", "50", null, "0.8367500508", "41.83750254", "1475.45", "1.63", "9.52", null, - "-1.63", "971.30"), - expectedRow("6/21/2019", "32", "168", "50", "50", null, "0.83585751", "41.7928755", "1427.03", "1.58", "9.52", null, - "-1.58", "971.30"), - expectedRow("6/22/2019", "31", "169", "50", "50", null, "0.8349659213", "41.74829607", "1378.55", "1.52", "9.52", null, - "-1.52", "971.30"), - expectedRow("6/23/2019", "30", "170", "50", "50", null, "0.8340752837", "41.70376418", "1330.02", "1.47", "9.52", null, - "-1.47", "971.30"), - expectedRow("6/24/2019", "29", "171", "50", "50", null, "0.833185596", "41.6592798", "1281.45", "1.42", "9.52", null, - "-1.42", "971.30"), - expectedRow("6/25/2019", "28", "172", "50", "50", null, "0.8322968574", "41.61484287", "1232.81", "1.37", "9.52", null, - "-1.37", "971.30"), - expectedRow("6/26/2019", "27", "173", "50", "50", null, "0.8314090667", "41.57045334", "1184.13", "1.32", "9.52", null, - "-1.32", "971.30"), - expectedRow("6/27/2019", "26", "174", "50", "50", null, "0.8305222231", "41.52611115", "1135.39", "1.26", "9.52", null, - "-1.26", "971.30"), - expectedRow("6/28/2019", "25", "175", "50", "50", null, "0.8296363254", "41.48181627", "1086.61", "1.21", "9.52", null, - "-1.21", "971.30"), - expectedRow("6/29/2019", "24", "176", "50", "50", null, "0.8287513727", "41.43756863", "1037.77", "1.16", "9.52", null, - "-1.16", "971.30"), - expectedRow("6/30/2019", "23", "177", "50", "50", null, "0.8278673639", "41.39336819", "988.88", "1.11", "9.52", null, - "-1.11", "971.30"), - expectedRow("7/1/2019", "22", "178", "50", "50", null, "0.8269842981", "41.3492149", "939.93", "1.06", "9.52", null, - "-1.06", "971.30"), - expectedRow("7/2/2019", "21", "179", "50", "50", null, "0.8261021742", "41.30510871", "890.93", "1.00", "9.52", null, - "-1.00", "971.30"), - expectedRow("7/3/2019", "20", "180", "50", "50", null, "0.8252209913", "41.26104956", "841.89", "0.95", "9.52", null, - "-0.95", "971.30"), - expectedRow("7/4/2019", "19", "181", "50", "50", null, "0.8243407483", "41.21703741", "792.79", "0.90", "9.52", null, - "-0.90", "971.30"), - expectedRow("7/5/2019", "18", "182", "50", "50", null, "0.8234614442", "41.17307221", "743.63", "0.85", "9.52", null, - "-0.85", "971.30"), - expectedRow("7/6/2019", "17", "183", "50", "50", null, "0.8225830781", "41.1291539", "694.43", "0.79", "9.52", null, - "-0.79", "971.30"), - expectedRow("7/7/2019", "16", "184", "50", "50", null, "0.8217056489", "41.08528244", "645.17", "0.74", "9.52", null, - "-0.74", "971.30"), - expectedRow("7/8/2019", "15", "185", "50", "50", null, "0.8208291556", "41.04145778", "595.86", "0.69", "9.52", null, - "-0.69", "971.30"), - expectedRow("7/9/2019", "14", "186", "50", "50", null, "0.8199535973", "40.99767987", "546.49", "0.64", "9.52", null, - "-0.64", "971.30"), - expectedRow("7/10/2019", "13", "187", "50", "50", null, "0.8190789729", "40.95394865", "497.08", "0.58", "9.52", null, - "-0.58", "971.30"), - expectedRow("7/11/2019", "12", "188", "50", "50", null, "0.8182052815", "40.91026407", "447.61", "0.53", "9.52", null, - "-0.53", "971.30"), - expectedRow("7/12/2019", "11", "189", "50", "50", null, "0.8173325219", "40.8666261", "398.08", "0.48", "9.52", null, - "-0.48", "971.30"), - expectedRow("7/13/2019", "10", "190", "50", "50", null, "0.8164606934", "40.82303467", "348.51", "0.43", "9.52", null, - "-0.43", "971.30"), - expectedRow("7/14/2019", "9", "191", "50", "50", null, "0.8155897948", "40.77948974", "298.88", "0.37", "9.52", null, - "-0.37", "971.30"), - expectedRow("7/15/2019", "8", "192", "50", "50", null, "0.8147198252", "40.73599126", "249.20", "0.32", "9.52", null, - "-0.32", "971.30"), - expectedRow("7/16/2019", "7", "193", "50", "50", null, "0.8138507835", "40.69253918", "199.47", "0.27", "9.52", null, - "-0.27", "971.30"), - expectedRow("7/17/2019", "6", "194", "50", "50", null, "0.8129826688", "40.64913344", "149.68", "0.21", "9.52", null, - "-0.21", "971.30"), - expectedRow("7/18/2019", "5", "195", "50", "50", null, "0.8121154802", "40.60577401", "99.84", "0.16", "9.52", null, - "-0.16", "971.30"), - expectedRow("7/19/2019", "4", "196", "50", "50", null, "0.8112492165", "40.56246082", "49.95", "0.11", "9.52", null, - "-0.11", "971.30"), - expectedRow("7/20/2019", "3", "197", "50", "50", null, "0.8103838768", "40.51919384", "0.00", "0.05", "9.52", null, "-0.05", - "971.30")); - } - - private static ExpectedScheduleRow expectedRow(final String paymentDate, final String count, final String paymentsLeft, - final String expectedPaymentAmount, final String forecastPaymentAmount, final String actualPaymentAmount, - final String discountFactor, final String npvValue, final String balance, final String expectedAmortization, - final String netAmortization, final String actualAmortization, final String incomeModification, final String deferredBalance) { - return new ExpectedScheduleRow(parseDateString(paymentDate), count, paymentsLeft, expectedPaymentAmount, forecastPaymentAmount, - actualPaymentAmount, discountFactor, npvValue, balance, expectedAmortization, netAmortization, actualAmortization, - incomeModification, deferredBalance); + return List.of(expectedRow("1/1/2019", "-9000", null, "1", "-9000", "9000", null, null, null, "1000"), + expectedRow("1/2/2019", "50", "50", "1", "50", "8959.61", "9.61", "9.61", "0.00", "990.39"), + expectedRow("1/3/2019", "50", "50", "1", "50", "8919.18", "9.57", "9.57", "0.00", "980.82"), + expectedRow("1/4/2019", "50", "50", "1", "50", "8878.70", "9.52", "9.52", "0.00", "971.30"), + expectedRow("1/5/2019", "50", null, "0.9989333245", "49.94666623", "8838.18", "9.48", null, null, "971.30"), + expectedRow("1/6/2019", "50", null, "0.9978677868", "49.89338934", "8797.62", "9.44", null, null, "971.30"), + expectedRow("1/7/2019", "50", null, "0.9968033857", "49.84016929", "8757.01", "9.39", null, null, "971.30"), + expectedRow("1/8/2019", "50", null, "0.99574012", "49.787006", "8716.36", "9.35", null, null, "971.30"), + expectedRow("1/9/2019", "50", null, "0.9946779885", "49.73389942", "8675.67", "9.31", null, null, "971.30"), + expectedRow("1/10/2019", "50", null, "0.9936169898", "49.68084949", "8634.94", "9.26", null, null, "971.30"), + expectedRow("1/11/2019", "50", null, "0.992557123", "49.62785615", "8594.16", "9.22", null, null, "971.30"), + expectedRow("1/12/2019", "50", null, "0.9914983866", "49.57491933", "8553.33", "9.18", null, null, "971.30"), + expectedRow("1/13/2019", "50", null, "0.9904407796", "49.52203898", "8512.47", "9.13", null, null, "971.30"), + expectedRow("1/14/2019", "50", null, "0.9893843007", "49.46921504", "8471.56", "9.09", null, null, "971.30"), + expectedRow("1/15/2019", "50", null, "0.9883289487", "49.41644744", "8430.60", "9.05", null, null, "971.30"), + expectedRow("1/16/2019", "50", null, "0.9872747225", "49.36373612", "8389.61", "9.00", null, null, "971.30"), + expectedRow("1/17/2019", "50", null, "0.9862216208", "49.31108104", "8348.56", "8.96", null, null, "971.30"), + expectedRow("1/18/2019", "50", null, "0.9851696423", "49.25848212", "8307.48", "8.91", null, null, "971.30"), + expectedRow("1/19/2019", "50", null, "0.984118786", "49.2059393", "8266.35", "8.87", null, null, "971.30"), + expectedRow("1/20/2019", "50", null, "0.9830690507", "49.15345253", "8225.18", "8.83", null, null, "971.30"), + expectedRow("1/21/2019", "50", null, "0.982020435", "49.10102175", "8183.96", "8.78", null, null, "971.30"), + expectedRow("1/22/2019", "50", null, "0.9809729379", "49.0486469", "8142.70", "8.74", null, null, "971.30"), + expectedRow("1/23/2019", "50", null, "0.9799265581", "48.99632791", "8101.39", "8.69", null, null, "971.30"), + expectedRow("1/24/2019", "50", null, "0.9788812945", "48.94406473", "8060.04", "8.65", null, null, "971.30"), + expectedRow("1/25/2019", "50", null, "0.9778371458", "48.89185729", "8018.65", "8.61", null, null, "971.30"), + expectedRow("1/26/2019", "50", null, "0.9767941109", "48.83970555", "7977.21", "8.56", null, null, "971.30"), + expectedRow("1/27/2019", "50", null, "0.9757521886", "48.78760943", "7935.73", "8.52", null, null, "971.30"), + expectedRow("1/28/2019", "50", null, "0.9747113777", "48.73556888", "7894.21", "8.47", null, null, "971.30"), + expectedRow("1/29/2019", "50", null, "0.973671677", "48.68358385", "7852.63", "8.43", null, null, "971.30"), + expectedRow("1/30/2019", "50", null, "0.9726330853", "48.63165426", "7811.02", "8.39", null, null, "971.30"), + expectedRow("1/31/2019", "50", null, "0.9715956014", "48.57978007", "7769.36", "8.34", null, null, "971.30"), + expectedRow("2/1/2019", "50", null, "0.9705592242", "48.52796121", "7727.66", "8.30", null, null, "971.30"), + expectedRow("2/2/2019", "50", null, "0.9695239525", "48.47619762", "7685.91", "8.25", null, null, "971.30"), + expectedRow("2/3/2019", "50", null, "0.968489785", "48.42448925", "7644.12", "8.21", null, null, "971.30"), + expectedRow("2/4/2019", "50", null, "0.9674567207", "48.37283604", "7602.28", "8.16", null, null, "971.30"), + expectedRow("2/5/2019", "50", null, "0.9664247584", "48.32123792", "7560.40", "8.12", null, null, "971.30"), + expectedRow("2/6/2019", "50", null, "0.9653938968", "48.26969484", "7518.47", "8.07", null, null, "971.30"), + expectedRow("2/7/2019", "50", null, "0.9643641348", "48.21820674", "7476.50", "8.03", null, null, "971.30"), + expectedRow("2/8/2019", "50", null, "0.9633354712", "48.16677356", "7434.48", "7.98", null, null, "971.30"), + expectedRow("2/9/2019", "50", null, "0.9623079049", "48.11539525", "7392.42", "7.94", null, null, "971.30"), + expectedRow("2/10/2019", "50", null, "0.9612814347", "48.06407173", "7350.31", "7.89", null, null, "971.30"), + expectedRow("2/11/2019", "50", null, "0.9602560593", "48.01280297", "7308.16", "7.85", null, null, "971.30"), + expectedRow("2/12/2019", "50", null, "0.9592317777", "47.96158889", "7265.97", "7.80", null, null, "971.30"), + expectedRow("2/13/2019", "50", null, "0.9582085887", "47.91042944", "7223.72", "7.76", null, null, "971.30"), + expectedRow("2/14/2019", "50", null, "0.9571864911", "47.85932456", "7181.44", "7.71", null, null, "971.30"), + expectedRow("2/15/2019", "50", null, "0.9561654838", "47.80827419", "7139.11", "7.67", null, null, "971.30"), + expectedRow("2/16/2019", "50", null, "0.9551455655", "47.75727827", "7096.73", "7.62", null, null, "971.30"), + expectedRow("2/17/2019", "50", null, "0.9541267351", "47.70633676", "7054.31", "7.58", null, null, "971.30"), + expectedRow("2/18/2019", "50", null, "0.9531089916", "47.65544958", "7011.84", "7.53", null, null, "971.30"), + expectedRow("2/19/2019", "50", null, "0.9520923336", "47.60461668", "6969.33", "7.49", null, null, "971.30"), + expectedRow("2/20/2019", "50", null, "0.95107676", "47.553838", "6926.77", "7.44", null, null, "971.30"), + expectedRow("2/21/2019", "50", null, "0.9500622698", "47.50311349", "6884.17", "7.40", null, null, "971.30"), + expectedRow("2/22/2019", "50", null, "0.9490488616", "47.45244308", "6841.52", "7.35", null, null, "971.30"), + expectedRow("2/23/2019", "50", null, "0.9480365345", "47.40182672", "6798.82", "7.31", null, null, "971.30"), + expectedRow("2/24/2019", "50", null, "0.9470252872", "47.35126436", "6756.08", "7.26", null, null, "971.30"), + expectedRow("2/25/2019", "50", null, "0.9460151185", "47.30075593", "6713.30", "7.21", null, null, "971.30"), + expectedRow("2/26/2019", "50", null, "0.9450060274", "47.25030137", "6670.47", "7.17", null, null, "971.30"), + expectedRow("2/27/2019", "50", null, "0.9439980126", "47.19990063", "6627.59", "7.12", null, null, "971.30"), + expectedRow("2/28/2019", "50", null, "0.9429910731", "47.14955366", "6584.67", "7.08", null, null, "971.30"), + expectedRow("3/1/2019", "50", null, "0.9419852077", "47.09926038", "6541.70", "7.03", null, null, "971.30"), + expectedRow("3/2/2019", "50", null, "0.9409804151", "47.04902076", "6498.68", "6.99", null, null, "971.30"), + expectedRow("3/3/2019", "50", null, "0.9399766944", "46.99883472", "6455.62", "6.94", null, null, "971.30"), + expectedRow("3/4/2019", "50", null, "0.9389740443", "46.94870222", "6412.51", "6.89", null, null, "971.30"), + expectedRow("3/5/2019", "50", null, "0.9379724637", "46.89862319", "6369.36", "6.85", null, null, "971.30"), + expectedRow("3/6/2019", "50", null, "0.9369719515", "46.84859758", "6326.16", "6.80", null, null, "971.30"), + expectedRow("3/7/2019", "50", null, "0.9359725065", "46.79862533", "6282.92", "6.76", null, null, "971.30"), + expectedRow("3/8/2019", "50", null, "0.9349741276", "46.74870638", "6239.63", "6.71", null, null, "971.30"), + expectedRow("3/9/2019", "50", null, "0.9339768136", "46.69884068", "6196.29", "6.66", null, null, "971.30"), + expectedRow("3/10/2019", "50", null, "0.9329805635", "46.64902817", "6152.91", "6.62", null, null, "971.30"), + expectedRow("3/11/2019", "50", null, "0.931985376", "46.5992688", "6109.48", "6.57", null, null, "971.30"), + expectedRow("3/12/2019", "50", null, "0.93099125", "46.5495625", "6066.00", "6.52", null, null, "971.30"), + expectedRow("3/13/2019", "50", null, "0.9299981845", "46.49990922", "6022.48", "6.48", null, null, "971.30"), + expectedRow("3/14/2019", "50", null, "0.9290061782", "46.45030891", "5978.91", "6.43", null, null, "971.30"), + expectedRow("3/15/2019", "50", null, "0.9280152301", "46.40076151", "5935.29", "6.38", null, null, "971.30"), + expectedRow("3/16/2019", "50", null, "0.927025339", "46.35126695", "5891.63", "6.34", null, null, "971.30"), + expectedRow("3/17/2019", "50", null, "0.9260365038", "46.30182519", "5847.92", "6.29", null, null, "971.30"), + expectedRow("3/18/2019", "50", null, "0.9250487234", "46.25243617", "5804.17", "6.24", null, null, "971.30"), + expectedRow("3/19/2019", "50", null, "0.9240619966", "46.20309983", "5760.36", "6.20", null, null, "971.30"), + expectedRow("3/20/2019", "50", null, "0.9230763224", "46.15381612", "5716.52", "6.15", null, null, "971.30"), + expectedRow("3/21/2019", "50", null, "0.9220916995", "46.10458497", "5672.62", "6.10", null, null, "971.30"), + expectedRow("3/22/2019", "50", null, "0.9211081269", "46.05540634", "5628.68", "6.06", null, null, "971.30"), + expectedRow("3/23/2019", "50", null, "0.9201256034", "46.00628017", "5584.69", "6.01", null, null, "971.30"), + expectedRow("3/24/2019", "50", null, "0.919144128", "45.9572064", "5540.65", "5.96", null, null, "971.30"), + expectedRow("3/25/2019", "50", null, "0.9181636995", "45.90818498", "5496.57", "5.92", null, null, "971.30"), + expectedRow("3/26/2019", "50", null, "0.9171843168", "45.85921584", "5452.44", "5.87", null, null, "971.30"), + expectedRow("3/27/2019", "50", null, "0.9162059788", "45.81029894", "5408.26", "5.82", null, null, "971.30"), + expectedRow("3/28/2019", "50", null, "0.9152286843", "45.76143422", "5364.03", "5.78", null, null, "971.30"), + expectedRow("3/29/2019", "50", null, "0.9142524323", "45.71262162", "5319.76", "5.73", null, null, "971.30"), + expectedRow("3/30/2019", "50", null, "0.9132772217", "45.66386108", "5275.44", "5.68", null, null, "971.30"), + expectedRow("3/31/2019", "50", null, "0.9123030513", "45.61515256", "5231.08", "5.63", null, null, "971.30"), + expectedRow("4/1/2019", "50", null, "0.91132992", "45.566496", "5186.66", "5.59", null, null, "971.30"), + expectedRow("4/2/2019", "50", null, "0.9103578267", "45.51789134", "5142.20", "5.54", null, null, "971.30"), + expectedRow("4/3/2019", "50", null, "0.9093867703", "45.46933852", "5097.69", "5.49", null, null, "971.30"), + expectedRow("4/4/2019", "50", null, "0.9084167498", "45.42083749", "5053.13", "5.44", null, null, "971.30"), + expectedRow("4/5/2019", "50", null, "0.9074477639", "45.3723882", "5008.53", "5.40", null, null, "971.30"), + expectedRow("4/6/2019", "50", null, "0.9064798116", "45.32399058", "4963.88", "5.35", null, null, "971.30"), + expectedRow("4/7/2019", "50", null, "0.9055128918", "45.27564459", "4919.18", "5.30", null, null, "971.30"), + expectedRow("4/8/2019", "50", null, "0.9045470035", "45.22735017", "4874.43", "5.25", null, null, "971.30"), + expectedRow("4/9/2019", "50", null, "0.9035821453", "45.17910727", "4829.64", "5.20", null, null, "971.30"), + expectedRow("4/10/2019", "50", null, "0.9026183164", "45.13091582", "4784.79", "5.16", null, null, "971.30"), + expectedRow("4/11/2019", "50", null, "0.9016555156", "45.08277578", "4739.90", "5.11", null, null, "971.30"), + expectedRow("4/12/2019", "50", null, "0.9006937418", "45.03468709", "4694.96", "5.06", null, null, "971.30"), + expectedRow("4/13/2019", "50", null, "0.8997329939", "44.98664969", "4649.98", "5.01", null, null, "971.30"), + expectedRow("4/14/2019", "50", null, "0.8987732707", "44.93866354", "4604.94", "4.97", null, null, "971.30"), + expectedRow("4/15/2019", "50", null, "0.8978145713", "44.89072857", "4559.86", "4.92", null, null, "971.30"), + expectedRow("4/16/2019", "50", null, "0.8968568945", "44.84284473", "4514.73", "4.87", null, null, "971.30"), + expectedRow("4/17/2019", "50", null, "0.8959002393", "44.79501196", "4469.55", "4.82", null, null, "971.30"), + expectedRow("4/18/2019", "50", null, "0.8949446045", "44.74723022", "4424.32", "4.77", null, null, "971.30"), + expectedRow("4/19/2019", "50", null, "0.893989989", "44.69949945", "4379.05", "4.72", null, null, "971.30"), + expectedRow("4/20/2019", "50", null, "0.8930363918", "44.65181959", "4333.72", "4.68", null, null, "971.30"), + expectedRow("4/21/2019", "50", null, "0.8920838118", "44.60419059", "4288.35", "4.63", null, null, "971.30"), + expectedRow("4/22/2019", "50", null, "0.8911322479", "44.55661239", "4242.93", "4.58", null, null, "971.30"), + expectedRow("4/23/2019", "50", null, "0.890181699", "44.50908495", "4197.46", "4.53", null, null, "971.30"), + expectedRow("4/24/2019", "50", null, "0.889232164", "44.4616082", "4151.94", "4.48", null, null, "971.30"), + expectedRow("4/25/2019", "50", null, "0.8882836418", "44.41418209", "4106.38", "4.43", null, null, "971.30"), + expectedRow("4/26/2019", "50", null, "0.8873361315", "44.36680657", "4060.76", "4.38", null, null, "971.30"), + expectedRow("4/27/2019", "50", null, "0.8863896318", "44.31948159", "4015.10", "4.34", null, null, "971.30"), + expectedRow("4/28/2019", "50", null, "0.8854441417", "44.27220708", "3969.38", "4.29", null, null, "971.30"), + expectedRow("4/29/2019", "50", null, "0.8844996601", "44.22498301", "3923.62", "4.24", null, null, "971.30"), + expectedRow("4/30/2019", "50", null, "0.883556186", "44.1778093", "3877.81", "4.19", null, null, "971.30"), + expectedRow("5/1/2019", "50", null, "0.8826137183", "44.13068592", "3831.95", "4.14", null, null, "971.30"), + expectedRow("5/2/2019", "50", null, "0.8816722559", "44.0836128", "3786.04", "4.09", null, null, "971.30"), + expectedRow("5/3/2019", "50", null, "0.8807317977", "44.03658989", "3740.09", "4.04", null, null, "971.30"), + expectedRow("5/4/2019", "50", null, "0.8797923427", "43.98961714", "3694.08", "3.99", null, null, "971.30"), + expectedRow("5/5/2019", "50", null, "0.8788538898", "43.94269449", "3648.03", "3.94", null, null, "971.30"), + expectedRow("5/6/2019", "50", null, "0.8779164379", "43.8958219", "3601.92", "3.90", null, null, "971.30"), + expectedRow("5/7/2019", "50", null, "0.876979986", "43.8489993", "3555.77", "3.85", null, null, "971.30"), + expectedRow("5/8/2019", "50", null, "0.8760445329", "43.80222665", "3509.56", "3.80", null, null, "971.30"), + expectedRow("5/9/2019", "50", null, "0.8751100777", "43.75550389", "3463.31", "3.75", null, null, "971.30"), + expectedRow("5/10/2019", "50", null, "0.8741766193", "43.70883096", "3417.01", "3.70", null, null, "971.30"), + expectedRow("5/11/2019", "50", null, "0.8732441565", "43.66220782", "3370.66", "3.65", null, null, "971.30"), + expectedRow("5/12/2019", "50", null, "0.8723126884", "43.61563442", "3324.26", "3.60", null, null, "971.30"), + expectedRow("5/13/2019", "50", null, "0.8713822138", "43.56911069", "3277.81", "3.55", null, null, "971.30"), + expectedRow("5/14/2019", "50", null, "0.8704527318", "43.52263659", "3231.31", "3.50", null, null, "971.30"), + expectedRow("5/15/2019", "50", null, "0.8695242412", "43.47621206", "3184.76", "3.45", null, null, "971.30"), + expectedRow("5/16/2019", "50", null, "0.868596741", "43.42983705", "3138.16", "3.40", null, null, "971.30"), + expectedRow("5/17/2019", "50", null, "0.8676702302", "43.38351151", "3091.51", "3.35", null, null, "971.30"), + expectedRow("5/18/2019", "50", null, "0.8667447076", "43.33723538", "3044.81", "3.30", null, null, "971.30"), + expectedRow("5/19/2019", "50", null, "0.8658201723", "43.29100861", "2998.06", "3.25", null, null, "971.30"), + expectedRow("5/20/2019", "50", null, "0.8648966231", "43.24483116", "2951.26", "3.20", null, null, "971.30"), + expectedRow("5/21/2019", "50", null, "0.8639740591", "43.19870296", "2904.42", "3.15", null, null, "971.30"), + expectedRow("5/22/2019", "50", null, "0.8630524792", "43.15262396", "2857.52", "3.10", null, null, "971.30"), + expectedRow("5/23/2019", "50", null, "0.8621318823", "43.10659411", "2810.57", "3.05", null, null, "971.30"), + expectedRow("5/24/2019", "50", null, "0.8612122673", "43.06061337", "2763.57", "3.00", null, null, "971.30"), + expectedRow("5/25/2019", "50", null, "0.8602936333", "43.01468167", "2716.52", "2.95", null, null, "971.30"), + expectedRow("5/26/2019", "50", null, "0.8593759792", "42.96879896", "2669.42", "2.90", null, null, "971.30"), + expectedRow("5/27/2019", "50", null, "0.8584593039", "42.9229652", "2622.27", "2.85", null, null, "971.30"), + expectedRow("5/28/2019", "50", null, "0.8575436064", "42.87718032", "2575.07", "2.80", null, null, "971.30"), + expectedRow("5/29/2019", "50", null, "0.8566288857", "42.83144429", "2527.82", "2.75", null, null, "971.30"), + expectedRow("5/30/2019", "50", null, "0.8557151407", "42.78575703", "2480.52", "2.70", null, null, "971.30"), + expectedRow("5/31/2019", "50", null, "0.8548023703", "42.74011852", "2433.17", "2.65", null, null, "971.30"), + expectedRow("6/1/2019", "50", null, "0.8538905736", "42.69452868", "2385.77", "2.60", null, null, "971.30"), + expectedRow("6/2/2019", "50", null, "0.8529797495", "42.64898747", "2338.31", "2.55", null, null, "971.30"), + expectedRow("6/3/2019", "50", null, "0.8520698969", "42.60349484", "2290.81", "2.50", null, null, "971.30"), + expectedRow("6/4/2019", "50", null, "0.8511610148", "42.55805074", "2243.26", "2.45", null, null, "971.30"), + expectedRow("6/5/2019", "50", null, "0.8502531022", "42.51265511", "2195.65", "2.40", null, null, "971.30"), + expectedRow("6/6/2019", "50", null, "0.8493461581", "42.46730791", "2148.00", "2.34", null, null, "971.30"), + expectedRow("6/7/2019", "50", null, "0.8484401814", "42.42200907", "2100.29", "2.29", null, null, "971.30"), + expectedRow("6/8/2019", "50", null, "0.8475351711", "42.37675855", "2052.53", "2.24", null, null, "971.30"), + expectedRow("6/9/2019", "50", null, "0.8466311261", "42.3315563", "2004.73", "2.19", null, null, "971.30"), + expectedRow("6/10/2019", "50", null, "0.8457280454", "42.28640227", "1956.87", "2.14", null, null, "971.30"), + expectedRow("6/11/2019", "50", null, "0.844825928", "42.2412964", "1908.96", "2.09", null, null, "971.30"), + expectedRow("6/12/2019", "50", null, "0.8439247729", "42.19623865", "1860.99", "2.04", null, null, "971.30"), + expectedRow("6/13/2019", "50", null, "0.8430245791", "42.15122895", "1812.98", "1.99", null, null, "971.30"), + expectedRow("6/14/2019", "50", null, "0.8421253454", "42.10626727", "1764.92", "1.94", null, null, "971.30"), + expectedRow("6/15/2019", "50", null, "0.841227071", "42.06135355", "1716.80", "1.88", null, null, "971.30"), + expectedRow("6/16/2019", "50", null, "0.8403297547", "42.01648774", "1668.64", "1.83", null, null, "971.30"), + expectedRow("6/17/2019", "50", null, "0.8394333956", "41.97166978", "1620.42", "1.78", null, null, "971.30"), + expectedRow("6/18/2019", "50", null, "0.8385379925", "41.92689963", "1572.15", "1.73", null, null, "971.30"), + expectedRow("6/19/2019", "50", null, "0.8376435446", "41.88217723", "1523.83", "1.68", null, null, "971.30"), + expectedRow("6/20/2019", "50", null, "0.8367500508", "41.83750254", "1475.45", "1.63", null, null, "971.30"), + expectedRow("6/21/2019", "50", null, "0.83585751", "41.7928755", "1427.03", "1.58", null, null, "971.30"), + expectedRow("6/22/2019", "50", null, "0.8349659213", "41.74829607", "1378.55", "1.52", null, null, "971.30"), + expectedRow("6/23/2019", "50", null, "0.8340752837", "41.70376418", "1330.02", "1.47", null, null, "971.30"), + expectedRow("6/24/2019", "50", null, "0.833185596", "41.6592798", "1281.45", "1.42", null, null, "971.30"), + expectedRow("6/25/2019", "50", null, "0.8322968574", "41.61484287", "1232.81", "1.37", null, null, "971.30"), + expectedRow("6/26/2019", "50", null, "0.8314090667", "41.57045334", "1184.13", "1.32", null, null, "971.30"), + expectedRow("6/27/2019", "50", null, "0.8305222231", "41.52611115", "1135.39", "1.26", null, null, "971.30"), + expectedRow("6/28/2019", "50", null, "0.8296363254", "41.48181627", "1086.61", "1.21", null, null, "971.30"), + expectedRow("6/29/2019", "50", null, "0.8287513727", "41.43756863", "1037.77", "1.16", null, null, "971.30"), + expectedRow("6/30/2019", "50", null, "0.8278673639", "41.39336819", "988.88", "1.11", null, null, "971.30"), + expectedRow("7/1/2019", "50", null, "0.8269842981", "41.3492149", "939.93", "1.06", null, null, "971.30"), + expectedRow("7/2/2019", "50", null, "0.8261021742", "41.30510871", "890.93", "1.00", null, null, "971.30"), + expectedRow("7/3/2019", "50", null, "0.8252209913", "41.26104956", "841.89", "0.95", null, null, "971.30"), + expectedRow("7/4/2019", "50", null, "0.8243407483", "41.21703741", "792.79", "0.90", null, null, "971.30"), + expectedRow("7/5/2019", "50", null, "0.8234614442", "41.17307221", "743.63", "0.85", null, null, "971.30"), + expectedRow("7/6/2019", "50", null, "0.8225830781", "41.1291539", "694.43", "0.79", null, null, "971.30"), + expectedRow("7/7/2019", "50", null, "0.8217056489", "41.08528244", "645.17", "0.74", null, null, "971.30"), + expectedRow("7/8/2019", "50", null, "0.8208291556", "41.04145778", "595.86", "0.69", null, null, "971.30"), + expectedRow("7/9/2019", "50", null, "0.8199535973", "40.99767987", "546.49", "0.64", null, null, "971.30"), + expectedRow("7/10/2019", "50", null, "0.8190789729", "40.95394865", "497.08", "0.58", null, null, "971.30"), + expectedRow("7/11/2019", "50", null, "0.8182052815", "40.91026407", "447.61", "0.53", null, null, "971.30"), + expectedRow("7/12/2019", "50", null, "0.8173325219", "40.8666261", "398.08", "0.48", null, null, "971.30"), + expectedRow("7/13/2019", "50", null, "0.8164606934", "40.82303467", "348.51", "0.43", null, null, "971.30"), + expectedRow("7/14/2019", "50", null, "0.8155897948", "40.77948974", "298.88", "0.37", null, null, "971.30"), + expectedRow("7/15/2019", "50", null, "0.8147198252", "40.73599126", "249.20", "0.32", null, null, "971.30"), + expectedRow("7/16/2019", "50", null, "0.8138507835", "40.69253918", "199.47", "0.27", null, null, "971.30"), + expectedRow("7/17/2019", "50", null, "0.8129826688", "40.64913344", "149.68", "0.21", null, null, "971.30"), + expectedRow("7/18/2019", "50", null, "0.8121154802", "40.60577401", "99.84", "0.16", null, null, "971.30"), + expectedRow("7/19/2019", "50", null, "0.8112492165", "40.56246082", "49.95", "0.11", null, null, "971.30"), + expectedRow("7/20/2019", "50", null, "0.8103838768", "40.51919384", "0.00", "0.05", null, null, "971.30")); + } + + private static ExpectedScheduleRow expectedRow(final String paymentDate, final String expectedPaymentAmount, + final String actualPaymentAmount, final String discountFactor, final String npvValue, final String balance, + final String expectedAmortization, final String actualAmortization, final String incomeModification, + final String deferredBalance) { + return new ExpectedScheduleRow(parseDateString(paymentDate), expectedPaymentAmount, actualPaymentAmount, discountFactor, npvValue, + balance, expectedAmortization, actualAmortization, incomeModification, deferredBalance); } private static LocalDate parseDateString(final String dateText) { return LocalDate.parse(dateText, DateTimeFormatter.ofPattern("M/d/yyyy")); } - private record ExpectedScheduleRow(LocalDate paymentDate, String count, String paymentsLeft, String expectedPaymentAmount, - String forecastPaymentAmount, String actualPaymentAmount, String discountFactor, String npvValue, String balance, - String expectedAmortization, String netAmortization, String actualAmortization, String incomeModification, - String deferredBalance) { + private record ExpectedScheduleRow(LocalDate paymentDate, String expectedPaymentAmount, String actualPaymentAmount, + String discountFactor, String npvValue, String balance, String expectedAmortization, String actualAmortization, + String incomeModification, String deferredBalance) { } private Long createProduct() { diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/helpers/FeignWorkingCapitalLoanHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/helpers/FeignWorkingCapitalLoanHelper.java new file mode 100644 index 00000000000..6eb70fe0ac6 --- /dev/null +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/helpers/FeignWorkingCapitalLoanHelper.java @@ -0,0 +1,100 @@ +/** + * 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.integrationtests.client.feign.helpers; + +import static org.apache.fineract.client.feign.util.FeignCalls.ok; + +import java.util.List; +import org.apache.fineract.client.feign.FineractFeignClient; +import org.apache.fineract.client.feign.util.CallFailedRuntimeException; +import org.apache.fineract.client.models.CommandProcessingResult; +import org.apache.fineract.client.models.GetWorkingCapitalLoansLoanIdResponse; +import org.apache.fineract.client.models.PostWorkingCapitalLoansLoanIdRequest; +import org.apache.fineract.client.models.PostWorkingCapitalLoansLoanIdResponse; +import org.apache.fineract.client.models.PostWorkingCapitalLoansRequest; +import org.apache.fineract.client.models.PostWorkingCapitalLoansResponse; +import org.apache.fineract.client.models.PutWorkingCapitalLoansLoanIdDiscountRequest; +import org.apache.fineract.client.models.PutWorkingCapitalLoansLoanIdRateRequest; +import org.apache.fineract.client.models.WorkingCapitalLoanPeriodPaymentRateChangeData; + +public class FeignWorkingCapitalLoanHelper { + + private final FineractFeignClient fineractClient; + + public FeignWorkingCapitalLoanHelper(FineractFeignClient fineractClient) { + this.fineractClient = fineractClient; + } + + public Long submitApplication(PostWorkingCapitalLoansRequest request) { + PostWorkingCapitalLoansResponse response = ok( + () -> fineractClient.workingCapitalLoans().submitWorkingCapitalLoanApplication(request)); + return response.getResourceId(); + } + + public Long approve(Long loanId, PostWorkingCapitalLoansLoanIdRequest request) { + PostWorkingCapitalLoansLoanIdResponse result = ok( + () -> fineractClient.workingCapitalLoans().stateTransitionWorkingCapitalLoanById(loanId, "approve", request)); + return result.getResourceId(); + } + + public Long disburse(Long loanId, PostWorkingCapitalLoansLoanIdRequest request) { + PostWorkingCapitalLoansLoanIdResponse result = ok( + () -> fineractClient.workingCapitalLoans().stateTransitionWorkingCapitalLoanById(loanId, "disburse", request)); + return result.getResourceId(); + } + + public Long undoDisbursal(Long loanId, PostWorkingCapitalLoansLoanIdRequest request) { + PostWorkingCapitalLoansLoanIdResponse result = ok( + () -> fineractClient.workingCapitalLoans().stateTransitionWorkingCapitalLoanById(loanId, "undodisbursal", request)); + return result.getResourceId(); + } + + public void undoApproval(Long loanId, PostWorkingCapitalLoansLoanIdRequest request) { + ok(() -> fineractClient.workingCapitalLoans().stateTransitionWorkingCapitalLoanById(loanId, "undoapproval", request)); + } + + public void delete(Long loanId) { + ok(() -> fineractClient.workingCapitalLoans().deleteWorkingCapitalLoanApplication(loanId)); + } + + public GetWorkingCapitalLoansLoanIdResponse getLoanDetails(Long loanId) { + return ok(() -> fineractClient.workingCapitalLoans().retrieveWorkingCapitalLoanById(loanId)); + } + + public Long updateDiscount(Long loanId, PutWorkingCapitalLoansLoanIdDiscountRequest request) { + return ok(() -> fineractClient.workingCapitalLoans().updateWorkingCapitalLoanDiscountById(loanId, request)).getResourceId(); + } + + public CommandProcessingResult updateRate(Long loanId, PutWorkingCapitalLoansLoanIdRateRequest request) { + return ok(() -> fineractClient.workingCapitalLoans().updateWorkingCapitalLoanRateById(loanId, request)); + } + + public CallFailedRuntimeException updateRateExpectingError(Long loanId, PutWorkingCapitalLoansLoanIdRateRequest request) { + try { + ok(() -> fineractClient.workingCapitalLoans().updateWorkingCapitalLoanRateById(loanId, request)); + throw new AssertionError("Expected rate update to fail but it succeeded"); + } catch (final CallFailedRuntimeException e) { + return e; + } + } + + public List getRateChangeHistory(Long loanId) { + return ok(() -> fineractClient.workingCapitalLoans().getWorkingCapitalLoanRateChangeHistoryById(loanId)); + } +} diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/modules/WorkingCapitalLoanRequestBuilders.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/modules/WorkingCapitalLoanRequestBuilders.java new file mode 100644 index 00000000000..3535437a22e --- /dev/null +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/modules/WorkingCapitalLoanRequestBuilders.java @@ -0,0 +1,62 @@ +/** + * 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.integrationtests.client.feign.modules; + +import java.math.BigDecimal; +import org.apache.fineract.client.models.PostWorkingCapitalLoansLoanIdRequest; +import org.apache.fineract.client.models.PostWorkingCapitalLoansRequest; +import org.apache.fineract.client.models.PutWorkingCapitalLoansLoanIdRateRequest; + +public final class WorkingCapitalLoanRequestBuilders { + + private static final String LOCALE = "en"; + private static final String DATE_FORMAT = "dd MMMM yyyy"; + + private WorkingCapitalLoanRequestBuilders() {} + + public static PostWorkingCapitalLoansRequest submitApplication(Long clientId, Long productId, BigDecimal principal, + BigDecimal periodPaymentRate, String submittedOnDate, String expectedDisbursementDate) { + return new PostWorkingCapitalLoansRequest().clientId(clientId).productId(productId).principalAmount(principal) + .periodPaymentRate(periodPaymentRate).submittedOnDate(submittedOnDate).expectedDisbursementDate(expectedDisbursementDate) + .totalPayment(BigDecimal.valueOf(100000)).locale(LOCALE).dateFormat(DATE_FORMAT); + } + + public static PostWorkingCapitalLoansLoanIdRequest approve(String approvedOnDate, BigDecimal approvedAmount, + String expectedDisbursementDate) { + return new PostWorkingCapitalLoansLoanIdRequest().approvedOnDate(approvedOnDate).approvedLoanAmount(approvedAmount) + .expectedDisbursementDate(expectedDisbursementDate).locale(LOCALE).dateFormat(DATE_FORMAT); + } + + public static PostWorkingCapitalLoansLoanIdRequest disburse(String actualDisbursementDate, BigDecimal transactionAmount) { + return new PostWorkingCapitalLoansLoanIdRequest().actualDisbursementDate(actualDisbursementDate) + .transactionAmount(transactionAmount).locale(LOCALE).dateFormat(DATE_FORMAT); + } + + public static PostWorkingCapitalLoansLoanIdRequest undoDisbursal() { + return new PostWorkingCapitalLoansLoanIdRequest().locale(LOCALE).dateFormat(DATE_FORMAT); + } + + public static PostWorkingCapitalLoansLoanIdRequest emptyCommand() { + return new PostWorkingCapitalLoansLoanIdRequest().locale(LOCALE).dateFormat(DATE_FORMAT); + } + + public static PutWorkingCapitalLoansLoanIdRateRequest updateRate(BigDecimal newRate) { + return new PutWorkingCapitalLoansLoanIdRateRequest().periodPaymentRate(newRate).locale(LOCALE); + } +} diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/tests/FeignWorkingCapitalLoanRateChangeTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/tests/FeignWorkingCapitalLoanRateChangeTest.java new file mode 100644 index 00000000000..c503be6f2e6 --- /dev/null +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/tests/FeignWorkingCapitalLoanRateChangeTest.java @@ -0,0 +1,233 @@ +/** + * 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.integrationtests.client.feign.tests; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import org.apache.fineract.client.feign.util.CallFailedRuntimeException; +import org.apache.fineract.client.models.GetWorkingCapitalLoansLoanIdResponse; +import org.apache.fineract.client.models.WorkingCapitalLoanPeriodPaymentRateChangeData; +import org.apache.fineract.integrationtests.client.FeignIntegrationTest; +import org.apache.fineract.integrationtests.client.feign.helpers.FeignBusinessDateHelper; +import org.apache.fineract.integrationtests.client.feign.helpers.FeignClientHelper; +import org.apache.fineract.integrationtests.client.feign.helpers.FeignWorkingCapitalLoanHelper; +import org.apache.fineract.integrationtests.client.feign.modules.WorkingCapitalLoanRequestBuilders; +import org.apache.fineract.integrationtests.common.Utils; +import org.apache.fineract.integrationtests.common.workingcapitalloanproduct.WorkingCapitalLoanProductHelper; +import org.apache.fineract.integrationtests.common.workingcapitalloanproduct.WorkingCapitalLoanProductTestBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class FeignWorkingCapitalLoanRateChangeTest extends FeignIntegrationTest { + + private FeignWorkingCapitalLoanHelper wcLoanHelper; + private FeignClientHelper clientHelper; + private WorkingCapitalLoanProductHelper productHelper; + private FeignBusinessDateHelper businessDateHelper; + + private Long clientId; + + private final List createdLoanIds = new ArrayList<>(); + private final List createdProductIds = new ArrayList<>(); + + @BeforeAll + void setupHelpers() { + wcLoanHelper = new FeignWorkingCapitalLoanHelper(fineractClient()); + clientHelper = new FeignClientHelper(fineractClient()); + productHelper = new WorkingCapitalLoanProductHelper(); + businessDateHelper = new FeignBusinessDateHelper(fineractClient()); + clientId = clientHelper.createClient(); + } + + @AfterAll + void cleanupEntities() { + for (final Long loanId : createdLoanIds) { + if (loanId == null) { + continue; + } + try { + wcLoanHelper.undoDisbursal(loanId, WorkingCapitalLoanRequestBuilders.undoDisbursal()); + } catch (final CallFailedRuntimeException ignored) { + // best-effort cleanup: loan may not be in disbursed state + } + try { + wcLoanHelper.undoApproval(loanId, WorkingCapitalLoanRequestBuilders.emptyCommand()); + } catch (final CallFailedRuntimeException ignored) { + // best-effort cleanup: loan may not be in approved state + } + try { + wcLoanHelper.delete(loanId); + } catch (final CallFailedRuntimeException ignored) { + // best-effort cleanup: loan may already be deleted or in non-deletable state + } + } + createdLoanIds.clear(); + createdProductIds.clear(); + } + + @Test + void testUpdateRateOnActiveLoan() { + Long loanId = createAndDisburseLoan(BigDecimal.valueOf(5000), BigDecimal.valueOf(18)); + + wcLoanHelper.updateRate(loanId, WorkingCapitalLoanRequestBuilders.updateRate(BigDecimal.valueOf(17))); + + GetWorkingCapitalLoansLoanIdResponse loan = wcLoanHelper.getLoanDetails(loanId); + assertNotNull(loan); + assertEquals(0, BigDecimal.valueOf(17).compareTo(loan.getPeriodPaymentRate())); + } + + @Test + void testRateChangeHistoryIsRecorded() { + Long loanId = createAndDisburseLoan(BigDecimal.valueOf(5000), BigDecimal.valueOf(18)); + + wcLoanHelper.updateRate(loanId, WorkingCapitalLoanRequestBuilders.updateRate(BigDecimal.valueOf(17))); + + List history = wcLoanHelper.getRateChangeHistory(loanId); + assertFalse(history.isEmpty(), "Rate change history should not be empty"); + + WorkingCapitalLoanPeriodPaymentRateChangeData change = history.getFirst(); + assertEquals(0, BigDecimal.valueOf(18).compareTo(change.getPreviousRate())); + assertEquals(0, BigDecimal.valueOf(17).compareTo(change.getNewRate())); + assertFalse(change.getReversed()); + } + + @Test + void testRateChangeNotAllowedOnNonActiveLoan() { + Long productId = createProduct(); + String today = Utils.dateFormatter.format(Utils.getLocalDateOfTenant()); + Long loanId = submitAndTrack(clientId, productId, BigDecimal.valueOf(5000), BigDecimal.valueOf(18), today); + + CallFailedRuntimeException exception = wcLoanHelper.updateRateExpectingError(loanId, + WorkingCapitalLoanRequestBuilders.updateRate(BigDecimal.valueOf(17))); + assertTrue(exception.getStatus() >= 400, + "Rate change on non-active loan should fail with 4xx status, got: " + exception.getStatus()); + } + + @Test + void testMultipleRateChangesAutoReversesPrevious() { + Long loanId = createAndDisburseLoan(BigDecimal.valueOf(5000), BigDecimal.valueOf(18)); + + wcLoanHelper.updateRate(loanId, WorkingCapitalLoanRequestBuilders.updateRate(BigDecimal.valueOf(17))); + wcLoanHelper.updateRate(loanId, WorkingCapitalLoanRequestBuilders.updateRate(BigDecimal.valueOf(15))); + + List history = wcLoanHelper.getRateChangeHistory(loanId); + assertEquals(2, history.size(), "Should have 2 rate change records"); + + // Most recent (15%) should be active, previous (17%) should be auto-reversed + WorkingCapitalLoanPeriodPaymentRateChangeData latestChange = history.get(0); + WorkingCapitalLoanPeriodPaymentRateChangeData firstChange = history.get(1); + assertFalse(latestChange.getReversed(), "Latest rate change should be active"); + assertTrue(firstChange.getReversed(), "Previous rate change should be auto-reversed"); + + GetWorkingCapitalLoansLoanIdResponse loan = wcLoanHelper.getLoanDetails(loanId); + assertEquals(0, BigDecimal.valueOf(15).compareTo(loan.getPeriodPaymentRate())); + } + + @Test + void testMultipleRateChangesOnDifferentBusinessDates() { + businessDateHelper.runAt("2026-01-01", () -> { + Long clientForTest = clientHelper.createClient("01 January 2026"); + Long loanId = createAndDisburseLoanOnDate(clientForTest, BigDecimal.valueOf(50000), BigDecimal.valueOf(18), "01 January 2026"); + + // First rate change: 18 → 15 on Jan 1 + wcLoanHelper.updateRate(loanId, WorkingCapitalLoanRequestBuilders.updateRate(BigDecimal.valueOf(15))); + + // Advance business date by 8 days + businessDateHelper.updateBusinessDate("BUSINESS_DATE", "2026-01-09"); + + // Second rate change: 15 → 11 on Jan 9 + wcLoanHelper.updateRate(loanId, WorkingCapitalLoanRequestBuilders.updateRate(BigDecimal.valueOf(11))); + + GetWorkingCapitalLoansLoanIdResponse loan = wcLoanHelper.getLoanDetails(loanId); + assertEquals(0, BigDecimal.valueOf(11).compareTo(loan.getPeriodPaymentRate()), + "Rate should be updated to 11 after second rate change"); + + List history = wcLoanHelper.getRateChangeHistory(loanId); + assertEquals(2, history.size(), "Should have 2 rate change records"); + + // Latest (11%) should be active, first (15%) should be auto-reversed + assertFalse(history.get(0).getReversed(), "Latest rate change should be active"); + assertTrue(history.get(1).getReversed(), "Previous rate change should be auto-reversed"); + }); + } + + @Test + void testRateChangePastEndOfTermSucceeds() { + // Use a small principal with high rate to create a very short-term loan, + // then advance past term. The rate change should succeed — the segment starts + // at the base term end with the remaining principal as balance. + businessDateHelper.runAt("2026-01-01", () -> { + Long clientForTest = clientHelper.createClient("01 January 2026"); + Long loanId = createAndDisburseLoanOnDate(clientForTest, BigDecimal.valueOf(100), BigDecimal.valueOf(18), "01 January 2026"); + + // Advance past the loan term — rate change at day 5 is past the schedule end + businessDateHelper.updateBusinessDate("BUSINESS_DATE", "2026-01-06"); + + wcLoanHelper.updateRate(loanId, WorkingCapitalLoanRequestBuilders.updateRate(BigDecimal.valueOf(15))); + + GetWorkingCapitalLoansLoanIdResponse loan = wcLoanHelper.getLoanDetails(loanId); + assertEquals(0, BigDecimal.valueOf(15).compareTo(loan.getPeriodPaymentRate()), + "Rate should be updated to 15 after past-term rate change"); + }); + } + + private Long createAndDisburseLoanOnDate(Long clientIdParam, BigDecimal principal, BigDecimal rate, String date) { + Long productId = createProduct(); + Long loanId = submitAndTrack(clientIdParam, productId, principal, rate, date); + wcLoanHelper.approve(loanId, WorkingCapitalLoanRequestBuilders.approve(date, principal, date)); + wcLoanHelper.disburse(loanId, WorkingCapitalLoanRequestBuilders.disburse(date, principal)); + return loanId; + } + + private Long createAndDisburseLoan(BigDecimal principal, BigDecimal rate) { + Long productId = createProduct(); + String today = Utils.dateFormatter.format(Utils.getLocalDateOfTenant()); + Long loanId = submitAndTrack(clientId, productId, principal, rate, today); + + wcLoanHelper.approve(loanId, WorkingCapitalLoanRequestBuilders.approve(today, principal, today)); + wcLoanHelper.disburse(loanId, WorkingCapitalLoanRequestBuilders.disburse(today, principal)); + + return loanId; + } + + private Long submitAndTrack(Long clientIdParam, Long productId, BigDecimal principal, BigDecimal rate, String date) { + Long loanId = wcLoanHelper.submitApplication( + WorkingCapitalLoanRequestBuilders.submitApplication(clientIdParam, productId, principal, rate, date, date)); + createdLoanIds.add(loanId); + return loanId; + } + + private Long createProduct() { + String uniqueName = "WCL Rate " + Utils.uniqueRandomStringGenerator("", 8); + String uniqueShortName = Utils.uniqueRandomStringGenerator("", 4); + Long productId = productHelper + .createWorkingCapitalLoanProduct( + new WorkingCapitalLoanProductTestBuilder().withName(uniqueName).withShortName(uniqueShortName).build()) + .getResourceId(); + createdProductIds.add(productId); + return productId; + } +}