FINERACT-2070: Further test cases

This commit is contained in:
Arnold Galovics
2024-03-26 21:24:05 +01:00
committed by Arnold Gálovics
parent 7c618a69d5
commit ed5098b5a5
39 changed files with 31201 additions and 0 deletions
+2
View File
@@ -33,6 +33,8 @@ dependencies {
implementation 'org.springframework:spring-test'
testImplementation 'org.springframework:spring-jms'
testImplementation 'com.github.spotbugs:spotbugs-annotations'
testImplementation 'com.squareup.retrofit2:retrofit:2.9.0'
testImplementation 'commons-httpclient:commons-httpclient:3.1'
testImplementation 'org.apache.commons:commons-lang3:3.14.0'
@@ -0,0 +1,75 @@
/**
* 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.test.service;
import static org.awaitility.Awaitility.await;
import java.io.IOException;
import java.time.Duration;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.fineract.client.models.ExecuteJobRequest;
import org.apache.fineract.client.models.GetJobsResponse;
import org.apache.fineract.client.services.SchedulerJobApi;
import org.apache.fineract.test.data.job.Job;
import org.apache.fineract.test.data.job.JobResolver;
import org.apache.fineract.test.helper.ErrorHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import retrofit2.Response;
@Component
@RequiredArgsConstructor
@Slf4j
public class JobService {
@Autowired
private SchedulerJobApi schedulerJobApi;
private final JobResolver jobResolver;
public void execute(Job job) {
try {
Long jobId = jobResolver.resolve(job);
Response<Void> response = schedulerJobApi.executeJob(jobId, "executeJob", new ExecuteJobRequest()).execute();
ErrorHelper.checkSuccessfulApiCall(response);
} catch (IOException e) {
throw new RuntimeException("Exception while executing job %s".formatted(job.getName()), e);
}
}
public void executeAndWait(Job job) {
execute(job);
waitUntilJobIsFinished(job);
}
private void waitUntilJobIsFinished(Job job) {
String jobName = job.getName();
await().atMost(Duration.ofMinutes(2)).alias("%s didn't finish on time".formatted(jobName)).pollInterval(Duration.ofSeconds(10))
.until(() -> {
log.info("Waiting for job {} to finish", jobName);
Long jobId = jobResolver.resolve(job);
Response<GetJobsResponse> getJobsResponse = schedulerJobApi.retrieveOne5(jobId).execute();
ErrorHelper.checkSuccessfulApiCall(getJobsResponse);
Boolean currentlyRunning = getJobsResponse.body().getCurrentlyRunning();
return BooleanUtils.isFalse(currentlyRunning);
});
}
}
@@ -0,0 +1,771 @@
/**
* 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.
*/
/**
* 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.test.stepdef.assetexternalization;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.math.RoundingMode;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.ExternalOwnerJournalEntryData;
import org.apache.fineract.client.models.ExternalOwnerTransferJournalEntryData;
import org.apache.fineract.client.models.ExternalTransferData;
import org.apache.fineract.client.models.JournalEntryData;
import org.apache.fineract.client.models.PageExternalTransferData;
import org.apache.fineract.client.models.PostInitiateTransferRequest;
import org.apache.fineract.client.models.PostInitiateTransferResponse;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.ExternalAssetOwnersApi;
import org.apache.fineract.client.util.JSON;
import org.apache.fineract.test.data.AssetExternalizationErrorMessage;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.helper.Utils;
import org.apache.fineract.test.messaging.event.EventCheckHelper;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j
public class AssetExternalizationStepDef extends AbstractStepDef {
public static final String OWNER_EXTERNAL_ID_PREFIX = "TestOwner-";
public static final String DATE_FORMAT_ASSET_EXT = "yyyy-MM-dd";
public static final String DEFAULT_LOCALE = "en";
public static final String TRANSACTION_TYPE_SALE = "sale";
public static final String TRANSACTION_TYPE_BUYBACK = "buyback";
public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT_ASSET_EXT);
private static final Gson GSON = new JSON().getGson();
@Autowired
private ExternalAssetOwnersApi externalAssetOwnersApi;
@Autowired
private EventCheckHelper eventCheckHelper;
@When("Admin makes asset externalization request by Loan ID with unique ownerExternalId, user-generated transferExternalId and the following data:")
public void createAssetExternalizationRequestByLoanIdUserGeneratedExtId(DataTable table) throws IOException {
// if user created transferExternalId previously, it will use that, otherwise create a new one
String transferExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED);
if (transferExternalId == null) {
transferExternalId = Utils.randomNameGenerator("TestTransferExtId_", 3);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED, transferExternalId);
}
createAssetExternalizationRequestByLoanId(table, transferExternalId);
}
@When("Admin makes asset externalization request by Loan ID with unique ownerExternalId, system-generated transferExternalId and the following data:")
public void createAssetExternalizationRequestByLoanIdSystemGeneratedExtId(DataTable table) throws IOException {
createAssetExternalizationRequestByLoanId(table, null);
}
private void createAssetExternalizationRequestByLoanId(DataTable table, String transferExternalId) throws IOException {
createAssetExternalizationRequestByLoanId(table, transferExternalId, true);
}
private void createAssetExternalizationRequestByLoanId(DataTable table, String transferExternalId, boolean regenerateOwner)
throws IOException {
List<List<String>> data = table.asLists();
List<String> transferData = data.get(1);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostInitiateTransferRequest request = new PostInitiateTransferRequest();
if (transferData.get(0).equals(TRANSACTION_TYPE_BUYBACK)) {
request.settlementDate(transferData.get(1))//
.transferExternalId(transferExternalId)//
.dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi
.transferRequestWithLoanId(loanId, request, transferData.get(0)).execute();
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE, response);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_BUYBACK_TRANSFER_EXTERNAL_ID_FROM_RESPONSE,
response.body().getResourceExternalId());
ErrorHelper.checkSuccessfulApiCall(response);
} else if (transferData.get(0).equals(TRANSACTION_TYPE_SALE)) {
String ownerExternalId;
if (regenerateOwner) {
ownerExternalId = Utils.randomNameGenerator(OWNER_EXTERNAL_ID_PREFIX, 3);
} else {
ownerExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID);
}
request.settlementDate(transferData.get(1))//
.ownerExternalId(ownerExternalId)//
.transferExternalId(transferExternalId)//
.purchasePriceRatio(transferData.get(2))//
.dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi
.transferRequestWithLoanId(loanId, request, transferData.get(0)).execute();
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE, response);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_SALES_TRANSFER_EXTERNAL_ID_FROM_RESPONSE,
response.body().getResourceExternalId());
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID, ownerExternalId);
ErrorHelper.checkSuccessfulApiCall(response);
} else {
throw new IllegalStateException(String.format("%s is not supported Asset externalization transaction", transferData.get(0)));
}
}
@When("Admin makes asset externalization BUYBACK request with ownerExternalId = null and settlement date {string} by Loan ID with system-generated transferExternalId")
public void createAssetExternalizationBuybackRequestOwnerNullByLoanIdSystemGeneratedExtId(String settlementDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostInitiateTransferRequest request = new PostInitiateTransferRequest()//
.settlementDate(settlementDate)//
.ownerExternalId(null)//
.transferExternalId(testContext().get(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_FROM_RESPONSE))//
.dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi
.transferRequestWithLoanId(loanId, request, TRANSACTION_TYPE_BUYBACK).execute();
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE, response);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_BUYBACK_TRANSFER_EXTERNAL_ID_FROM_RESPONSE,
response.body().getResourceExternalId());
ErrorHelper.checkSuccessfulApiCall(response);
}
@When("Admin makes asset externalization request by Loan external ID with unique ownerExternalId, user-generated transferExternalId and the following data:")
public void createAssetExternalizationRequestByLoanExternalIdUserGeneratedExtId(DataTable table) throws IOException {
// if user created transferExternalId previously, it will use that, otherwise create a new one
String transferExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED);
if (transferExternalId == null) {
transferExternalId = Utils.randomNameGenerator("TestTransferExtId_", 3);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED, transferExternalId);
}
createAssetExternalizationRequestByLoanExternalId(table, transferExternalId);
}
@When("Admin makes asset externalization request by Loan external ID with unique ownerExternalId, system-generated transferExternalId and the following data:")
public void createAssetExternalizationRequestByLoanExternalIdSystemGeneratedExtId(DataTable table) throws IOException {
createAssetExternalizationRequestByLoanExternalId(table, null);
}
private void createAssetExternalizationRequestByLoanExternalId(DataTable table, String transferExternalId) throws IOException {
List<List<String>> data = table.asLists();
List<String> transferData = data.get(1);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.body().getResourceExternalId();
PostInitiateTransferRequest request = new PostInitiateTransferRequest();
if (transferData.get(0).equals(TRANSACTION_TYPE_BUYBACK)) {
request.settlementDate(transferData.get(1))//
.transferExternalId(transferExternalId)//
.dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi
.transferRequestWithLoanExternalId(loanExternalId, request, transferData.get(0)).execute();
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE, response);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_BUYBACK_TRANSFER_EXTERNAL_ID_FROM_RESPONSE,
response.body().getResourceExternalId());
ErrorHelper.checkSuccessfulApiCall(response);
} else if (transferData.get(0).equals(TRANSACTION_TYPE_SALE)) {
String ownerExternalId = Utils.randomNameGenerator(OWNER_EXTERNAL_ID_PREFIX, 3);
request.settlementDate(transferData.get(1))//
.ownerExternalId(ownerExternalId)//
.transferExternalId(transferExternalId)//
.purchasePriceRatio(transferData.get(2))//
.dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi
.transferRequestWithLoanExternalId(loanExternalId, request, transferData.get(0)).execute();
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE, response);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_SALES_TRANSFER_EXTERNAL_ID_FROM_RESPONSE,
response.body().getResourceExternalId());
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID, ownerExternalId);
ErrorHelper.checkSuccessfulApiCall(response);
} else {
throw new IllegalStateException(String.format("%s is not supported Asset externalization transaction", transferData.get(0)));
}
}
@Then("Asset externalization response has the correct Loan ID, transferExternalId")
public void checkAssetExternalizationResponse() {
String ownerExternalIdStored = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID);
String transferExternalIdExpected = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<PostInitiateTransferResponse> response = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE);
PostInitiateTransferResponse body = response.body();
Long loanIdActual = body.getSubResourceId();
String transferExternalIdActual = body.getResourceExternalId();
log.info("loanId: {}", loanId);
log.info("ownerExternalIdStored: {}", ownerExternalIdStored);
log.info("transferExternalId generated by user: {}", transferExternalIdExpected);
log.info("transferExternalIdActual: {}", transferExternalIdActual);
assertThat(loanIdActual).as(ErrorMessageHelper.wrongDataInAssetExternalizationResponse(loanIdActual, loanId)).isEqualTo(loanId);
assertThat(body.getResourceId()).isNotNull();
if (transferExternalIdExpected != null) {
assertThat(transferExternalIdActual)
.as(ErrorMessageHelper.wrongDataInAssetExternalizationResponse(transferExternalIdActual, transferExternalIdExpected))
.isEqualTo(transferExternalIdExpected);
} else {
assertThat(transferExternalIdActual).isNotEmpty();
}
}
@Then("Fetching Asset externalization details by loan id gives numberOfElements: {int} with correct ownerExternalId and the following data:")
public void checkAssetExternalizationDetailsByLoanId(int numberOfElements, DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null).execute();
ErrorHelper.checkSuccessfulApiCall(response);
checkExternalAssetDetails(loanId, null, response, numberOfElements, table);
}
@Then("Fetching Asset externalization details by loan external id gives numberOfElements: {int} with correct ownerExternalId and the following data:")
public void checkAssetExternalizationDetailsByLoanExternalId(int numberOfElements, DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.body().getResourceExternalId();
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(null, null, loanExternalId, null, null).execute();
ErrorHelper.checkSuccessfulApiCall(response);
checkExternalAssetDetails(null, loanExternalId, response, numberOfElements, table);
}
@Then("Fetching Asset externalization details by transfer external id gives numberOfElements: {int} with correct ownerExternalId and the following data:")
public void checkAssetExternalizationDetailsByTransferExternalId(int numberOfElements, DataTable table) throws IOException {
String transferExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_SALES_TRANSFER_EXTERNAL_ID_FROM_RESPONSE);
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(transferExternalId, null, null, null, null)
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
checkExternalAssetDetails(null, null, response, numberOfElements, table);
}
@Then("Asset externalization details has the generated transferExternalId")
public void checkGeneratedTransferExternalId() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<PostInitiateTransferResponse> assetExtResponse = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE);
String transferExternalIdExpected = assetExtResponse.body().getResourceExternalId();
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null).execute();
ErrorHelper.checkSuccessfulApiCall(response);
List<ExternalTransferData> content = response.body().getContent();
content.forEach(e -> {
assertThat(e.getTransferExternalId()).as(ErrorMessageHelper
.wrongDataInAssetExternalizationTransferExternalId(e.getTransferExternalId(), transferExternalIdExpected))
.isEqualTo(transferExternalIdExpected);
});
}
private void checkExternalAssetDetails(Long loanId, String loanExternalId, Response<PageExternalTransferData> response,
int numberOfElements, DataTable table) {
PageExternalTransferData body = response.body();
Integer numberOfElementsActual = body.getNumberOfElements();
List<ExternalTransferData> content = body.getContent();
String ownerExternalIdStored = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID);
String transferExternalId;
List<List<String>> data = table.asLists();
for (int i = 1; i < data.size(); i++) {
List<String> expectedValues = data.get(i);
String transactionType = expectedValues.get(5);
if (transactionType.equals(ExternalTransferData.StatusEnum.BUYBACK.getValue())) {
transferExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_BUYBACK_TRANSFER_EXTERNAL_ID_FROM_RESPONSE);
} else {
transferExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_SALES_TRANSFER_EXTERNAL_ID_FROM_RESPONSE);
}
expectedValues.add(ownerExternalIdStored);
expectedValues.add(loanId == null ? null : String.valueOf(loanId));
expectedValues.add(loanExternalId);
expectedValues.add(transferExternalId);
List<List<String>> actualValuesList = content.stream().map(t -> {
List<String> actualValues = new ArrayList<>();
actualValues.add(t.getSettlementDate() == null ? null : FORMATTER.format(t.getSettlementDate()));
actualValues.add(t.getPurchasePriceRatio() == null ? null : t.getPurchasePriceRatio());
actualValues.add(t.getStatus() == null ? null : String.valueOf(t.getStatus()));
actualValues.add(t.getEffectiveFrom() == null ? null : FORMATTER.format(t.getEffectiveFrom()));
actualValues.add(t.getEffectiveTo() == null ? null : FORMATTER.format(t.getEffectiveTo()));
actualValues.add(transactionType);
actualValues.add(t.getOwner().getExternalId() == null ? null : t.getOwner().getExternalId());
actualValues.add(loanId == null ? null : String.valueOf(t.getLoan().getLoanId()));
actualValues.add(loanExternalId == null ? null : t.getLoan().getExternalId());
actualValues.add(t.getTransferExternalId());
return actualValues;
}).collect(Collectors.toList());
boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValues));
assertThat(numberOfElementsActual)
.as(ErrorMessageHelper.wrongTotalFilteredRecordsInAssetExternalizationDetails(numberOfElementsActual, numberOfElements))
.isEqualTo(numberOfElements);
assertThat(containsExpectedValues).as(ErrorMessageHelper.wrongValueInExternalAssetDetails(i, actualValuesList, expectedValues))
.isTrue();
}
}
@Then("BUYBACK transaction results a {int} error and proper error message when its settlementDate is earlier than the original settlementDate")
public void buybackDateError(int errorCodeExpected, DataTable table) throws IOException {
List<List<String>> data = table.asLists();
List<String> transferData = data.get(1);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
String transferExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_FROM_RESPONSE);
PostInitiateTransferRequest request = new PostInitiateTransferRequest()//
.settlementDate(transferData.get(1))//
.transferExternalId(transferExternalId)//
.dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi
.transferRequestWithLoanId(loanId, request, transferData.get(0)).execute();
PageExternalTransferData transfers = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null).execute().body();
String settlementDateOriginal = FORMATTER.format(transfers.getContent().get(0).getSettlementDate());
String errorMessageExpected = String.format(
"This loan cannot be bought back, settlement date is earlier than effective transfer settlement date: %s",
settlementDateOriginal);
String errorToString = response.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(errorToString, ErrorResponse.class);
String errorMessageActual = errorResponse.getDeveloperMessage();
int errorCodeActual = response.code();
assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected);
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.isEqualTo(errorMessageExpected);
log.info("ERROR CODE: {}", errorCodeActual);
log.info("ERROR MESSAGE: {}", errorMessageActual);
}
@Then("Asset externalization transaction with the following data results a {int} error and {string} error message")
public void transactionError(int errorCodeExpected, String errorMessageType, DataTable table) throws IOException {
List<List<String>> data = table.asLists();
List<String> transferData = data.get(1);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostInitiateTransferRequest request = new PostInitiateTransferRequest();
if (transferData.get(0).equals(TRANSACTION_TYPE_BUYBACK)) {
request.settlementDate(transferData.get(1))//
.transferExternalId(null)//
.dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);//
} else if (transferData.get(0).equals(TRANSACTION_TYPE_SALE)) {
String ownerExternalId = Utils.randomNameGenerator(OWNER_EXTERNAL_ID_PREFIX, 3);
request.settlementDate(transferData.get(1))//
.ownerExternalId(ownerExternalId)//
.transferExternalId(null)//
.purchasePriceRatio(transferData.get(2))//
.dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);//
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID, ownerExternalId);
} else {
throw new IllegalStateException(String.format("%s is not supported Asset externalization transaction", transferData.get(0)));
}
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi
.transferRequestWithLoanId(loanId, request, transferData.get(0)).execute();
AssetExternalizationErrorMessage errorMsgType = AssetExternalizationErrorMessage.valueOf(errorMessageType);
String errorMessageExpected = errorMsgType.getValue();
String errorToString = response.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(errorToString, ErrorResponse.class);
String errorMessageActual = errorResponse.getDeveloperMessage();
int errorCodeActual = response.code();
assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected);
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.isEqualTo(errorMessageExpected);
log.info("ERROR CODE: {}", errorCodeActual);
log.info("ERROR MESSAGE: {}", errorMessageActual);
}
@Then("Asset externalization SALES transaction with ownerExternalId = null and the following data results a {int} error and {string} error message")
public void transactionErrorSalesOwnerNull(int errorCodeExpected, String errorMessageType, DataTable table) throws IOException {
List<List<String>> data = table.asLists();
List<String> transferData = data.get(1);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostInitiateTransferRequest request = new PostInitiateTransferRequest()//
.settlementDate(transferData.get(0))//
.ownerExternalId(null)//
.transferExternalId(null)//
.purchasePriceRatio(transferData.get(1))//
.dateFormat(DATE_FORMAT_ASSET_EXT)//
.locale(DEFAULT_LOCALE);//
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi
.transferRequestWithLoanId(loanId, request, TRANSACTION_TYPE_SALE).execute();
AssetExternalizationErrorMessage errorMsgType = AssetExternalizationErrorMessage.valueOf(errorMessageType);
String errorMessageExpected = errorMsgType.getValue();
String errorToString = response.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(errorToString, ErrorResponse.class);
String errorMessageActual = errorResponse.getDeveloperMessage();
int errorCodeActual = response.code();
assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected);
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.isEqualTo(errorMessageExpected);
log.info("ERROR CODE: {}", errorCodeActual);
log.info("ERROR MESSAGE: {}", errorMessageActual);
}
@Then("The latest asset externalization transaction with {string} status has the following TRANSFER Journal entries:")
public void checkJournalEntriesTransaction(String status, DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Long lastTransferIdByStatus = getLastTransferIdByStatus(loanId, status);
Response<ExternalOwnerTransferJournalEntryData> journalEntriesOfTransfer = externalAssetOwnersApi
.getJournalEntriesOfTransfer(lastTransferIdByStatus, null, null).execute();
List<JournalEntryData> content = journalEntriesOfTransfer.body().getJournalEntryData().getContent();
List<List<String>> data = table.asLists();
int linesExpected = data.size() - 1;
for (int i = 1; i < data.size(); i++) {
List<String> expectedValues = data.get(i);
List<List<String>> actualValuesList = content.stream().map(t -> {
List<String> actualValues = new ArrayList<>();
actualValues.add(t.getGlAccountType().getValue());
actualValues.add(t.getGlAccountCode());
actualValues.add(t.getGlAccountName());
actualValues.add(t.getEntryType().getValue());
actualValues.add(t.getAmount().setScale(2, RoundingMode.HALF_DOWN).toString());
return actualValues;
}).collect(Collectors.toList());
boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValues));
assertThat(containsExpectedValues)
.as(ErrorMessageHelper.wrongValueInLineInAssetExternalizationJournalEntry(i, actualValuesList, expectedValues))
.isTrue();
int linesActual = journalEntriesOfTransfer.body().getJournalEntryData().getNumberOfElements();
assertThat(linesActual).as(ErrorMessageHelper.wrongNumberOfLinesInAssetExternalizationJournalEntry(linesActual, linesExpected))
.isEqualTo(linesExpected);
}
log.info("loanId: {}", journalEntriesOfTransfer.body().getTransferData().getLoan().getLoanId());
log.info("ownerExternalId: {}", journalEntriesOfTransfer.body().getTransferData().getOwner().getExternalId());
log.info("transferId: {}", lastTransferIdByStatus);
log.info("transferExternalId: {}", journalEntriesOfTransfer.body().getTransferData().getTransferExternalId());
}
private Long getLastTransferIdByStatus(Long loanId, String status) throws IOException {
Response<PageExternalTransferData> transfersResponse = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null)
.execute();
List<ExternalTransferData> content = transfersResponse.body().getContent();
ExternalTransferData result = content.stream().filter(t -> status.equals(t.getStatus().getValue()))
.reduce((first, second) -> second)
.orElseThrow(() -> new IllegalStateException(String.format("No Journal entry found with status: %s", status)));
return result.getTransferId();
}
private Long getLastTransferId(Long loanId) throws IOException {
Response<PageExternalTransferData> transfersResponse = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null)
.execute();
List<ExternalTransferData> content = transfersResponse.body().getContent();
ExternalTransferData result = content.stream().reduce((first, second) -> second)
.orElseThrow(() -> new IllegalStateException("transfersResponse.body().getContent() is empty"));
return result.getTransferId();
}
@Then("The asset external owner has the following OWNER Journal entries:")
public void checkJournalEntriesOwner(DataTable table) throws IOException {
String ownerExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID);
Response<ExternalOwnerJournalEntryData> journalEntriesOfOwner = externalAssetOwnersApi
.getJournalEntriesOfOwner(ownerExternalId, null, null).execute();
List<JournalEntryData> content = journalEntriesOfOwner.body().getJournalEntryData().getContent();
List<List<String>> data = table.asLists();
int linesExpected = data.size() - 1;
for (int i = 1; i < data.size(); i++) {
List<String> expectedValues = data.get(i);
List<List<String>> actualValuesList = content.stream().map(t -> {
List<String> actualValues = new ArrayList<>();
actualValues.add(t.getGlAccountType().getValue());
actualValues.add(t.getGlAccountCode());
actualValues.add(t.getGlAccountName());
actualValues.add(t.getEntryType().getValue());
actualValues.add(t.getAmount().setScale(2, RoundingMode.HALF_DOWN).toString());
return actualValues;
}).collect(Collectors.toList());
boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValues));
assertThat(containsExpectedValues)
.as(ErrorMessageHelper.wrongValueInLineInAssetExternalizationJournalEntry(i, actualValuesList, expectedValues))
.isTrue();
int linesActual = journalEntriesOfOwner.body().getJournalEntryData().getNumberOfElements();
assertThat(linesActual).as(ErrorMessageHelper.wrongNumberOfLinesInAssetExternalizationJournalEntry(linesActual, linesExpected))
.isEqualTo(linesExpected);
}
log.info("ownerExternalId: {}", journalEntriesOfOwner.body().getOwnerData().getExternalId());
}
@Then("LoanOwnershipTransferBusinessEvent is created")
public void loanOwnershipTransferBusinessEventCheck() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Long transferId = getLastTransferId(loanId);
eventCheckHelper.loanOwnershipTransferBusinessEventCheck(loanId, transferId);
}
@Then("LoanOwnershipTransferBusinessEvent with transfer status: {string} and transfer status reason {string} is created")
public void loanOwnershipTransferBusinessEventCheck(String transferStatus, String transferStatusReason) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Long transferId = getLastTransferId(loanId);
eventCheckHelper.loanOwnershipTransferBusinessEventWithStatusCheck(loanId, transferId, transferStatus, transferStatusReason);
}
@Then("LoanAccountSnapshotBusinessEvent is created")
public void loanAccountSnapshotBusinessEventCheck() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Long transferId = getLastTransferId(loanId);
eventCheckHelper.loanAccountSnapshotBusinessEventCheck(loanId, transferId);
}
@Then("Asset externalization response {string} has the correct Loan ID, transferExternalId")
public void checkAssetExternalizationResponse(String type) {
String ownerExternalIdStored = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID);
String transferExternalIdExpected = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_BUYBACK_TRANSFER_PREFIX + "_" + type);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<PostInitiateTransferResponse> response = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_RESPONSE);
PostInitiateTransferResponse body = response.body();
Long loanIdActual = body.getSubResourceId();
String transferExternalIdActual = body.getResourceExternalId();
log.info("loanId: {}", loanId);
log.info("ownerExternalIdStored: {}", ownerExternalIdStored);
log.info("transferExternalId generated by user: {}", transferExternalIdExpected);
log.info("transferExternalIdActual: {}", transferExternalIdActual);
assertThat(loanIdActual).as(ErrorMessageHelper.wrongDataInAssetExternalizationResponse(loanIdActual, loanId)).isEqualTo(loanId);
assertThat(body.getResourceId()).isNotNull();
if (transferExternalIdExpected != null) {
assertThat(transferExternalIdActual)
.as(ErrorMessageHelper.wrongDataInAssetExternalizationResponse(transferExternalIdActual, transferExternalIdExpected))
.isEqualTo(transferExternalIdExpected);
} else {
assertThat(transferExternalIdActual).isNotEmpty();
}
}
@When("Admin makes asset externalization request for type {string} by Loan ID with unique ownerExternalId, user-generated transferExternalId and the following data:")
public void createAssetExternalizationRequestByLoanIdUserGeneratedExtId(String type, DataTable table) throws IOException {
// if user created transferExternalId previously, it will use that, otherwise create a new one
String transferExternalId = testContext()
.get(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED + "_" + type);
if (transferExternalId == null) {
transferExternalId = Utils.randomNameGenerator("TestTransferExtId_", 3);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED + "_" + type, transferExternalId);
}
createAssetExternalizationRequestByLoanId(table, transferExternalId);
}
@When("Admin send {string} command to the transaction type {string}")
public void adminTransactionCommandTheWithType(String command, String type) throws IOException {
String transferExternalId = testContext()
.get(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED + "_" + type);
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi.transferRequestWithId1(transferExternalId, command)
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
}
@When("Admin send {string} command to the transaction type {string} will throw error")
public void adminTransactionCommandTheWithTypeThrowError(String command, String type) throws IOException {
String transferExternalId = testContext()
.get(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED + "_" + type);
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi.transferRequestWithId1(transferExternalId, command)
.execute();
ErrorHelper.checkFailedApiCall(response, 403);
}
@Then("Fetching Asset externalization details by loan id gives numberOfElements: {int} with correct ownerExternalId, ignore transactionExternalId and contain the following data:")
public void checkAssetExternalizationDetailsByLoanIdIgnoreTransactionExternalId(int numberOfElements, DataTable table)
throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<PageExternalTransferData> response = externalAssetOwnersApi.getTransfers(null, loanId, null, null, null).execute();
ErrorHelper.checkSuccessfulApiCall(response);
checkExternalAssetDetailsIgnoreTransferExternalId(loanId, null, response, numberOfElements, table);
}
private void checkExternalAssetDetailsIgnoreTransferExternalId(Long loanId, String loanExternalId,
Response<PageExternalTransferData> response, int numberOfElements, DataTable table) {
PageExternalTransferData body = response.body();
Integer numberOfElementsActual = body.getNumberOfElements();
List<ExternalTransferData> content = body.getContent();
String ownerExternalIdStored = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID);
List<List<String>> data = table.asLists();
for (int i = 1; i < data.size(); i++) {
List<String> expectedValues = data.get(i);
expectedValues.add(ownerExternalIdStored);
expectedValues.add(loanId == null ? null : String.valueOf(loanId));
expectedValues.add(loanExternalId);
List<List<String>> actualValuesList = content.stream().map(t -> {
List<String> actualValues = new ArrayList<>();
actualValues.add(t.getSettlementDate() == null ? null : FORMATTER.format(t.getSettlementDate()));
actualValues.add(t.getPurchasePriceRatio() == null ? null : t.getPurchasePriceRatio());
actualValues.add(t.getStatus() == null ? null : String.valueOf(t.getStatus()));
actualValues.add(t.getEffectiveFrom() == null ? null : FORMATTER.format(t.getEffectiveFrom()));
actualValues.add(t.getEffectiveTo() == null ? null : FORMATTER.format(t.getEffectiveTo()));
actualValues.add(t.getOwner().getExternalId() == null ? null : t.getOwner().getExternalId());
actualValues.add(loanId == null ? null : String.valueOf(t.getLoan().getLoanId()));
actualValues.add(loanExternalId == null ? null : t.getLoan().getExternalId());
return actualValues;
}).collect(Collectors.toList());
boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValues));
assertThat(numberOfElementsActual)
.as(ErrorMessageHelper.wrongTotalFilteredRecordsInAssetExternalizationDetails(numberOfElementsActual, numberOfElements))
.isEqualTo(numberOfElements);
assertThat(containsExpectedValues).as(ErrorMessageHelper.wrongValueInExternalAssetDetails(i, actualValuesList, expectedValues))
.isTrue();
}
}
@When("Admin send {string} command on {string} transaction it will throw an error")
public void adminSendCommandAndItWillThrowError(String command, String transactionType) throws IOException {
String transferExternalId;
if (transactionType.equals(ExternalTransferData.StatusEnum.BUYBACK.getValue())) {
transferExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_BUYBACK_TRANSFER_EXTERNAL_ID_FROM_RESPONSE);
} else {
transferExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_SALES_TRANSFER_EXTERNAL_ID_FROM_RESPONSE);
}
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi.transferRequestWithId1(transferExternalId, command)
.execute();
ErrorHelper.checkFailedApiCall(response, 403);
}
@When("Admin send {string} command on {string} transaction")
public void adminSendCommand(String command, String transactionType) throws IOException {
String transferExternalId;
if (transactionType.equals(ExternalTransferData.StatusEnum.BUYBACK.getValue())) {
transferExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_BUYBACK_TRANSFER_EXTERNAL_ID_FROM_RESPONSE);
} else {
transferExternalId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_SALES_TRANSFER_EXTERNAL_ID_FROM_RESPONSE);
}
Response<PostInitiateTransferResponse> response = externalAssetOwnersApi.transferRequestWithId1(transferExternalId, command)
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
}
@When("Admin makes asset externalization request for type {string} by Loan ID with unique ownerExternalId, force generated transferExternalId and without change test owner with following data:")
public void createAssetExternalizationRequestByLoanIdUserGeneratedExtIdForceTransferIdNoTestOwner(String type, DataTable table)
throws IOException {
// if user created transferExternalId previously, it will use that, otherwise create a new one
String transferExternalId = Utils.randomNameGenerator("TestTransferExtId_", 3);
testContext().set(TestContextKey.ASSET_EXTERNALIZATION_TRANSFER_EXTERNAL_ID_USER_GENERATED + "_" + type, transferExternalId);
createAssetExternalizationRequestByLoanId(table, transferExternalId, false);
}
}
@@ -0,0 +1,64 @@
/**
* 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.test.stepdef.common;
import static org.assertj.core.api.Assertions.assertThat;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import org.apache.fineract.client.models.BusinessDateResponse;
import org.apache.fineract.client.services.BusinessDateManagementApi;
import org.apache.fineract.test.helper.BusinessDateHelper;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class BusinessDateStepDef extends AbstractStepDef {
@Autowired
private BusinessDateHelper businessDateHelper;
@Autowired
private BusinessDateManagementApi businessDateManagementApi;
@When("Admin sets the business date to {string}")
public void setBusinessDate(String businessDate) throws IOException {
businessDateHelper.setBusinessDate(businessDate);
}
@When("Admin sets the business date to the actual date")
public void setBusinessDateToday() throws IOException {
businessDateHelper.setBusinessDateToday();
}
@Then("Admin checks that the business date is correctly set to {string}")
public void checkBusinessDate(String businessDate) throws IOException {
Response<BusinessDateResponse> businessDateResponse = businessDateManagementApi.getBusinessDate(BusinessDateHelper.BUSINESS_DATE)
.execute();
ErrorHelper.checkSuccessfulApiCall(businessDateResponse);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d MMMM yyyy");
LocalDate localDate = LocalDate.parse(businessDate, formatter);
assertThat(businessDateResponse.body().getDate()).isEqualTo(localDate);
}
}
@@ -0,0 +1,152 @@
/**
* 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.test.stepdef.common;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.fineract.client.models.BusinessStep;
import org.apache.fineract.client.models.UpdateBusinessStepConfigRequest;
import org.apache.fineract.client.services.BusinessStepConfigurationApi;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class BusinessStepStepDef extends AbstractStepDef {
private static final String WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS = "LOAN_CLOSE_OF_BUSINESS";
private static final String BUSINESS_STEP_NAME_APPLY_CHARGE_TO_OVERDUE_LOANS = "APPLY_CHARGE_TO_OVERDUE_LOANS";
private static final String BUSINESS_STEP_NAME_LOAN_DELINQUENCY_CLASSIFICATION = "LOAN_DELINQUENCY_CLASSIFICATION";
private static final String BUSINESS_STEP_NAME_CHECK_LOAN_REPAYMENT_DUE = "CHECK_LOAN_REPAYMENT_DUE";
private static final String BUSINESS_STEP_NAME_CHECK_LOAN_REPAYMENT_OVERDUE = "CHECK_LOAN_REPAYMENT_OVERDUE";
private static final String BUSINESS_STEP_NAME_UPDATE_LOAN_ARREARS_AGING = "UPDATE_LOAN_ARREARS_AGING";
private static final String BUSINESS_STEP_NAME_ADD_PERIODIC_ACCRUAL_ENTRIES = "ADD_PERIODIC_ACCRUAL_ENTRIES";
private static final String BUSINESS_STEP_NAME_EXTERNAL_ASSET_OWNER_TRANSFER = "EXTERNAL_ASSET_OWNER_TRANSFER";
private static final String BUSINESS_STEP_NAME_CHECK_DUE_INSTALLMENTS = "CHECK_DUE_INSTALLMENTS";
@Autowired
private BusinessStepConfigurationApi businessStepConfigurationApi;
@Given("Admin puts EXTERNAL_ASSET_OWNER_TRANSFER job into LOAN_CLOSE_OF_BUSINESS workflow")
public void putExternalAssetOwnerTransferJobInCOB() throws IOException {
BusinessStep applyChargeToOverdueLoans = new BusinessStep().stepName(BUSINESS_STEP_NAME_APPLY_CHARGE_TO_OVERDUE_LOANS).order(1L);
BusinessStep loanDelinquencyClassification = new BusinessStep().stepName(BUSINESS_STEP_NAME_LOAN_DELINQUENCY_CLASSIFICATION)
.order(2L);
BusinessStep checkLoanRepaymentDue = new BusinessStep().stepName(BUSINESS_STEP_NAME_CHECK_LOAN_REPAYMENT_DUE).order(3L);
BusinessStep checkLoanRepaymentOverdue = new BusinessStep().stepName(BUSINESS_STEP_NAME_CHECK_LOAN_REPAYMENT_OVERDUE).order(4L);
BusinessStep updateLoanArrearsAging = new BusinessStep().stepName(BUSINESS_STEP_NAME_UPDATE_LOAN_ARREARS_AGING).order(5L);
BusinessStep addPeriodicAccrualEntries = new BusinessStep().stepName(BUSINESS_STEP_NAME_ADD_PERIODIC_ACCRUAL_ENTRIES).order(6L);
BusinessStep externalAssetOwnerTransfer = new BusinessStep().stepName(BUSINESS_STEP_NAME_EXTERNAL_ASSET_OWNER_TRANSFER).order(7L);
List<BusinessStep> businessSteps = new ArrayList<>();
businessSteps.add(applyChargeToOverdueLoans);
businessSteps.add(loanDelinquencyClassification);
businessSteps.add(checkLoanRepaymentDue);
businessSteps.add(checkLoanRepaymentOverdue);
businessSteps.add(updateLoanArrearsAging);
businessSteps.add(addPeriodicAccrualEntries);
businessSteps.add(externalAssetOwnerTransfer);
UpdateBusinessStepConfigRequest request = new UpdateBusinessStepConfigRequest().businessSteps(businessSteps);
Response<Void> response = businessStepConfigurationApi.updateJobBusinessStepConfig(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS, request)
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
}
@Then("Admin removes EXTERNAL_ASSET_OWNER_TRANSFER job from LOAN_CLOSE_OF_BUSINESS workflow")
public void removeExternalAssetOwnerTransferJobInCOB() throws IOException {
BusinessStep applyChargeToOverdueLoans = new BusinessStep().stepName(BUSINESS_STEP_NAME_APPLY_CHARGE_TO_OVERDUE_LOANS).order(1L);
BusinessStep loanDelinquencyClassification = new BusinessStep().stepName(BUSINESS_STEP_NAME_LOAN_DELINQUENCY_CLASSIFICATION)
.order(2L);
BusinessStep checkLoanRepaymentDue = new BusinessStep().stepName(BUSINESS_STEP_NAME_CHECK_LOAN_REPAYMENT_DUE).order(3L);
BusinessStep checkLoanRepaymentOverdue = new BusinessStep().stepName(BUSINESS_STEP_NAME_CHECK_LOAN_REPAYMENT_OVERDUE).order(4L);
BusinessStep updateLoanArrearsAging = new BusinessStep().stepName(BUSINESS_STEP_NAME_UPDATE_LOAN_ARREARS_AGING).order(5L);
BusinessStep addPeriodicAccrualEntries = new BusinessStep().stepName(BUSINESS_STEP_NAME_ADD_PERIODIC_ACCRUAL_ENTRIES).order(6L);
List<BusinessStep> businessSteps = new ArrayList<>();
businessSteps.add(applyChargeToOverdueLoans);
businessSteps.add(loanDelinquencyClassification);
businessSteps.add(checkLoanRepaymentDue);
businessSteps.add(checkLoanRepaymentOverdue);
businessSteps.add(updateLoanArrearsAging);
businessSteps.add(addPeriodicAccrualEntries);
UpdateBusinessStepConfigRequest request = new UpdateBusinessStepConfigRequest().businessSteps(businessSteps);
Response<Void> response = businessStepConfigurationApi.updateJobBusinessStepConfig(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS, request)
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
}
@Given("Admin puts CHECK_DUE_INSTALLMENTS job into LOAN_CLOSE_OF_BUSINESS workflow")
public void putCheckDueInstallmentsJobInCOB() throws IOException {
BusinessStep applyChargeToOverdueLoans = new BusinessStep().stepName(BUSINESS_STEP_NAME_APPLY_CHARGE_TO_OVERDUE_LOANS).order(1L);
BusinessStep loanDelinquencyClassification = new BusinessStep().stepName(BUSINESS_STEP_NAME_LOAN_DELINQUENCY_CLASSIFICATION)
.order(2L);
BusinessStep checkLoanRepaymentDue = new BusinessStep().stepName(BUSINESS_STEP_NAME_CHECK_LOAN_REPAYMENT_DUE).order(3L);
BusinessStep checkLoanRepaymentOverdue = new BusinessStep().stepName(BUSINESS_STEP_NAME_CHECK_LOAN_REPAYMENT_OVERDUE).order(4L);
BusinessStep updateLoanArrearsAging = new BusinessStep().stepName(BUSINESS_STEP_NAME_UPDATE_LOAN_ARREARS_AGING).order(5L);
BusinessStep addPeriodicAccrualEntries = new BusinessStep().stepName(BUSINESS_STEP_NAME_ADD_PERIODIC_ACCRUAL_ENTRIES).order(6L);
BusinessStep checkDueInstallments = new BusinessStep().stepName(BUSINESS_STEP_NAME_CHECK_DUE_INSTALLMENTS).order(7L);
List<BusinessStep> businessSteps = new ArrayList<>();
businessSteps.add(applyChargeToOverdueLoans);
businessSteps.add(loanDelinquencyClassification);
businessSteps.add(checkLoanRepaymentDue);
businessSteps.add(checkLoanRepaymentOverdue);
businessSteps.add(updateLoanArrearsAging);
businessSteps.add(addPeriodicAccrualEntries);
businessSteps.add(checkDueInstallments);
UpdateBusinessStepConfigRequest request = new UpdateBusinessStepConfigRequest().businessSteps(businessSteps);
Response<Void> response = businessStepConfigurationApi.updateJobBusinessStepConfig(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS, request)
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
}
@Then("Admin removes CHECK_DUE_INSTALLMENTS job from LOAN_CLOSE_OF_BUSINESS workflow")
public void removeCheckDueInstallmentsJobInCOB() throws IOException {
BusinessStep applyChargeToOverdueLoans = new BusinessStep().stepName(BUSINESS_STEP_NAME_APPLY_CHARGE_TO_OVERDUE_LOANS).order(1L);
BusinessStep loanDelinquencyClassification = new BusinessStep().stepName(BUSINESS_STEP_NAME_LOAN_DELINQUENCY_CLASSIFICATION)
.order(2L);
BusinessStep checkLoanRepaymentDue = new BusinessStep().stepName(BUSINESS_STEP_NAME_CHECK_LOAN_REPAYMENT_DUE).order(3L);
BusinessStep checkLoanRepaymentOverdue = new BusinessStep().stepName(BUSINESS_STEP_NAME_CHECK_LOAN_REPAYMENT_OVERDUE).order(4L);
BusinessStep updateLoanArrearsAging = new BusinessStep().stepName(BUSINESS_STEP_NAME_UPDATE_LOAN_ARREARS_AGING).order(5L);
BusinessStep addPeriodicAccrualEntries = new BusinessStep().stepName(BUSINESS_STEP_NAME_ADD_PERIODIC_ACCRUAL_ENTRIES).order(6L);
List<BusinessStep> businessSteps = new ArrayList<>();
businessSteps.add(applyChargeToOverdueLoans);
businessSteps.add(loanDelinquencyClassification);
businessSteps.add(checkLoanRepaymentDue);
businessSteps.add(checkLoanRepaymentOverdue);
businessSteps.add(updateLoanArrearsAging);
businessSteps.add(addPeriodicAccrualEntries);
UpdateBusinessStepConfigRequest request = new UpdateBusinessStepConfigRequest().businessSteps(businessSteps);
Response<Void> response = businessStepConfigurationApi.updateJobBusinessStepConfig(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS, request)
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
}
}
@@ -33,6 +33,7 @@ import org.apache.fineract.test.helper.CodeHelper;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.Utils;
import org.apache.fineract.test.messaging.event.EventCheckHelper;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
@@ -49,6 +50,9 @@ public class ClientStepDef extends AbstractStepDef {
@Autowired
private ClientRequestFactory clientRequestFactory;
@Autowired
private EventCheckHelper eventCheckHelper;
@When("Admin creates a client with random data")
public void createClientRandomFirstNameLastName() throws IOException {
PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest();
@@ -56,6 +60,8 @@ public class ClientStepDef extends AbstractStepDef {
Response<PostClientsResponse> response = clientApi.create6(clientsRequest).execute();
ErrorHelper.checkSuccessfulApiCall(response);
testContext().set(TestContextKey.CLIENT_CREATE_RESPONSE, response);
eventCheckHelper.clientEventCheck(response);
}
@When("Admin creates a second client with random data")
@@ -65,6 +71,8 @@ public class ClientStepDef extends AbstractStepDef {
Response<PostClientsResponse> response = clientApi.create6(clientsRequest).execute();
ErrorHelper.checkSuccessfulApiCall(response);
testContext().set(TestContextKey.CLIENT_CREATE_SECOND_CLIENT_RESPONSE, response);
eventCheckHelper.clientEventCheck(response);
}
@When("Admin creates a client with Firstname {string} and Lastname {string}")
@@ -114,5 +122,7 @@ public class ClientStepDef extends AbstractStepDef {
Response<PostClientsResponse> response = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE);
assertThat(response.isSuccessful()).as(ErrorMessageHelper.requestFailed(response)).isTrue();
eventCheckHelper.clientEventCheck(response);
}
}
@@ -0,0 +1,73 @@
/**
* 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.test.stepdef.common;
import io.cucumber.java.en.Then;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ClassInfoList;
import io.github.classgraph.ScanResult;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.Event;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@SuppressWarnings({ "unchecked", "rawtypes" })
public class EventStepDef extends AbstractStepDef {
@Autowired
private EventAssertion eventAssertion;
@Then("{string} event has been raised for the loan")
public void assertEventRaisedForLoan(String eventType) {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Class<? extends Event> eventTypeClazz = resolveEventType(eventType);
eventAssertion.assertEventRaised(eventTypeClazz, loanId);
}
@Then("No new event with type {string} has been raised for the loan")
public void assertEventNotRaisedForLoan(String eventType) {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Class<? extends Event> eventTypeClazz = resolveEventType(eventType);
eventAssertion.assertEventNotRaised(eventTypeClazz, loanId);
}
private Class<? extends Event> resolveEventType(String eventType) {
String fullyQualifiedEventClassName = null;
try (ScanResult scanResult = new ClassGraph().enableAllInfo().acceptPackages("org.apache.fineract.test").scan()) {
ClassInfoList matchingClasses = scanResult.getAllClasses().filter(i -> i.getSimpleName().equalsIgnoreCase(eventType));
if (matchingClasses.isEmpty()) {
throw new IllegalArgumentException("Cannot find event with type name: " + eventType);
}
fullyQualifiedEventClassName = matchingClasses.get(0).getName();
}
Class<? extends Event> eventTypeClazz = null;
try {
eventTypeClazz = (Class<? extends Event>) Class.forName(fullyQualifiedEventClassName);
} catch (Exception e) {
throw new RuntimeException("Cannot find event with type name: " + eventType, e);
}
return eventTypeClazz;
}
}
@@ -0,0 +1,225 @@
/**
* 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.test.stepdef.common;
import static org.assertj.core.api.Assertions.assertThat;
import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Then;
import java.io.IOException;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.GetJournalEntriesTransactionIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
import org.apache.fineract.client.models.JournalEntryTransactionItem;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.JournalEntriesApi;
import org.apache.fineract.client.services.LoansApi;
import org.apache.fineract.test.data.TransactionType;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j
public class JournalEntriesStepDef extends AbstractStepDef {
public static final String DATE_FORMAT = "dd MMMM yyyy";
@Autowired
private LoansApi loansApi;
@Autowired
private JournalEntriesApi journalEntriesApi;
@Then("Loan Transactions tab has a {string} transaction with date {string} which has the following Journal entries:")
public void journalEntryDataCheck(String transactionType, String transactionDate, DataTable table) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
TransactionType transactionType1 = TransactionType.valueOf(transactionType);
String transactionTypeExpected = transactionType1.getValue();
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions();
List<GetLoansLoanIdTransactions> transactionsMatch = transactions.stream()
.filter(t -> transactionDate.equals(formatter.format(t.getDate()))
&& transactionTypeExpected.equals(t.getType().getCode().substring(20)))
.collect(Collectors.toList());
List<List<JournalEntryTransactionItem>> journalLinesActualList = transactionsMatch.stream().map(t -> {
String transactionId = "L" + t.getId();
Response<GetJournalEntriesTransactionIdResponse> journalEntryDataResponse = null;
try {
journalEntryDataResponse = journalEntriesApi.retrieveAll1(//
null, //
null, //
null, //
null, //
null, //
null, //
null, //
transactionId, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
true//
).execute();
ErrorHelper.checkSuccessfulApiCall(journalEntryDataResponse);
} catch (IOException e) {
log.error("Exception", e);
}
return journalEntryDataResponse.body().getPageItems();
}).collect(Collectors.toList());
List<List<String>> data = table.asLists();
for (int i = 1; i < data.size(); i++) {
List<List<List<String>>> possibleActualValuesList = new ArrayList<>();
List<String> expectedValues = data.get(i);
boolean containsAnyExpected = false;
for (int j = 0; j < journalLinesActualList.size(); j++) {
List<JournalEntryTransactionItem> journalLinesActual = journalLinesActualList.get(j);
List<List<String>> actualValuesList = journalLinesActual.stream().map(t -> {
List<String> actualValues = new ArrayList<>();
actualValues.add(t.getGlAccountType().getValue() == null ? null : t.getGlAccountType().getValue());
actualValues.add(t.getGlAccountCode() == null ? null : t.getGlAccountCode());
actualValues.add(t.getGlAccountName() == null ? null : t.getGlAccountName());
actualValues.add("DEBIT".equals(t.getEntryType().getValue()) ? String.valueOf(t.getAmount()) : null);
actualValues.add("CREDIT".equals(t.getEntryType().getValue()) ? String.valueOf(t.getAmount()) : null);
return actualValues;
}).collect(Collectors.toList());
possibleActualValuesList.add(actualValuesList);
boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValues));
if (containsExpectedValues) {
containsAnyExpected = true;
}
}
assertThat(containsAnyExpected)
.as(ErrorMessageHelper.wrongValueInLineInJournalEntries(i, possibleActualValuesList, expectedValues)).isTrue();
}
}
@Then("In Loan transactions the replayed {string} transaction with date {string} has a reverted transaction pair with the following Journal entries:")
public void revertedJournalEntryDataCheck(String transactionType, String transactionDate, DataTable table) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
TransactionType transactionType1 = TransactionType.valueOf(transactionType);
String transactionTypeExpected = transactionType1.getValue();
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions();
List<GetLoansLoanIdTransactions> transactionsMatch = transactions.stream()
.filter(t -> transactionDate.equals(formatter.format(t.getDate()))
&& transactionTypeExpected.equals(t.getType().getCode().substring(20)))
.collect(Collectors.toList());
List<String> transactionIdList = transactionsMatch.stream().flatMap(t -> t.getTransactionRelations().stream()
.filter(e -> "REPLAYED".equals(e.getRelationType())).map(c -> "L" + c.getToLoanTransaction().toString()))
.collect(Collectors.toList());
List<List<JournalEntryTransactionItem>> journalLinesActualList = transactionIdList.stream().map(t -> {
Response<GetJournalEntriesTransactionIdResponse> journalEntryDataResponse = null;
try {
journalEntryDataResponse = journalEntriesApi.retrieveAll1(//
null, //
null, //
null, //
null, //
null, //
null, //
null, //
t, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
null, //
true//
).execute();
ErrorHelper.checkSuccessfulApiCall(journalEntryDataResponse);
} catch (IOException e) {
log.error("Exception", e);
}
return journalEntryDataResponse.body().getPageItems();
}).collect(Collectors.toList());
List<List<String>> data = table.asLists();
for (int i = 1; i < data.size(); i++) {
List<List<List<String>>> possibleActualValuesList = new ArrayList<>();
List<String> expectedValues = data.get(i);
boolean containsAnyExpected = false;
for (int j = 0; j < journalLinesActualList.size(); j++) {
List<JournalEntryTransactionItem> journalLinesActual = journalLinesActualList.get(j);
List<List<String>> actualValuesList = journalLinesActual.stream().map(t -> {
List<String> actualValues = new ArrayList<>();
actualValues.add(t.getGlAccountType().getValue() == null ? null : t.getGlAccountType().getValue());
actualValues.add(t.getGlAccountCode() == null ? null : t.getGlAccountCode());
actualValues.add(t.getGlAccountName() == null ? null : t.getGlAccountName());
actualValues.add("DEBIT".equals(t.getEntryType().getValue()) ? String.valueOf(t.getAmount()) : null);
actualValues.add("CREDIT".equals(t.getEntryType().getValue()) ? String.valueOf(t.getAmount()) : null);
return actualValues;
}).collect(Collectors.toList());
possibleActualValuesList.add(actualValuesList);
boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValues));
if (containsExpectedValues) {
containsAnyExpected = true;
}
}
assertThat(containsAnyExpected)
.as(ErrorMessageHelper.wrongValueInLineInJournalEntries(i, possibleActualValuesList, expectedValues)).isTrue();
}
}
}
@@ -0,0 +1,59 @@
/**
* 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.test.stepdef.common;
import io.cucumber.java.en.And;
import io.cucumber.java.en.When;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.test.data.job.DefaultJob;
import org.apache.fineract.test.service.JobService;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.springframework.beans.factory.annotation.Autowired;
@Slf4j
public class SchedulerStepDef extends AbstractStepDef {
@Autowired
private JobService jobService;
@And("Admin runs the Add Accrual Transactions job")
public void runAccrualTransaction() {
jobService.executeAndWait(DefaultJob.ADD_ACCRUAL_TRANSACTIONS);
}
@And("Admin runs the Add Periodic Accrual Transactions job")
public void runPeriodicAccrualTransaction() {
jobService.executeAndWait(DefaultJob.ADD_PERIODIC_ACCRUAL_TRANSACTIONS);
}
@And("Admin runs the Increase Business Date by 1 day job")
public void runIncreaseBusinessDate() {
jobService.executeAndWait(DefaultJob.INCREASE_BUSINESS_DAY);
}
@And("Admin runs the Loan Delinquency Classification job")
public void runLoanDelinquencyClassification() {
jobService.executeAndWait(DefaultJob.LOAN_DELINQUENCY_CLASSIFICATION);
}
@When("Admin runs COB job")
public void runCOB() {
jobService.executeAndWait(DefaultJob.LOAN_COB);
}
}
@@ -0,0 +1,87 @@
/**
* 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.test.stepdef.common;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.fineract.client.models.GetRolesResponse;
import org.apache.fineract.client.models.PostRolesRequest;
import org.apache.fineract.client.models.PostRolesResponse;
import org.apache.fineract.client.models.PostUsersRequest;
import org.apache.fineract.client.models.PostUsersResponse;
import org.apache.fineract.client.models.PutRolesRoleIdPermissionsRequest;
import org.apache.fineract.client.models.PutRolesRoleIdPermissionsResponse;
import org.apache.fineract.client.services.RolesApi;
import org.apache.fineract.client.services.UsersApi;
import org.apache.fineract.test.api.ApiProperties;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.Utils;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class UserStepDef extends AbstractStepDef {
private static final String EMAIL = "test@test.com";
@Autowired
private RolesApi rolesApi;
@Autowired
private UsersApi usersApi;
@Autowired
private ApiProperties apiProperties;
@When("Admin creates new user with {string} username, {string} role name and given permissions:")
public void createUserWithUsernameAndRoles(String username, String roleName, List<String> permissions) throws IOException {
Response<List<GetRolesResponse>> retrieveAllRolesResponse = rolesApi.retrieveAllRoles().execute();
ErrorHelper.checkSuccessfulApiCall(retrieveAllRolesResponse);
PostRolesRequest newRoleRequest = new PostRolesRequest().name(Utils.randomNameGenerator(roleName, 8)).description(roleName);
Response<PostRolesResponse> createNewRole = rolesApi.createRole(newRoleRequest).execute();
ErrorHelper.checkSuccessfulApiCall(createNewRole);
Long roleId = createNewRole.body().getResourceId();
Map<String, Boolean> permissionMap = new HashMap<>();
permissions.forEach(role -> permissionMap.put(role, true));
PutRolesRoleIdPermissionsRequest putRolesRoleIdPermissionsRequest = new PutRolesRoleIdPermissionsRequest()
.permissions(permissionMap);
Response<PutRolesRoleIdPermissionsResponse> updateRolePermissionResponse = rolesApi
.updateRolePermissions(roleId, putRolesRoleIdPermissionsRequest).execute();
ErrorHelper.checkSuccessfulApiCall(updateRolePermissionResponse);
PostUsersRequest postUsersRequest = new PostUsersRequest() //
.username(Utils.randomNameGenerator(username, 8)) //
.email(EMAIL) //
.firstname(username) //
.lastname(username) //
.sendPasswordToEmail(Boolean.FALSE) //
.officeId(1L) //
.password(apiProperties.getPassword()) //
.repeatPassword(apiProperties.getPassword()) //
.roles(List.of(roleId));
Response<PostUsersResponse> createUserResponse = usersApi.create15(postUsersRequest).execute();
ErrorHelper.checkSuccessfulApiCall(createUserResponse);
testContext().set(TestContextKey.CREATED_SIMPLE_USER_RESPONSE, createUserResponse);
}
}
@@ -0,0 +1,89 @@
/**
* 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.test.stepdef.loan;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import org.apache.fineract.client.models.InlineJobRequest;
import org.apache.fineract.client.models.InlineJobResponse;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.ExternalEventConfigurationApi;
import org.apache.fineract.client.services.InlineJobApi;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.assetexternalization.LoanAccountCustomSnapshotEvent;
import org.apache.fineract.test.messaging.event.loan.repayment.LoanRepaymentDueEvent;
import org.apache.fineract.test.messaging.event.loan.repayment.LoanRepaymentOverdueEvent;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class InlineCOBStepDef extends AbstractStepDef {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("dd MMMM yyyy");
@Autowired
private InlineJobApi inlineJobApi;
@Autowired
private EventAssertion eventAssertion;
@Autowired
ExternalEventConfigurationApi eventConfigurationApi;
@When("Admin runs inline COB job for Loan")
public void runInlineCOB() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
InlineJobRequest inlineJobRequest = new InlineJobRequest().addLoanIdsItem(loanId);
Response<InlineJobResponse> inlineJobResponse = inlineJobApi.executeInlineJob("LOAN_COB", inlineJobRequest).execute();
ErrorHelper.checkSuccessfulApiCall(inlineJobResponse);
}
@Then("Loan Repayment Due Business Event is created")
public void checkLoanRepaymentDueBusinessEventCreated() {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
eventAssertion.assertEventRaised(LoanRepaymentDueEvent.class, loanId);
}
@Then("Loan Repayment Overdue Business Event is created")
public void checkLoanRepaymentOverdueBusinessEventCreated() {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
eventAssertion.assertEventRaised(LoanRepaymentOverdueEvent.class, loanId);
}
@Then("LoanAccountCustomSnapshotBusinessEvent is created with business date {string}")
public void checkLoanRepaymentDueBusinessEventCreatedWithBusinessDate(String expectedBusinessDate) {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
LocalDate expectedBusinessDateParsed = LocalDate.parse(expectedBusinessDate, FORMATTER);
eventAssertion.assertEvent(LoanAccountCustomSnapshotEvent.class, loanId).isRaisedOnBusinessDate(expectedBusinessDateParsed);
}
}
@@ -0,0 +1,113 @@
/**
* 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.test.stepdef.loan;
import static org.assertj.core.api.Assertions.assertThat;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.time.LocalDate;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.GetLoanAccountLockResponse;
import org.apache.fineract.client.models.GetOldestCOBProcessedLoanResponse;
import org.apache.fineract.client.models.LoanAccountLock;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.DefaultApi;
import org.apache.fineract.client.services.LoanAccountLockApi;
import org.apache.fineract.client.services.LoanCobCatchUpApi;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j
public class LoanCOBStepDef extends AbstractStepDef {
@Autowired
private LoanCobCatchUpApi loanCobCatchUpApi;
@Autowired
private LoanAccountLockApi loanAccountLockApi;
@Autowired
private DefaultApi defaultApi;
@Then("The cobProcessedDate of the oldest loan processed by COB is more than 1 day earlier than cobBusinessDate")
public void checkOldestCOBProcessed() throws IOException {
Response<GetOldestCOBProcessedLoanResponse> response = loanCobCatchUpApi.getOldestCOBProcessedLoan().execute();
ErrorHelper.checkSuccessfulApiCall(response);
LocalDate cobDate = response.body().getCobBusinessDate();
LocalDate cobDateMinusOne = cobDate.minusDays(1);
LocalDate cobProcessedDate = response.body().getCobProcessedDate();
log.info("cobDateMinusOne: {}", cobDateMinusOne);
log.info("cobProcessedDate: {}", cobProcessedDate);
boolean result = cobDateMinusOne.isAfter(cobProcessedDate);
assertThat(result).as(ErrorMessageHelper.wrongLastCOBProcessedLoanDate(cobProcessedDate, cobDateMinusOne)).isTrue();
}
@Then("There are no locked loan accounts")
public void listOfLockedLoansEmpty() throws IOException {
Response<GetLoanAccountLockResponse> response = loanAccountLockApi.retrieveLockedAccounts(0, 1000).execute();
ErrorHelper.checkSuccessfulApiCall(response);
int size = response.body().getContent().size();
assertThat(size).as(ErrorMessageHelper.listOfLockedLoansNotEmpty(response)).isEqualTo(0);
log.info("Size of List of the locked loans: {}", size);
}
@Then("The loan account is not locked")
public void loanIsNotInListOfLockedLoans() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Long targetLoanId = loanResponse.body().getLoanId();
Response<GetLoanAccountLockResponse> response = loanAccountLockApi.retrieveLockedAccounts(0, 1000).execute();
ErrorHelper.checkSuccessfulApiCall(response);
List<LoanAccountLock> content = response.body().getContent();
boolean contains = content.stream()//
.map(LoanAccountLock::getLoanId)//
.anyMatch(targetLoanId::equals);//
assertThat(contains).as(ErrorMessageHelper.listOfLockedLoansContainsLoan(targetLoanId, response)).isFalse();
}
@When("Admin places a lock on loan account with an error message")
public void placeLockOnLoanAccountWithErrorMessage() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<Void> response = defaultApi.placeLockOnLoanAccount(loanId, "LOAN_COB_CHUNK_PROCESSING", "TestError").execute();
ErrorHelper.checkSuccessfulApiCall(response);
}
@When("Admin places a lock on loan account WITHOUT an error message")
public void placeLockOnLoanAccountNoErrorMessage() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<Void> response = defaultApi.placeLockOnLoanAccount(loanId, "LOAN_COB_CHUNK_PROCESSING").execute();
ErrorHelper.checkSuccessfulApiCall(response);
}
}
@@ -0,0 +1,188 @@
/**
* 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.test.stepdef.loan;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import org.apache.fineract.client.models.BusinessDateResponse;
import org.apache.fineract.client.models.GetLoansLoanIdLoanChargeData;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdRequest;
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.BusinessDateManagementApi;
import org.apache.fineract.client.services.LoanChargesApi;
import org.apache.fineract.client.services.LoanTransactionsApi;
import org.apache.fineract.client.services.LoansApi;
import org.apache.fineract.client.util.JSON;
import org.apache.fineract.test.data.ChargeProductType;
import org.apache.fineract.test.factory.LoanRequestFactory;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class LoanChargeAdjustmentStepDef extends AbstractStepDef {
public static final String DATE_FORMAT = "dd MMMM yyyy";
private static final Gson GSON = new JSON().getGson();
@Autowired
private LoanChargesApi loanChargesApi;
@Autowired
private LoansApi loansApi;
@Autowired
private LoanTransactionsApi loanTransactionsApi;
@Autowired
private BusinessDateManagementApi businessDateManagementApi;
@When("Admin makes a charge adjustment for the last {string} type charge which is due on {string} with {double} EUR transaction amount and externalId {string}")
public void makeLoanChargeAdjustment(String chargeTypeEnum, String date, Double transactionAmount, String externalId)
throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "charges", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
Long transactionId = getTransactionIdForLastChargeMetConditions(chargeTypeEnum, date, loanDetailsResponse);
makeChargeAdjustmentCall(loanId, transactionId, externalId, transactionAmount);
}
@When("Admin makes a charge adjustment for the last {string} type charge which is due on {string} with transaction amount higher than the available charge amount")
public void loanChargeAdjustmentFailedOnWrongAmount(String chargeTypeEnum, String date) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "charges", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
Long transactionId = getTransactionIdForLastChargeMetConditions(chargeTypeEnum, date, loanDetailsResponse);
PostLoansLoanIdChargesChargeIdRequest chargeAdjustmentRequest = LoanRequestFactory.defaultChargeAdjustmentRequest().amount(8.0)
.externalId("");
Response<PostLoansLoanIdChargesChargeIdResponse> chargeAdjustmentResponseFail = loanChargesApi
.executeLoanCharge2(loanId, transactionId, chargeAdjustmentRequest, "adjustment").execute();
String string = chargeAdjustmentResponseFail.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(string, ErrorResponse.class);
Integer httpStatusCodeActual = errorResponse.getHttpStatusCode();
String developerMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage();
Integer httpStatusCodeExpected = 403;
String developerMessageExpected = "Transaction amount cannot be higher than the available charge amount for adjustment: 7.000000";
assertThat(httpStatusCodeActual)
.as(ErrorMessageHelper.wrongErrorCodeInFailedChargeAdjustment(httpStatusCodeActual, httpStatusCodeExpected))
.isEqualTo(httpStatusCodeExpected);
assertThat(developerMessageActual)
.as(ErrorMessageHelper.wrongErrorMessageInFailedChargeAdjustment(developerMessageActual, developerMessageExpected))
.isEqualTo(developerMessageExpected);
}
@When("Admin reverts the charge adjustment which was raised on {string} with {double} EUR transaction amount")
public void loanChargeAdjustmentUndo(String transactionDate, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
Long transactionId = getTransactionIdForTransactionMetConditions(transactionDate, transactionAmount, loanDetailsResponse);
Response<List<BusinessDateResponse>> businessDateResponse = businessDateManagementApi.getBusinessDates().execute();
LocalDate businessDate = businessDateResponse.body().get(0).getDate();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
String businessDateActual = formatter.format(businessDate);
PostLoansLoanIdTransactionsTransactionIdRequest chargeAdjustmentUndoRequest = LoanRequestFactory
.defaultChargeAdjustmentTransactionUndoRequest().transactionDate(businessDateActual);
Response<PostLoansLoanIdTransactionsResponse> chargeAdjustmentUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, transactionId, chargeAdjustmentUndoRequest, "").execute();
ErrorHelper.checkSuccessfulApiCall(chargeAdjustmentUndoResponse);
}
private Long getTransactionIdForTransactionMetConditions(String transactionDate, double transactionAmount,
Response<GetLoansLoanIdResponse> loanDetailsResponse) {
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions();
GetLoansLoanIdTransactions transactionMetConditions = new GetLoansLoanIdTransactions();
for (int i = 0; i < transactions.size(); i++) {
LocalDate date = transactions.get(i).getDate();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
String dateActual = formatter.format(date);
Double amountActual = transactions.get(i).getAmount();
if (dateActual.equals(transactionDate) && amountActual.equals(transactionAmount)) {
transactionMetConditions = transactions.get(i);
break;
}
}
return transactionMetConditions.getId();
}
private void makeChargeAdjustmentCall(Long loanId, Long transactionId, String externalId, double transactionAmount) throws IOException {
PostLoansLoanIdChargesChargeIdRequest chargeAdjustmentRequest = LoanRequestFactory.defaultChargeAdjustmentRequest()
.amount(transactionAmount).externalId(externalId);
Response<PostLoansLoanIdChargesChargeIdResponse> chargeAdjustmentResponse = loanChargesApi
.executeLoanCharge2(loanId, transactionId, chargeAdjustmentRequest, "adjustment").execute();
testContext().set(TestContextKey.LOAN_CHARGE_ADJUSTMENT_RESPONSE, chargeAdjustmentResponse);
ErrorHelper.checkSuccessfulApiCall(chargeAdjustmentResponse);
}
private Long getTransactionIdForLastChargeMetConditions(String chargeTypeEnum, String date,
Response<GetLoansLoanIdResponse> loanDetailsResponse) {
List<GetLoansLoanIdLoanChargeData> charges = loanDetailsResponse.body().getCharges();
ChargeProductType chargeType = ChargeProductType.valueOf(chargeTypeEnum);
Long chargeProductId = chargeType.getValue();
List<GetLoansLoanIdLoanChargeData> resultList = new ArrayList<>();
charges.forEach(charge -> {
Long chargeId = charge.getChargeId();
LocalDate dueDate = charge.getDueDate();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
String chargeDueDate = formatter.format(dueDate);
if (chargeId.equals(chargeProductId) && chargeDueDate.equals(date)) {
resultList.add(charge);
}
});
GetLoansLoanIdLoanChargeData lastChargeResult = resultList.get(resultList.size() - 1);
return lastChargeResult.getId();
}
}
@@ -0,0 +1,174 @@
/**
* 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.test.stepdef.loan;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.fineract.avro.loan.v1.LoanTransactionDataV1;
import org.apache.fineract.avro.loan.v1.LoanTransactionEnumDataV1;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
import org.apache.fineract.client.models.GetLoansType;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.LoanTransactionsApi;
import org.apache.fineract.client.services.LoansApi;
import org.apache.fineract.test.data.paymenttype.DefaultPaymentType;
import org.apache.fineract.test.data.paymenttype.PaymentTypeResolver;
import org.apache.fineract.test.factory.LoanRequestFactory;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.loan.LoanBalanceChangedEvent;
import org.apache.fineract.test.messaging.event.loan.transaction.LoanChargebackTransactionEvent;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class LoanChargeBackStepDef extends AbstractStepDef {
@Autowired
private LoanTransactionsApi loanTransactionsApi;
@Autowired
private LoansApi loansApi;
@Autowired
private EventAssertion eventAssertion;
@Autowired
private PaymentTypeResolver paymentTypeResolver;
@When("Admin makes {string} chargeback with {double} EUR transaction amount")
public void makeLoanChargeback(String repaymentType, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
Long transactionId = Long.valueOf(repaymentResponse.body().getResourceId());
makeChargebackCall(loanId, transactionId, repaymentType, transactionAmount);
}
@When("Admin makes {string} chargeback with {double} EUR transaction amount for Payment nr. {double}")
public void makeLoanChargebackForPayment(String repaymentType, double transactionAmount, double paymentNr) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
List<GetLoansLoanIdTransactions> transactions = loanDetails.body().getTransactions();
List<Long> transactionIdList = new ArrayList<>();
for (GetLoansLoanIdTransactions f : transactions) {
String code = f.getType().getCode();
if (code.equals("loanTransactionType.repayment")) {
transactionIdList.add(f.getId());
}
}
Collections.sort(transactionIdList);
Long transactionId = transactionIdList.get((int) paymentNr - 1);
makeChargebackCall(loanId, transactionId, repaymentType, transactionAmount);
}
@When("Admin makes {string} chargeback with {double} EUR transaction amount for Downpayment nr. {double}")
public void makeLoanChargebackForDownpaymentayment(String repaymentType, double transactionAmount, double paymentNr)
throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
List<GetLoansLoanIdTransactions> transactions = loanDetails.body().getTransactions();
List<Long> transactionIdList = new ArrayList<>();
for (GetLoansLoanIdTransactions f : transactions) {
String code = f.getType().getCode();
if (code.equals("loanTransactionType.downPayment")) {
transactionIdList.add(f.getId());
}
}
Collections.sort(transactionIdList);
Long transactionId = transactionIdList.get((int) paymentNr - 1);
makeChargebackCall(loanId, transactionId, repaymentType, transactionAmount);
}
private void makeChargebackCall(Long loanId, Long transactionId, String repaymentType, double transactionAmount) throws IOException {
DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType);
Long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
PostLoansLoanIdTransactionsTransactionIdRequest chargebackRequest = LoanRequestFactory.defaultChargebackRequest()
.paymentTypeId(paymentTypeValue).transactionAmount(transactionAmount);
Response<PostLoansLoanIdTransactionsResponse> chargebackResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, transactionId, chargebackRequest, "chargeback").execute();
testContext().set(TestContextKey.LOAN_CHARGEBACK_RESPONSE, chargebackResponse);
ErrorHelper.checkSuccessfulApiCall(chargebackResponse);
checkEvents(chargebackResponse);
}
private void checkEvents(Response<PostLoansLoanIdTransactionsResponse> chargebackResponse) throws IOException {
PostLoansLoanIdTransactionsResponse responseBody = chargebackResponse.body();
Long loanId = responseBody.getLoanId();
eventAssertion.assertEvent(LoanBalanceChangedEvent.class, loanId).extractingData(data -> data.getId()).isEqualTo(loanId);
checkLoanChargebackTransactionEvent(responseBody);
}
private void checkLoanChargebackTransactionEvent(PostLoansLoanIdTransactionsResponse chargebackResponse) throws IOException {
// get loanId and transactionId
Long loanId = chargebackResponse.getLoanId();
long transactionId = Long.valueOf(chargebackResponse.getResourceId());
// retrieve transaction details
Response<GetLoansLoanIdTransactionsTransactionIdResponse> transactionResponse = loanTransactionsApi
.retrieveTransaction(loanId, transactionId, "").execute();
ErrorHelper.checkSuccessfulApiCall(transactionResponse);
GetLoansLoanIdTransactionsTransactionIdResponse transactionResponseBody = transactionResponse.body();
// Get transaction type from response
GetLoansType transactionType = transactionResponseBody.getType();
// Build expected avro transaction type from api response type
LoanTransactionEnumDataV1 expectedEventTransactionType = LoanTransactionEnumDataV1.newBuilder()
.setId(transactionType.getId().intValue()).setCode(transactionType.getCode()).setValue("Chargeback")
.setDisbursement(transactionType.getDisbursement()).setRepaymentAtDisbursement(transactionType.getRepaymentAtDisbursement())
.setRepayment(transactionType.getRepayment()).setMerchantIssuedRefund(false).setPayoutRefund(false).setGoodwillCredit(false)
.setChargeRefund(false).setContra(transactionType.getContra()).setWaiveInterest(transactionType.getWaiveInterest())
.setWaiveCharges(transactionType.getWaiveCharges()).setAccrual(false).setWriteOff(transactionType.getWriteOff())
.setRecoveryRepayment(transactionType.getRecoveryRepayment()).setInitiateTransfer(false).setApproveTransfer(false)
.setWithdrawTransfer(false).setRejectTransfer(false).setChargePayment(false).setRefund(false).setRefundForActiveLoans(false)
.setCreditBalanceRefund(false).setChargeback(true).build();
// verify payload for loanId, transactionId, transactionType, amount
eventAssertion.assertEvent(LoanChargebackTransactionEvent.class, transactionId).extractingData(LoanTransactionDataV1::getLoanId)
.isEqualTo(loanId).extractingData(LoanTransactionDataV1::getType).isEqualTo(expectedEventTransactionType)
.extractingBigDecimal(LoanTransactionDataV1::getAmount).isEqualTo(BigDecimal.valueOf(transactionResponseBody.getAmount()));
}
}
@@ -0,0 +1,296 @@
/**
* 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.test.stepdef.loan;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.And;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.time.format.DateTimeFormatter;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.avro.loan.v1.LoanChargeDataV1;
import org.apache.fineract.client.models.GetLoansLoanIdChargesChargeIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdRequest;
import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdResponse;
import org.apache.fineract.client.models.PostLoansLoanIdChargesRequest;
import org.apache.fineract.client.models.PostLoansLoanIdChargesResponse;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.models.PutChargeTransactionChangesRequest;
import org.apache.fineract.client.models.PutChargeTransactionChangesResponse;
import org.apache.fineract.client.services.LoanChargesApi;
import org.apache.fineract.client.services.LoanTransactionsApi;
import org.apache.fineract.client.services.LoansApi;
import org.apache.fineract.client.util.JSON;
import org.apache.fineract.test.data.ChargeProductType;
import org.apache.fineract.test.data.ErrorMessageType;
import org.apache.fineract.test.factory.LoanChargeRequestFactory;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.loan.charge.LoanAddChargeEvent;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j
public class LoanChargeStepDef extends AbstractStepDef {
public static final String DEFAULT_DATE_FORMAT = "dd MMMM yyyy";
public static final String DATE_FORMAT_EVENTS = "yyyy-MM-dd";
public static final Double DEFAULT_CHARGE_FEE_FLAT = 10D;
private static final Gson GSON = new JSON().getGson();
@Autowired
private LoanChargesApi loanChargesApi;
@Autowired
private LoanTransactionsApi loanTransactionsApi;
@Autowired
private LoansApi loansApi;
@Autowired
private EventAssertion eventAssertion;
@When("Admin adds {string} due date charge with {string} due date and {double} EUR transaction amount")
public void addChargeDueDate(String chargeType, String transactionDate, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType);
Long chargeTypeId = chargeProductType.getValue();
if (chargeTypeId.equals(ChargeProductType.LOAN_DISBURSEMENT_PERCENTAGE_FEE.getValue())
|| chargeTypeId.equals(ChargeProductType.LOAN_TRANCHE_DISBURSEMENT_PERCENTAGE_FEE.getValue())
|| chargeTypeId.equals(ChargeProductType.LOAN_INSTALLMENT_PERCENTAGE_FEE.getValue())) {
throw new IllegalStateException(String.format("The requested %s charge is NOT due date type, cannot be used here", chargeType));
}
PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest().chargeId(chargeTypeId)
.dueDate(transactionDate).amount(transactionAmount);
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi.executeLoanCharge(loanId, loanIdChargesRequest, "")
.execute();
ErrorHelper.checkSuccessfulApiCall(loanChargeResponse);
testContext().set(TestContextKey.ADD_DUE_DATE_CHARGE_RESPONSE, loanChargeResponse);
addChargeEventCheck(loanChargeResponse);
}
@When("Admin adds {string} charge with {double} % of transaction amount")
public void addChargePercentage(String chargeType, double transactionPercentageAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType);
Long chargeTypeId = chargeProductType.getValue();
if (!chargeTypeId.equals(ChargeProductType.LOAN_DISBURSEMENT_PERCENTAGE_FEE.getValue())
&& !chargeTypeId.equals(ChargeProductType.LOAN_TRANCHE_DISBURSEMENT_PERCENTAGE_FEE.getValue())
&& !chargeTypeId.equals(ChargeProductType.LOAN_INSTALLMENT_PERCENTAGE_FEE.getValue())) {
throw new IllegalStateException(String.format("The requested %s charge is due date type, cannot be used here", chargeType));
}
PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest().chargeId(chargeTypeId)
.amount(transactionPercentageAmount);
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi.executeLoanCharge(loanId, loanIdChargesRequest, "")
.execute();
ErrorHelper.checkSuccessfulApiCall(loanChargeResponse);
testContext().set(TestContextKey.ADD_DUE_DATE_CHARGE_RESPONSE, loanChargeResponse);
}
@Then("Admin is not able to add {string} due date charge with {string} due date and {double} EUR transaction amount because the of charged-off account")
public void addChargeDueDateOnChargedOff(String chargeType, String transactionDate, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType);
Long chargeTypeId = chargeProductType.getValue();
PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest().chargeId(chargeTypeId)
.dueDate(transactionDate).amount(transactionAmount);
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi.executeLoanCharge(loanId, loanIdChargesRequest, "")
.execute();
testContext().set(TestContextKey.ADD_DUE_DATE_CHARGE_RESPONSE, loanChargeResponse);
ErrorResponse errorDetails = ErrorResponse.from(loanChargeResponse);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.addChargeForChargeOffLoanCodeMsg()).isEqualTo(403);
assertThat(errorDetails.getSingleError().getDeveloperMessage())
.isEqualTo(ErrorMessageHelper.addChargeForChargeOffLoanFailure(loanId));
}
@And("Admin adds a {double} % Processing charge to the loan with {string} locale on date: {string}")
public void addProcessingFee(double chargeAmount, String locale, String date) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest()
.chargeId(ChargeProductType.LOAN_PERCENTAGE_PROCESSING_FEE.value).amount(chargeAmount).dueDate(date)
.dateFormat(DEFAULT_DATE_FORMAT).locale(locale);
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi.executeLoanCharge(loanId, loanIdChargesRequest, "")
.execute();
ErrorHelper.checkSuccessfulApiCall(loanChargeResponse);
testContext().set(TestContextKey.ADD_PROCESSING_FEE_RESPONSE, loanChargeResponse);
}
@And("Admin adds an NSF fee because of payment bounce with {string} transaction date")
public void addNSFfee(String date) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest()
.chargeId(ChargeProductType.LOAN_NSF_FEE.value).amount(DEFAULT_CHARGE_FEE_FLAT).dueDate(date)
.dateFormat(DEFAULT_DATE_FORMAT);
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi.executeLoanCharge(loanId, loanIdChargesRequest, "")
.execute();
ErrorHelper.checkSuccessfulApiCall(loanChargeResponse);
testContext().set(TestContextKey.ADD_NSF_FEE_RESPONSE, loanChargeResponse);
}
@And("Admin waives charge")
public void waiveCharge() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = testContext().get(TestContextKey.ADD_NSF_FEE_RESPONSE);
Long chargeId = Long.valueOf(loanChargeResponse.body().getResourceId());
PostLoansLoanIdChargesChargeIdRequest waiveRequest = new PostLoansLoanIdChargesChargeIdRequest();
Response<PostLoansLoanIdChargesChargeIdResponse> waiveResponse = loanChargesApi
.executeLoanCharge2(loanId, chargeId, waiveRequest, "waive").execute();
ErrorHelper.checkSuccessfulApiCall(waiveResponse);
testContext().set(TestContextKey.WAIVE_CHARGE_RESPONSE, waiveResponse);
}
@And("Admin waives due date charge")
public void waiveDueDateCharge() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = testContext().get(TestContextKey.ADD_DUE_DATE_CHARGE_RESPONSE);
Long chargeId = Long.valueOf(loanChargeResponse.body().getResourceId());
PostLoansLoanIdChargesChargeIdRequest waiveRequest = new PostLoansLoanIdChargesChargeIdRequest();
Response<PostLoansLoanIdChargesChargeIdResponse> waiveResponse = loanChargesApi
.executeLoanCharge2(loanId, chargeId, waiveRequest, "waive").execute();
ErrorHelper.checkSuccessfulApiCall(waiveResponse);
testContext().set(TestContextKey.WAIVE_CHARGE_RESPONSE, waiveResponse);
}
@And("Admin makes waive undone for charge")
public void undoWaiveForCharge() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
List<GetLoansLoanIdTransactions> transactions = loanDetails.body().getTransactions();
Long transactionId = 0L;
for (GetLoansLoanIdTransactions f : transactions) {
String code = f.getType().getCode();
if (code.equals("loanTransactionType.waiveCharges")) {
transactionId = f.getId();
}
}
PutChargeTransactionChangesRequest undoWaiveRequest = new PutChargeTransactionChangesRequest();
Response<PutChargeTransactionChangesResponse> undoWaiveResponse = loanTransactionsApi
.undoWaiveCharge(loanId, transactionId, undoWaiveRequest).execute();
ErrorHelper.checkSuccessfulApiCall(undoWaiveResponse);
testContext().set(TestContextKey.UNDO_WAIVE_RESPONSE, undoWaiveResponse);
}
@Then("Charge is successfully added to the loan")
public void loanChargeStatus() throws IOException {
Response<PostLoansLoanIdChargesResponse> response = testContext().get(TestContextKey.ADD_NSF_FEE_RESPONSE);
assertThat(response.isSuccessful()).as(ErrorMessageHelper.requestFailed(response)).isTrue();
assertThat(response.code()).as(ErrorMessageHelper.requestFailedWithCode(response)).isEqualTo(200);
}
@Then("Charge is successfully added to the loan with {float} EUR")
public void checkLoanChargeAmount(float chargeAmount) throws IOException {
Response<PostLoansLoanIdChargesResponse> response = testContext().get(TestContextKey.ADD_PROCESSING_FEE_RESPONSE);
Response<GetLoansLoanIdChargesChargeIdResponse> loanChargeAmount = loanChargesApi
.retrieveLoanCharge(response.body().getLoanId(), Long.valueOf(response.body().getResourceId())).execute();
ErrorHelper.checkSuccessfulApiCall(response);
assertThat(loanChargeAmount.body().getAmount()).as("Charge amount is wrong").isEqualTo(chargeAmount);
}
private void addChargeEventCheck(Response<PostLoansLoanIdChargesResponse> loanChargeResponse) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT_EVENTS);
Response<GetLoansLoanIdChargesChargeIdResponse> chargeDetails = loanChargesApi
.retrieveLoanCharge(loanChargeResponse.body().getLoanId(), loanChargeResponse.body().getResourceId()).execute();
GetLoansLoanIdChargesChargeIdResponse body = chargeDetails.body();
eventAssertion.assertEvent(LoanAddChargeEvent.class, loanChargeResponse.body().getResourceId())
.extractingData(LoanChargeDataV1::getName).isEqualTo(body.getName())
.extractingData(loanChargeDataV1 -> loanChargeDataV1.getAmount().longValue()).isEqualTo(body.getAmount().longValue())
.extractingData(LoanChargeDataV1::getDueDate).isEqualTo(formatter.format(body.getDueDate()));
}
@Then("Loan charge transaction with the following data results a {int} error and {string} error message")
public void chargeOffTransactionError(int errorCodeExpected, String errorMessageType, DataTable table) throws IOException {
List<List<String>> data = table.asLists();
List<String> chargeData = data.get(1);
String chargeType = chargeData.get(0);
String transactionDate = chargeData.get(1);
Double transactionAmount = Double.valueOf(chargeData.get(2));
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
ErrorMessageType errorMsgType = ErrorMessageType.valueOf(errorMessageType);
String errorMessageExpectedRaw = errorMsgType.getValue();
String errorMessageExpected = String.format(errorMessageExpectedRaw, loanId);
ChargeProductType chargeProductType = ChargeProductType.valueOf(chargeType);
Long chargeTypeId = chargeProductType.getValue();
if (chargeTypeId.equals(ChargeProductType.LOAN_DISBURSEMENT_PERCENTAGE_FEE.getValue())
|| chargeTypeId.equals(ChargeProductType.LOAN_TRANCHE_DISBURSEMENT_PERCENTAGE_FEE.getValue())
|| chargeTypeId.equals(ChargeProductType.LOAN_INSTALLMENT_PERCENTAGE_FEE.getValue())) {
throw new IllegalStateException(String.format("The requested %s charge is NOT due date type, cannot be used here", chargeType));
}
PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest().chargeId(chargeTypeId)
.dueDate(transactionDate).amount(transactionAmount);
Response<PostLoansLoanIdChargesResponse> loanChargeResponse = loanChargesApi.executeLoanCharge(loanId, loanIdChargesRequest, "")
.execute();
int errorCodeActual = loanChargeResponse.code();
String errorBody = loanChargeResponse.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(errorBody, ErrorResponse.class);
String errorMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage();
assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected);
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.isEqualTo(errorMessageExpected);
log.info("ERROR CODE: {}", errorCodeActual);
log.info("ERROR MESSAGE: {}", errorMessageActual);
}
}
@@ -0,0 +1,771 @@
/**
* 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.test.stepdef.loan;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.fineract.avro.loan.v1.LoanAccountDelinquencyRangeDataV1;
import org.apache.fineract.avro.loan.v1.LoanInstallmentDelinquencyBucketDataV1;
import org.apache.fineract.client.models.GetDelinquencyActionsResponse;
import org.apache.fineract.client.models.GetDelinquencyRangesResponse;
import org.apache.fineract.client.models.GetDelinquencyTagHistoryResponse;
import org.apache.fineract.client.models.GetLoansLoanIdDelinquencyPausePeriod;
import org.apache.fineract.client.models.GetLoansLoanIdDelinquencySummary;
import org.apache.fineract.client.models.GetLoansLoanIdLoanInstallmentLevelDelinquency;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetUsersUserIdResponse;
import org.apache.fineract.client.models.PostLoansDelinquencyActionRequest;
import org.apache.fineract.client.models.PostLoansDelinquencyActionResponse;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.models.PostUsersResponse;
import org.apache.fineract.client.services.LoansApi;
import org.apache.fineract.client.services.UsersApi;
import org.apache.fineract.client.util.JSON;
import org.apache.fineract.test.api.ApiProperties;
import org.apache.fineract.test.data.DelinquencyRange;
import org.apache.fineract.test.data.LoanStatus;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.EventCheckHelper;
import org.apache.fineract.test.messaging.event.loan.delinquency.LoanDelinquencyRangeChangeEvent;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j
public class LoanDelinquencyStepDef extends AbstractStepDef {
public static final String DATE_FORMAT = "dd MMMM yyyy";
public static final String DEFAULT_LOCALE = "en";
public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT);
private static final Gson GSON = new JSON().getGson();
@Autowired
private LoansApi loansApi;
@Autowired
private EventAssertion eventAssertion;
@Autowired
private EventCheckHelper eventCheckHelper;
@Autowired
private ApiProperties apiProperties;
@Autowired
private UsersApi usersApi;
@Then("Admin checks that delinquency range is: {string} and has delinquentDate {string}")
public void checkDelinquencyRange(String range, String delinquentDateExpected) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetails);
Integer loanStatus = loanDetails.body().getStatus().getId();
if (!LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.value.equals(loanStatus) && !LoanStatus.APPROVED.value.equals(loanStatus)) {
String delinquentDateExpectedValue = "".equals(delinquentDateExpected) ? null : delinquentDateExpected;
eventAssertion.assertEvent(LoanDelinquencyRangeChangeEvent.class, loanId)//
.extractingData(LoanAccountDelinquencyRangeDataV1::getDelinquentDate)//
.isEqualTo(delinquentDateExpectedValue);//
}
DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(range);
String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue();
String actualDelinquencyRangeValue = DelinquencyRange.NO_DELINQUENCY.value;
GetDelinquencyRangesResponse actualDelinquencyRange = loanDetails.body().getDelinquencyRange();
if (actualDelinquencyRange != null) {
actualDelinquencyRangeValue = actualDelinquencyRange.getClassification();
}
assertThat(actualDelinquencyRangeValue)
.as(ErrorMessageHelper.delinquencyRangeError(actualDelinquencyRangeValue, expectedDelinquencyRangeValue))
.isEqualTo(expectedDelinquencyRangeValue);
}
@Then("Admin checks that {string}th delinquency range is: {string} and added on: {string} and has delinquentDate {string}")
public void checkDelinquencyRange(String nthInList, String range, String addedOnDate, String delinquentDateExpected)
throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
String delinquentDateExpectedValue = "".equals(delinquentDateExpected) ? null : delinquentDateExpected;
eventAssertion.assertEvent(LoanDelinquencyRangeChangeEvent.class, loanId)
.extractingData(LoanAccountDelinquencyRangeDataV1::getDelinquentDate).isEqualTo(delinquentDateExpectedValue);
DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(range);
String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue();
Response<List<GetDelinquencyTagHistoryResponse>> delinquencyHistoryDetails = loansApi.getDelinquencyTagHistory(loanId).execute();
ErrorHelper.checkSuccessfulApiCall(delinquencyHistoryDetails);
String actualDelinquencyRangeValue = DelinquencyRange.NO_DELINQUENCY.value;
String actualDelinquencyAddedOnDate = "";
int i = Integer.parseInt(nthInList) - 1;
GetDelinquencyTagHistoryResponse delinquencyTag = delinquencyHistoryDetails.body().get(i);
if (delinquencyTag != null) {
actualDelinquencyRangeValue = delinquencyTag.getDelinquencyRange().getClassification();
actualDelinquencyAddedOnDate = formatter.format(delinquencyTag.getAddedOnDate());
}
assertThat(actualDelinquencyRangeValue)
.as(ErrorMessageHelper.delinquencyRangeError(actualDelinquencyRangeValue, expectedDelinquencyRangeValue))
.isEqualTo(expectedDelinquencyRangeValue);
assertThat(actualDelinquencyAddedOnDate).as(ErrorMessageHelper.delinquencyRangeError(actualDelinquencyAddedOnDate, addedOnDate))
.isEqualTo(addedOnDate);
}
@Then("Loan delinquency history has the following details:")
public void delinquencyHistoryCheck(DataTable table) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
List<List<String>> dataExpected = table.asLists();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<List<GetDelinquencyTagHistoryResponse>> delinquencyHistoryDetails = loansApi.getDelinquencyTagHistory(loanId).execute();
ErrorHelper.checkSuccessfulApiCall(delinquencyHistoryDetails);
List<GetDelinquencyTagHistoryResponse> body = delinquencyHistoryDetails.body();
for (int i = 0; i < body.size(); i++) {
List<String> line = dataExpected.get(i + 1);
DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(line.get(0));
String classificationExpected = expectedDelinquencyRange.getValue();
String addedOnDateExpected = line.get(1);
String liftedOnDateExpected = line.get(2);
String classificationActual = body.get(i).getDelinquencyRange().getClassification();
String addedOnDateActual = body.get(i).getAddedOnDate() == null ? null : formatter.format(body.get(i).getAddedOnDate());
String liftedOnDateActual = body.get(i).getLiftedOnDate() == null ? null : formatter.format(body.get(i).getLiftedOnDate());
assertThat(classificationActual)
.as(ErrorMessageHelper.wrongDataInDelinquencyHistoryClassification(classificationActual, classificationExpected))
.isEqualTo(classificationExpected);
assertThat(addedOnDateActual)
.as(ErrorMessageHelper.wrongDataInDelinquencyHistoryAddedOnDate(addedOnDateActual, addedOnDateExpected))
.isEqualTo(addedOnDateExpected);
assertThat(liftedOnDateActual)
.as(ErrorMessageHelper.wrongDataInDelinquencyHistoryLiftedOnDate(liftedOnDateActual, liftedOnDateExpected))
.isEqualTo(liftedOnDateExpected);
}
}
@When("Admin initiate a DELINQUENCY PAUSE with startDate: {string} and endDate: {string}")
public void delinquencyPause(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")//
.startDate(startDate)//
.endDate(endDate)//
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
testContext().set(TestContextKey.LOAN_DELINQUENCY_ACTION_RESPONSE, response);
ErrorHelper.checkSuccessfulApiCall(response);
eventCheckHelper.loanAccountDelinquencyPauseChangedBusinessEventCheck(loanId);
}
@When("Created user with CREATE_DELINQUENCY_ACTION permission initiate a DELINQUENCY PAUSE with startDate: {string} and endDate: {string}")
public void delinquencyPauseWithCreatedUser(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")//
.startDate(startDate)//
.endDate(endDate)//
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Map<String, String> headerMap = new HashMap<>();
Response<PostUsersResponse> createUserResponse = testContext().get(TestContextKey.CREATED_SIMPLE_USER_RESPONSE);
Long createdUserId = createUserResponse.body().getResourceId();
Response<GetUsersUserIdResponse> user = usersApi.retrieveOne31(createdUserId).execute();
ErrorHelper.checkSuccessfulApiCall(user);
String authorizationString = user.body().getUsername() + ":" + apiProperties.getPassword();
Base64 base64 = new Base64();
headerMap.put("Authorization",
"Basic " + new String(base64.encode(authorizationString.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8));
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request, headerMap).execute();
testContext().set(TestContextKey.LOAN_DELINQUENCY_ACTION_RESPONSE, response);
ErrorHelper.checkSuccessfulApiCall(response);
eventCheckHelper.loanAccountDelinquencyPauseChangedBusinessEventCheck(loanId);
}
@Then("Created user with no CREATE_DELINQUENCY_ACTION permission gets an error when initiate a DELINQUENCY PAUSE with startDate: {string} and endDate: {string}")
public void delinquencyPauseWithCreatedUserNOPermissionError(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
int errorCodeExpected = 403;
String errorMessageExpected = "User has no authority to CREATE delinquency_actions";
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")//
.startDate(startDate)//
.endDate(endDate)//
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Map<String, String> headerMap = new HashMap<>();
Response<PostUsersResponse> createUserResponse = testContext().get(TestContextKey.CREATED_SIMPLE_USER_RESPONSE);
Long createdUserId = createUserResponse.body().getResourceId();
Response<GetUsersUserIdResponse> user = usersApi.retrieveOne31(createdUserId).execute();
ErrorHelper.checkSuccessfulApiCall(user);
String authorizationString = user.body().getUsername() + ":" + apiProperties.getPassword();
Base64 base64 = new Base64();
headerMap.put("Authorization",
"Basic " + new String(base64.encode(authorizationString.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8));
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request, headerMap).execute();
testContext().set(TestContextKey.LOAN_DELINQUENCY_ACTION_RESPONSE, response);
int errorCodeActual = response.code();
String errorBody = response.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(errorBody, ErrorResponse.class);
String errorMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage();
assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected);
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.isEqualTo(errorMessageExpected);
log.info("ERROR CODE: {}", errorCodeActual);
log.info("ERROR MESSAGE: {}", errorMessageActual);
}
@When("Admin initiate a DELINQUENCY RESUME with startDate: {string}")
public void delinquencyResume(String startDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")//
.startDate(startDate)//
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
testContext().set(TestContextKey.LOAN_DELINQUENCY_ACTION_RESPONSE, response);
ErrorHelper.checkSuccessfulApiCall(response);
eventCheckHelper.loanAccountDelinquencyPauseChangedBusinessEventCheck(loanId);
}
@When("Admin initiate a DELINQUENCY PAUSE by loanExternalId with startDate: {string} and endDate: {string}")
public void delinquencyPauseByLoanExternalId(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.body().getResourceExternalId();
long loanId = loanResponse.body().getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")//
.startDate(startDate)//
.endDate(endDate)//
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction1(loanExternalId, request).execute();
testContext().set(TestContextKey.LOAN_DELINQUENCY_ACTION_RESPONSE, response);
ErrorHelper.checkSuccessfulApiCall(response);
eventCheckHelper.loanAccountDelinquencyPauseChangedBusinessEventCheck(loanId);
}
@When("Admin initiate a DELINQUENCY RESUME by loanExternalId with startDate: {string}")
public void delinquencyResumeByLoanExternalId(String startDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.body().getResourceExternalId();
long loanId = loanResponse.body().getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")//
.startDate(startDate)//
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction1(loanExternalId, request).execute();
testContext().set(TestContextKey.LOAN_DELINQUENCY_ACTION_RESPONSE, response);
ErrorHelper.checkSuccessfulApiCall(response);
eventCheckHelper.loanAccountDelinquencyPauseChangedBusinessEventCheck(loanId);
}
@Then("Delinquency-actions have the following data:")
public void getDelinquencyActionData(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
List<List<String>> data = table.asLists();
int nrOfLinesExpected = data.size() - 1;
Response<List<GetDelinquencyActionsResponse>> response = loansApi.getLoanDelinquencyActions(loanId).execute();
int nrOfLinesActual = response.body().size();
assertThat(nrOfLinesActual)//
.as(ErrorMessageHelper.wrongNumberOfLinesInDelinquencyActions(nrOfLinesActual, nrOfLinesExpected))//
.isEqualTo(nrOfLinesExpected);//
for (int i = 1; i < data.size(); i++) {
List<String> expectedValues = data.get(i);
GetDelinquencyActionsResponse lineActual = response.body().get(i - 1);
List<String> actualValues = new ArrayList<>();
actualValues.add(Objects.requireNonNull(lineActual.getAction()));
actualValues.add(FORMATTER.format(Objects.requireNonNull(lineActual.getStartDate())));
actualValues.add(lineActual.getEndDate() == null ? null : FORMATTER.format(lineActual.getEndDate()));
assertThat(actualValues)//
.as(ErrorMessageHelper.wrongValueInLineDelinquencyActions(i, actualValues, expectedValues))//
.isEqualTo(expectedValues);//
}
}
@Then("Initiating a delinquency-action other than PAUSE or RESUME in action field results an error - startDate: {string}, endDate: {string}")
public void actionFieldError(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("TEST")//
.startDate(startDate)//
.endDate(endDate)//
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400;
String errorMessageExpected = "Invalid Delinquency Action: TEST";
errorMessageAssertation(response, errorCodeExpected, errorMessageExpected);
}
@Then("Initiating a DELINQUENCY PAUSE with startDate before the actual business date results an error - startDate: {string}, endDate: {string}")
public void delinquencyPauseStartDateError(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")//
.startDate(startDate)//
.endDate(endDate)//
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400;
String errorMessageExpected = "Start date of pause period must be in the future";
errorMessageAssertation(response, errorCodeExpected, errorMessageExpected);
}
@Then("Initiating a DELINQUENCY PAUSE on a non-active loan results an error - startDate: {string}, endDate: {string}")
public void delinquencyPauseNonActiveLoanError(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")//
.startDate(startDate)//
.endDate(endDate)//
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400;
String errorMessageExpected = "Delinquency actions can be created only for active loans.";
errorMessageAssertation(response, errorCodeExpected, errorMessageExpected);
}
@Then("Initiating a DELINQUENCY RESUME on a non-active loan results an error - startDate: {string}")
public void delinquencyResumeNonActiveLoanError(String startDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")//
.startDate(startDate)//
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400;
String errorMessageExpected = "Delinquency actions can be created only for active loans.";
errorMessageAssertation(response, errorCodeExpected, errorMessageExpected);
}
@Then("Overlapping PAUSE periods result an error - startDate: {string}, endDate: {string}")
public void delinquencyPauseOverlappingError(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")//
.startDate(startDate)//
.endDate(endDate)//
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400;
String errorMessageExpected = "Delinquency pause period cannot overlap with another pause period";
errorMessageAssertation(response, errorCodeExpected, errorMessageExpected);
}
@Then("Initiating a DELINQUENCY RESUME without an active PAUSE period results an error - startDate: {string}")
public void delinquencyResumeWithoutPauseError(String startDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")//
.startDate(startDate)//
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400;
String errorMessageExpected = "Resume Delinquency Action can only be created during an active pause";
errorMessageAssertation(response, errorCodeExpected, errorMessageExpected);
}
@Then("Initiating a DELINQUENCY RESUME with start date other than actual business date results an error - startDate: {string}")
public void delinquencyResumeStartDateError(String startDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")//
.startDate(startDate)//
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400;
String errorMessageExpected = "Start date of the Resume Delinquency action must be the current business date";
errorMessageAssertation(response, errorCodeExpected, errorMessageExpected);
}
@Then("Initiating a DELINQUENCY RESUME with an endDate results an error - startDate: {string}, endDate: {string}")
public void delinquencyResumeWithEndDateError(String startDate, String endDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")//
.startDate(startDate)//
.endDate(endDate)//
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
Response<PostLoansDelinquencyActionResponse> response = loansApi.createLoanDelinquencyAction(loanId, request).execute();
int errorCodeExpected = 400;
String errorMessageExpected = "Resume Delinquency action can not have end date";
errorMessageAssertation(response, errorCodeExpected, errorMessageExpected);
}
@Then("Installment level delinquency event has correct data")
public void installmentLevelDelinquencyEventCheck() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
eventCheckHelper.installmentLevelDelinquencyRangeChangeEventCheck(loanId);
}
@Then("INSTALLMENT level delinquency is null")
public void installmentLevelDelinquencyNull() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetails);
List<GetLoansLoanIdLoanInstallmentLevelDelinquency> installmentLevelDelinquency = loanDetails.body().getDelinquent()
.getInstallmentLevelDelinquency() == null ? null : loanDetails.body().getDelinquent().getInstallmentLevelDelinquency();
assertThat(installmentLevelDelinquency).isNull();
}
@Then("Loan has the following LOAN level delinquency data:")
public void loanDelinquencyDataCheck(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
List<String> expectedValuesList = table.asLists().get(1);
DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(expectedValuesList.get(0));
String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue();
expectedValuesList.set(0, expectedDelinquencyRangeValue);
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetails);
String actualDelinquencyRangeValue = loanDetails.body().getDelinquencyRange() == null ? "NO_DELINQUENCY"
: loanDetails.body().getDelinquencyRange().getClassification();
GetLoansLoanIdDelinquencySummary delinquent = loanDetails.body().getDelinquent();
List<String> actualValuesList = List.of(actualDelinquencyRangeValue, delinquent.getDelinquentAmount().toString(),
delinquent.getDelinquentDate() == null ? "null" : FORMATTER.format(delinquent.getDelinquentDate()),
delinquent.getDelinquentDays().toString(), delinquent.getPastDueDays().toString());
assertThat(actualValuesList).as(ErrorMessageHelper.wrongValueInLoanLevelDelinquencyData(actualValuesList, expectedValuesList))
.isEqualTo(expectedValuesList);
}
@Then("Loan has the following INSTALLMENT level delinquency data:")
public void loanDelinquencyInstallmentLevelDataCheck(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetails);
List<GetLoansLoanIdLoanInstallmentLevelDelinquency> installmentLevelDelinquency = loanDetails.body().getDelinquent()
.getInstallmentLevelDelinquency();
List<List<String>> data = table.asLists();
assertThat(installmentLevelDelinquency.size())
.as(ErrorMessageHelper.nrOfLinesWrongInInstallmentLevelDelinquencyData(installmentLevelDelinquency.size(), data.size() - 1))
.isEqualTo(data.size() - 1);
for (int i = 1; i < data.size(); i++) {
DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(data.get(i).get(1));
String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue();
List<String> expectedValuesList = data.get(i);
expectedValuesList.set(1, expectedDelinquencyRangeValue);
List<String> actualValuesList = List.of(String.valueOf(installmentLevelDelinquency.get(i - 1).getRangeId()),
installmentLevelDelinquency.get(i - 1).getClassification(),
installmentLevelDelinquency.get(i - 1).getDelinquentAmount().setScale(2, RoundingMode.HALF_DOWN).toString());
assertThat(actualValuesList)
.as(ErrorMessageHelper.wrongValueInLineInInstallmentLevelDelinquencyData(i, actualValuesList, expectedValuesList))
.isEqualTo(expectedValuesList);
}
}
@Then("Loan Delinquency pause periods has the following data:")
public void loanDelinquencyPauseDataCheck(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
List<List<String>> expectedData = table.asLists();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetails);
List<GetLoansLoanIdDelinquencyPausePeriod> delinquencyPausePeriods = loanDetails.body().getDelinquent()
.getDelinquencyPausePeriods();
assertThat(delinquencyPausePeriods.size())
.as(ErrorMessageHelper.nrOfLinesWrongInLoanDelinquencyPauseData(delinquencyPausePeriods.size(), expectedData.size() - 1))
.isEqualTo(expectedData.size() - 1);
for (int i = 1; i < expectedData.size(); i++) {
List<String> expectedValuesList = expectedData.get(i);
List<List<String>> actualValuesList = delinquencyPausePeriods.stream()
.map(t -> fetchValuesOfDelinquencyPausePeriods(table.row(0), t)).collect(Collectors.toList());
boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValuesList));
assertThat(containsExpectedValues)
.as(ErrorMessageHelper.wrongValueInLineInDelinquencyPausePeriodData(i, actualValuesList, expectedValuesList)).isTrue();
}
}
@Then("Loan details delinquent.nextPaymentDueDate will be {string}")
public void nextPaymentDueDateCheck(String expectedDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetails);
String actualDate = FORMATTER.format(loanDetails.body().getDelinquent().getNextPaymentDueDate());
assertThat(actualDate).as(ErrorMessageHelper.wrongDataInNextPaymentDueDate(actualDate, expectedDate)).isEqualTo(expectedDate);
}
@Then("LoanAccountDelinquencyRangeDataV1 has delinquencyRange field with value {string}")
public void checkDelinquencyRangeInEvent(String expectedRange) {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(expectedRange);
String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue();
eventAssertion.assertEvent(LoanDelinquencyRangeChangeEvent.class, loanId)//
.extractingData(loanAccountDelinquencyRangeDataV1 -> { //
String actualDelinquencyRangeValue = loanAccountDelinquencyRangeDataV1.getDelinquencyRange().getClassification();//
assertThat(actualDelinquencyRangeValue)//
.as(ErrorMessageHelper.delinquencyRangeError(actualDelinquencyRangeValue, expectedDelinquencyRangeValue))//
.isEqualTo(expectedDelinquencyRangeValue);//
return null;
});
}
@Then("LoanDelinquencyRangeChangeBusinessEvent has the same Delinquency range, date and amount as in LoanDetails on both loan- and installment-level")
public void checkDelinquencyRangeInEvent() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "", "", "").execute();
GetDelinquencyRangesResponse delinquencyRange = loanDetails.body().getDelinquencyRange();
GetLoansLoanIdDelinquencySummary delinquent = loanDetails.body().getDelinquent();
eventAssertion.assertEvent(LoanDelinquencyRangeChangeEvent.class, loanId)//
.extractingData(loanAccountDelinquencyRangeDataV1 -> { //
Long loanLevelDelinquencyRangeId = loanAccountDelinquencyRangeDataV1.getDelinquencyRange().getId();
String loanLevelDelinquencyRange = loanAccountDelinquencyRangeDataV1.getDelinquencyRange().getClassification();
String loanLevelDelinquentDate = loanAccountDelinquencyRangeDataV1.getDelinquentDate();
BigDecimal loanLevelTotalAmount = loanAccountDelinquencyRangeDataV1.getAmount().getTotalAmount();
Long loanLevelDelinquencyRangeIdExpected = delinquencyRange.getId();
String loanLevelDelinquencyRangeExpected = delinquencyRange.getClassification();
String loanLevelDelinquentDateExpected = FORMATTER.format(delinquent.getDelinquentDate());
BigDecimal loanLevelTotalAmountExpected = new BigDecimal(delinquent.getDelinquentAmount());
assertThat(loanLevelDelinquencyRangeId)//
.as(ErrorMessageHelper.wrongValueInLoanDelinquencyRangeChangeBusinessEvent4(loanLevelDelinquencyRangeId,
loanLevelDelinquencyRangeIdExpected))//
.isEqualTo(loanLevelDelinquencyRangeIdExpected);//
assertThat(loanLevelDelinquencyRange)//
.as(ErrorMessageHelper.wrongValueInLoanDelinquencyRangeChangeBusinessEvent5(loanLevelDelinquencyRange,
loanLevelDelinquencyRangeExpected))//
.isEqualTo(loanLevelDelinquencyRangeExpected);//
assertThat(loanLevelDelinquentDate)//
.as(ErrorMessageHelper.wrongValueInLoanDelinquencyRangeChangeBusinessEvent7(loanLevelDelinquentDate,
loanLevelDelinquentDateExpected))//
.isEqualTo(loanLevelDelinquentDateExpected);//
assertThat(loanLevelTotalAmount)//
.as(ErrorMessageHelper.wrongValueInLoanDelinquencyRangeChangeBusinessEvent6(loanLevelTotalAmount,
loanLevelTotalAmountExpected))//
.isEqualTo(loanLevelTotalAmountExpected);//
List<GetLoansLoanIdLoanInstallmentLevelDelinquency> installmentLevelDelinquencyBucketsExpected = delinquent
.getInstallmentLevelDelinquency();
List<LoanInstallmentDelinquencyBucketDataV1> installmentDelinquencyBuckets = loanAccountDelinquencyRangeDataV1
.getInstallmentDelinquencyBuckets();
for (int i = 0; i < installmentDelinquencyBuckets.size(); i++) {
Long installmentLevelDelinquencyRangeId = installmentDelinquencyBuckets.get(i).getDelinquencyRange().getId();
String installmentLevelDelinquencyRange = installmentDelinquencyBuckets.get(i).getDelinquencyRange()
.getClassification();
BigDecimal installmentLevelTotalAmount = installmentDelinquencyBuckets.get(i).getAmount().getTotalAmount();
Long installmentLevelDelinquencyRangeIdExpected = installmentLevelDelinquencyBucketsExpected.get(i).getRangeId();
String installmentLevelDelinquencyRangeExpected = installmentLevelDelinquencyBucketsExpected.get(i)
.getClassification();
BigDecimal installmentLevelTotalAmountExpected = installmentLevelDelinquencyBucketsExpected.get(i)
.getDelinquentAmount();
assertThat(installmentLevelDelinquencyRangeId)//
.as(ErrorMessageHelper.wrongValueInLoanDelinquencyRangeChangeBusinessEvent1(
installmentLevelDelinquencyRangeId, installmentLevelDelinquencyRangeIdExpected))//
.isEqualTo(installmentLevelDelinquencyRangeIdExpected);//
assertThat(installmentLevelDelinquencyRange)//
.as(ErrorMessageHelper.wrongValueInLoanDelinquencyRangeChangeBusinessEvent2(
installmentLevelDelinquencyRange, installmentLevelDelinquencyRangeExpected))//
.isEqualTo(installmentLevelDelinquencyRangeExpected);//
assertThat(installmentLevelTotalAmount)//
.as(ErrorMessageHelper.wrongValueInLoanDelinquencyRangeChangeBusinessEvent3(installmentLevelTotalAmount,
installmentLevelTotalAmountExpected))//
.isEqualTo(installmentLevelTotalAmountExpected);//
}
return null;
});
}
@Then("In Loan details delinquent.lastRepaymentAmount is {int} EUR with lastRepaymentDate {string}")
public void delinquentLastRepaymentAmountCheck(int expectedLastRepaymentAmount, String expectedLastRepaymentDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetails);
Double expectedLastRepaymentAmount1 = Double.valueOf(expectedLastRepaymentAmount);
Double actualLastRepaymentAmount = loanDetails.body().getDelinquent().getLastRepaymentAmount();
String actualLastRepaymentDate = FORMATTER.format(loanDetails.body().getDelinquent().getLastRepaymentDate());
assertThat(actualLastRepaymentAmount)//
.as(ErrorMessageHelper.wrongDataInDelinquentLastRepaymentAmount(actualLastRepaymentAmount, expectedLastRepaymentAmount1))//
.isEqualTo(expectedLastRepaymentAmount);//
assertThat(actualLastRepaymentDate)//
.as(ErrorMessageHelper.wrongDataInDelinquentLastRepaymentDate(actualLastRepaymentDate, expectedLastRepaymentDate))//
.isEqualTo(expectedLastRepaymentDate);//
log.info("loanDetails.delinquent.lastRepaymentAmount: {}", actualLastRepaymentAmount);
log.info("loanDetails.delinquent.lastRepaymentDate: {}", actualLastRepaymentDate);
}
@SuppressFBWarnings("SF_SWITCH_NO_DEFAULT")
private List<String> fetchValuesOfDelinquencyPausePeriods(List<String> header, GetLoansLoanIdDelinquencyPausePeriod t) {
List<String> actualValues = new ArrayList<>();
for (String headerName : header) {
switch (headerName) {
case "active" -> actualValues.add(t.getActive() == null ? null : t.getActive().toString());
case "pausePeriodStart" ->
actualValues.add(t.getPausePeriodStart() == null ? null : FORMATTER.format(t.getPausePeriodStart()));
case "pausePeriodEnd" -> actualValues.add(t.getPausePeriodEnd() == null ? null : FORMATTER.format(t.getPausePeriodEnd()));
}
}
return actualValues;
}
private void errorMessageAssertation(Response<PostLoansDelinquencyActionResponse> response, int errorCodeExpected,
String errorMessageExpected) throws IOException {
String errorToString = response.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(errorToString, ErrorResponse.class);
String errorMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage();
int errorCodeActual = response.code();
assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected);
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.isEqualTo(errorMessageExpected);
log.info("ERROR CODE: {}", errorCodeActual);
log.info("ERROR MESSAGE: {}", errorMessageActual);
}
@Then("LoanDelinquencyRangeChangeBusinessEvent is created")
public void checkLoanDelinquencyRangeChangeBusinessEventCreated() {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
eventAssertion.assertEventRaised(LoanDelinquencyRangeChangeEvent.class, loanId);
}
}
@@ -0,0 +1,107 @@
/**
* 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.test.stepdef.loan;
import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.LoanTransactionsApi;
import org.apache.fineract.test.factory.LoanRequestFactory;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.loan.LoanReAgeEvent;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j
public class LoanReAgingStepDef extends AbstractStepDef {
@Autowired
private LoanTransactionsApi loanTransactionsApi;
@Autowired
private EventAssertion eventAssertion;
@When("Admin creates a Loan re-aging transaction with the following data:")
public void createReAgingTransaction(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
List<String> data = table.asLists().get(1);
int frequencyNumber = Integer.parseInt(data.get(0));
String frequencyType = data.get(1);
String startDate = data.get(2);
int numberOfInstallments = Integer.parseInt(data.get(3));
PostLoansLoanIdTransactionsRequest reAgingRequest = LoanRequestFactory.defaultReAgingRequest().frequencyNumber(frequencyNumber)
.frequencyType(frequencyType).startDate(startDate).numberOfInstallments(numberOfInstallments);
Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi.executeLoanTransaction(loanId, reAgingRequest, "reAge")
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
testContext().set(TestContextKey.LOAN_REAGING_RESPONSE, response);
}
@When("Admin creates a Loan re-aging transaction by Loan external ID with the following data:")
public void createReAgingTransactionByLoanExternalId(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.body().getResourceExternalId();
List<String> data = table.asLists().get(1);
int frequencyNumber = Integer.parseInt(data.get(0));
String frequencyType = data.get(1);
String startDate = data.get(2);
int numberOfInstallments = Integer.parseInt(data.get(3));
PostLoansLoanIdTransactionsRequest reAgingRequest = LoanRequestFactory.defaultReAgingRequest().frequencyNumber(frequencyNumber)
.frequencyType(frequencyType).startDate(startDate).numberOfInstallments(numberOfInstallments);
Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi
.executeLoanTransaction1(loanExternalId, reAgingRequest, "reAge").execute();
ErrorHelper.checkSuccessfulApiCall(response);
testContext().set(TestContextKey.LOAN_REAGING_RESPONSE, response);
}
@When("Admin successfully undo Loan re-aging transaction")
public void undoReAgingTransaction() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi
.executeLoanTransaction(loanId, new PostLoansLoanIdTransactionsRequest(), "undoReAge").execute();
ErrorHelper.checkSuccessfulApiCall(response);
testContext().set(TestContextKey.LOAN_REAGING_UNDO_RESPONSE, response);
}
@Then("LoanReAgeBusinessEvent is created")
public void checkLoanReAmortizeBusinessEventCreated() {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
eventAssertion.assertEventRaised(LoanReAgeEvent.class, loanId);
}
}
@@ -0,0 +1,89 @@
/**
* 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.test.stepdef.loan;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.services.LoanTransactionsApi;
import org.apache.fineract.test.factory.LoanRequestFactory;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.loan.LoanReAmortizeEvent;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class LoanReAmortizationStepDef extends AbstractStepDef {
@Autowired
private LoanTransactionsApi loanTransactionsApi;
@Autowired
private EventAssertion eventAssertion;
@When("When Admin creates a Loan re-amortization transaction on current business date")
public void createLoanReAmortization() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansLoanIdTransactionsRequest reAmortizationRequest = LoanRequestFactory.defaultLoanReAmortizationRequest();
Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi
.executeLoanTransaction(loanId, reAmortizationRequest, "reAmortize").execute();
ErrorHelper.checkSuccessfulApiCall(response);
testContext().set(TestContextKey.LOAN_REAMORTIZATION_RESPONSE, response);
}
@When("When Admin creates a Loan re-amortization transaction on current business date by loan external ID")
public void createLoanReAmortizationByLoanExternalId() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.body().getResourceExternalId();
PostLoansLoanIdTransactionsRequest reAmortizationRequest = LoanRequestFactory.defaultLoanReAmortizationRequest();
Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi
.executeLoanTransaction1(loanExternalId, reAmortizationRequest, "reAmortize").execute();
ErrorHelper.checkSuccessfulApiCall(response);
testContext().set(TestContextKey.LOAN_REAMORTIZATION_RESPONSE, response);
}
@When("When Admin undo Loan re-amortization transaction on current business date")
public void undoLoanReAmortization() throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi
.executeLoanTransaction(loanId, new PostLoansLoanIdTransactionsRequest(), "undoReAmortize").execute();
ErrorHelper.checkSuccessfulApiCall(response);
testContext().set(TestContextKey.LOAN_REAMORTIZATION_UNDO_RESPONSE, response);
}
@Then("LoanReAmortizeBusinessEvent is created")
public void checkLoanReAmortizeBusinessEventCreated() {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
eventAssertion.assertEventRaised(LoanReAmortizeEvent.class, loanId);
}
}
@@ -0,0 +1,564 @@
/**
* 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.test.stepdef.loan;
import static org.apache.fineract.test.data.paymenttype.DefaultPaymentType.AUTOPAY;
import static org.assertj.core.api.Assertions.assertThat;
import io.cucumber.java.en.And;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.codec.binary.Base64;
import org.apache.fineract.avro.loan.v1.LoanTransactionAdjustmentDataV1;
import org.apache.fineract.avro.loan.v1.LoanTransactionDataV1;
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
import org.apache.fineract.client.models.GetUsersUserIdResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.models.PostUsersResponse;
import org.apache.fineract.client.services.LoanTransactionsApi;
import org.apache.fineract.client.services.LoansApi;
import org.apache.fineract.client.services.UsersApi;
import org.apache.fineract.test.api.ApiProperties;
import org.apache.fineract.test.data.TransactionType;
import org.apache.fineract.test.data.paymenttype.DefaultPaymentType;
import org.apache.fineract.test.data.paymenttype.PaymentTypeResolver;
import org.apache.fineract.test.factory.LoanRequestFactory;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.EventCheckHelper;
import org.apache.fineract.test.messaging.event.loan.LoanBalanceChangedEvent;
import org.apache.fineract.test.messaging.event.loan.transaction.LoanAdjustTransactionBusinessEvent;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
public class LoanRepaymentStepDef extends AbstractStepDef {
public static final String DATE_FORMAT = "dd MMMM yyyy";
public static final String DEFAULT_LOCALE = "en";
public static final String DEFAULT_ACCOUNT_NB = "1234567890";
public static final String DEFAULT_CHECK_NB = "1234567890";
public static final String DEFAULT_RECEIPT_NB = "1234567890";
public static final String DEFAULT_BANK_NB = "1234567890";
@Autowired
private LoanTransactionsApi loanTransactionsApi;
@Autowired
private LoansApi loansApi;
@Autowired
private EventAssertion eventAssertion;
@Autowired
private ApiProperties apiProperties;
@Autowired
private UsersApi usersApi;
@Autowired
private PaymentTypeResolver paymentTypeResolver;
@Autowired
private EventCheckHelper eventCheckHelper;
@And("Customer makes {string} repayment on {string} with {double} EUR transaction amount")
public void makeLoanRepayment(String repaymentType, String transactionDate, double transactionAmount) throws IOException {
makeRepayment(repaymentType, transactionDate, transactionAmount, null);
}
@And("Customer makes {string} repayment on {string} with {double} EUR transaction amount and check external owner")
public void makeLoanRepaymentAndCheckOwner(String repaymentType, String transactionDate, double transactionAmount) throws IOException {
String transferExternalOwnerId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID);
makeRepayment(repaymentType, transactionDate, transactionAmount, transferExternalOwnerId);
}
private void makeRepayment(String repaymentType, String transactionDate, double transactionAmount, String transferExternalOwnerId)
throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType);
long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Map<String, String> headerMap = new HashMap<>();
String idempotencyKey = UUID.randomUUID().toString();
testContext().set(TestContextKey.TRANSACTION_IDEMPOTENCY_KEY, idempotencyKey);
headerMap.put("Idempotency-Key", idempotencyKey);
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = loanTransactionsApi
.executeLoanTransaction(loanId, repaymentRequest, "repayment", headerMap).execute();
testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, repaymentResponse);
ErrorHelper.checkSuccessfulApiCall(repaymentResponse);
EventAssertion.EventAssertionBuilder<LoanTransactionDataV1> transactionEvent = eventCheckHelper
.transactionEventCheck(repaymentResponse, TransactionType.REPAYMENT, transferExternalOwnerId);
testContext().set(TestContextKey.TRANSACTION_EVENT, transactionEvent);
eventAssertion.assertEventRaised(LoanBalanceChangedEvent.class, loanId);
}
@And("Created user makes {string} repayment on {string} with {double} EUR transaction amount")
public void makeRepaymentWithGivenUser(String repaymentType, String transactionDate, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType);
Long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Map<String, String> headerMap = new HashMap<>();
String idempotencyKey = UUID.randomUUID().toString();
testContext().set(TestContextKey.TRANSACTION_IDEMPOTENCY_KEY, idempotencyKey);
headerMap.put("Idempotency-Key", idempotencyKey);
Response<PostUsersResponse> createUserResponse = testContext().get(TestContextKey.CREATED_SIMPLE_USER_RESPONSE);
Long createdUserId = createUserResponse.body().getResourceId();
Response<GetUsersUserIdResponse> user = usersApi.retrieveOne31(createdUserId).execute();
ErrorHelper.checkSuccessfulApiCall(user);
String authorizationString = user.body().getUsername() + ":" + apiProperties.getPassword();
Base64 base64 = new Base64();
headerMap.put("Authorization",
"Basic " + new String(base64.encode(authorizationString.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8));
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = loanTransactionsApi
.executeLoanTransaction(loanId, repaymentRequest, "repayment", headerMap).execute();
testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, repaymentResponse);
ErrorHelper.checkSuccessfulApiCall(repaymentResponse);
eventAssertion.assertEventRaised(LoanBalanceChangedEvent.class, loanId);
}
@And("Customer makes externalID controlled {string} repayment on {string} with {double} EUR transaction amount")
public void makeRepaymentByExternalId(String repaymentType, String transactionDate, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
String resourceExternalId = loanResponse.body().getResourceExternalId();
DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType);
Long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Map<String, String> headerMap = new HashMap<>();
String idempotencyKey = UUID.randomUUID().toString();
testContext().set(TestContextKey.TRANSACTION_IDEMPOTENCY_KEY, idempotencyKey);
headerMap.put("Idempotency-Key", idempotencyKey);
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = loanTransactionsApi
.executeLoanTransaction1(resourceExternalId, repaymentRequest, "repayment", headerMap).execute();
testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, repaymentResponse);
ErrorHelper.checkSuccessfulApiCall(repaymentResponse);
eventAssertion.assertEventRaised(LoanBalanceChangedEvent.class, loanId);
}
@And("Created user makes externalID controlled {string} repayment on {string} with {double} EUR transaction amount")
public void makeRepaymentWithGivenUserByExternalId(String repaymentType, String transactionDate, double transactionAmount)
throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
String resourceExternalId = loanResponse.body().getResourceExternalId();
DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType);
Long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Map<String, String> headerMap = new HashMap<>();
String idempotencyKey = UUID.randomUUID().toString();
testContext().set(TestContextKey.TRANSACTION_IDEMPOTENCY_KEY, idempotencyKey);
headerMap.put("Idempotency-Key", idempotencyKey);
Response<PostUsersResponse> createUserResponse = testContext().get(TestContextKey.CREATED_SIMPLE_USER_RESPONSE);
Long createdUserId = createUserResponse.body().getResourceId();
Response<GetUsersUserIdResponse> user = usersApi.retrieveOne31(createdUserId).execute();
ErrorHelper.checkSuccessfulApiCall(user);
String authorizationString = user.body().getUsername() + ":" + apiProperties.getPassword();
Base64 base64 = new Base64();
headerMap.put("Authorization",
"Basic " + new String(base64.encode(authorizationString.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8));
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = loanTransactionsApi
.executeLoanTransaction1(resourceExternalId, repaymentRequest, "repayment", headerMap).execute();
testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, repaymentResponse);
ErrorHelper.checkSuccessfulApiCall(repaymentResponse);
eventAssertion.assertEventRaised(LoanBalanceChangedEvent.class, loanId);
}
@And("Customer not able to make {string} repayment on {string} with {double} EUR transaction amount")
public void makeLoanRepaymentFails(String repaymentType, String transactionDate, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType);
Long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = loanTransactionsApi
.executeLoanTransaction(loanId, repaymentRequest, "repayment").execute();
ErrorResponse errorDetails = ErrorResponse.from(repaymentResponse);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(400);
assertThat(errorDetails.getSingleError().getDeveloperMessage()).isEqualTo(ErrorMessageHelper.loanRepaymentOnClosedLoanFailureMsg());
}
@Then("Customer not able to make a repayment undo on {string} due to charge off")
public void makeLoanRepaymentUndoAfterChargeOff(String transactionDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
Response<PostLoansLoanIdTransactionsResponse> transactionResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
Long loanId = loanResponse.body().getLoanId();
Long transactionId = transactionResponse.body().getResourceId();
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
PostLoansLoanIdTransactionsTransactionIdRequest repaymentUndoRequest = LoanRequestFactory.defaultRepaymentUndoRequest()
.transactionDate(transactionDate).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> repaymentUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, repaymentResponse.body().getResourceId(), repaymentUndoRequest, "").execute();
ErrorResponse errorDetails = ErrorResponse.from(repaymentUndoResponse);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.repaymentUndoFailureDueToChargeOffCodeMsg()).isEqualTo(403);
assertThat(errorDetails.getSingleError().getDeveloperMessage())
.isEqualTo(ErrorMessageHelper.repaymentUndoFailureDueToChargeOff(transactionId));
}
@And("Customer makes {string} repayment on {string} with {double} EUR transaction amount \\(and transaction fails because of wrong date)")
public void makeLoanRepaymentWithWrongDate(String repaymentType, String transactionDate, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType);
Long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
PostLoansLoanIdTransactionsRequest repaymentRequest = LoanRequestFactory.defaultRepaymentRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeValue).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = loanTransactionsApi
.executeLoanTransaction(loanId, repaymentRequest, "repayment").execute();
testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, repaymentResponse);
}
@When("Refund happens on {string} with {double} EUR transaction amount")
public void makeRefund(String transactionDate, double transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansLoanIdTransactionsRequest refundRequest = LoanRequestFactory.defaultRefundRequest().transactionDate(transactionDate)
.transactionAmount(transactionAmount).paymentTypeId(paymentTypeResolver.resolve(AUTOPAY)).dateFormat(DATE_FORMAT)
.locale(DEFAULT_LOCALE).accountNumber(DEFAULT_ACCOUNT_NB).checkNumber(DEFAULT_CHECK_NB).receiptNumber(DEFAULT_RECEIPT_NB)
.bankNumber(DEFAULT_BANK_NB);
Response<PostLoansLoanIdTransactionsResponse> refundResponse = loanTransactionsApi
.executeLoanTransaction(loanId, refundRequest, "payoutRefund").execute();
ErrorHelper.checkSuccessfulApiCall(refundResponse);
testContext().set(TestContextKey.LOAN_REFUND_RESPONSE, refundResponse);
}
@When("Refund undo happens on {string}")
public void makeRefundUndo(String transactionDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<PostLoansLoanIdTransactionsResponse> refundResponse = testContext().get(TestContextKey.LOAN_REFUND_RESPONSE);
PostLoansLoanIdTransactionsTransactionIdRequest refundUndoRequest = LoanRequestFactory.defaultRefundUndoRequest()
.transactionDate(transactionDate).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> refundUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, refundResponse.body().getResourceId(), refundUndoRequest, "").execute();
ErrorHelper.checkSuccessfulApiCall(refundUndoResponse);
testContext().set(TestContextKey.LOAN_REPAYMENT_UNDO_RESPONSE, refundUndoResponse);
EventAssertion.EventAssertionBuilder<LoanTransactionAdjustmentDataV1> eventAssertionBuilder = eventAssertion
.assertEvent(LoanAdjustTransactionBusinessEvent.class, refundResponse.body().getResourceId());
eventAssertionBuilder
.extractingData(loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getId())
.isEqualTo(refundResponse.body().getResourceId());
eventAssertionBuilder
.extractingData(
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getManuallyReversed())
.isEqualTo(Boolean.TRUE);
eventAssertionBuilder.extractingData(LoanTransactionAdjustmentDataV1::getNewTransactionDetail).isEqualTo(null);
}
@When("Customer makes a repayment undo on {string}")
public void makeLoanRepaymentUndo(String transactionDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
PostLoansLoanIdTransactionsTransactionIdRequest repaymentUndoRequest = LoanRequestFactory.defaultRepaymentUndoRequest()
.transactionDate(transactionDate).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> repaymentUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, repaymentResponse.body().getResourceId(), repaymentUndoRequest, "").execute();
ErrorHelper.checkSuccessfulApiCall(repaymentUndoResponse);
testContext().set(TestContextKey.LOAN_REPAYMENT_UNDO_RESPONSE, repaymentUndoResponse);
EventAssertion.EventAssertionBuilder<LoanTransactionAdjustmentDataV1> eventAssertionBuilder = eventAssertion
.assertEvent(LoanAdjustTransactionBusinessEvent.class, repaymentResponse.body().getResourceId());
eventAssertionBuilder
.extractingData(loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getId())
.isEqualTo(repaymentResponse.body().getResourceId());
eventAssertionBuilder
.extractingData(
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getManuallyReversed())
.isEqualTo(Boolean.TRUE);
eventAssertionBuilder.extractingData(LoanTransactionAdjustmentDataV1::getNewTransactionDetail).isEqualTo(null);
}
@Then("Loan {string} transaction adjust amount {double} must return {int} code")
public void makeLoanRepaymentAdjustFail(String transactionType, double transactionAmount, int codeExpected) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = testContext().get(transactionType);
PostLoansLoanIdTransactionsTransactionIdRequest repaymentUndoRequest = LoanRequestFactory.defaultRepaymentUndoRequest()
.transactionAmount(transactionAmount);
Response<PostLoansLoanIdTransactionsResponse> repaymentUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, repaymentResponse.body().getResourceId(), repaymentUndoRequest, "").execute();
assertThat(repaymentUndoResponse.code()).isEqualTo(codeExpected);
}
@When("Customer undo {string}th repayment on {string}")
public void undoNthRepayment(String nthItemStr, String transactionDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
List<GetLoansLoanIdTransactions> transactions = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute().body()
.getTransactions();
int nthItem = Integer.parseInt(nthItemStr) - 1;
GetLoansLoanIdTransactions targetTransaction = transactions.stream().filter(t -> Boolean.TRUE.equals(t.getType().getRepayment()))
.toList().get(nthItem);
PostLoansLoanIdTransactionsTransactionIdRequest repaymentUndoRequest = LoanRequestFactory.defaultRepaymentUndoRequest()
.transactionDate(transactionDate).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> repaymentUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, targetTransaction.getId(), repaymentUndoRequest, "").execute();
ErrorHelper.checkSuccessfulApiCall(repaymentUndoResponse);
testContext().set(TestContextKey.LOAN_REPAYMENT_UNDO_RESPONSE, repaymentUndoResponse);
EventAssertion.EventAssertionBuilder<LoanTransactionAdjustmentDataV1> eventAssertionBuilder = eventAssertion
.assertEvent(LoanAdjustTransactionBusinessEvent.class, targetTransaction.getId());
eventAssertionBuilder
.extractingData(loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getId())
.isEqualTo(targetTransaction.getId());
eventAssertionBuilder
.extractingData(
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getManuallyReversed())
.isEqualTo(Boolean.TRUE);
eventAssertionBuilder.extractingData(LoanTransactionAdjustmentDataV1::getNewTransactionDetail).isEqualTo(null);
}
@When("Customer undo {string}th transaction made on {string}")
public void undoNthTransaction(String nthItemStr, String transactionDate) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
List<GetLoansLoanIdTransactions> transactions = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute().body()
.getTransactions();
int nthItem = Integer.parseInt(nthItemStr) - 1;
GetLoansLoanIdTransactions targetTransaction = transactions.stream()
.filter(t -> transactionDate.equals(formatter.format(t.getDate()))).toList().get(nthItem);
PostLoansLoanIdTransactionsTransactionIdRequest transactionUndoRequest = LoanRequestFactory.defaultTransactionUndoRequest()
.transactionDate(transactionDate);
Response<PostLoansLoanIdTransactionsResponse> transactionUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, "").execute();
ErrorHelper.checkSuccessfulApiCall(transactionUndoResponse);
testContext().set(TestContextKey.LOAN_TRANSACTION_UNDO_RESPONSE, transactionUndoResponse);
EventAssertion.EventAssertionBuilder<LoanTransactionAdjustmentDataV1> eventAssertionBuilder = eventAssertion
.assertEvent(LoanAdjustTransactionBusinessEvent.class, targetTransaction.getId());
eventAssertionBuilder
.extractingData(loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getId())
.isEqualTo(targetTransaction.getId());
eventAssertionBuilder
.extractingData(
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getManuallyReversed())
.isEqualTo(Boolean.TRUE);
eventAssertionBuilder.extractingData(LoanTransactionAdjustmentDataV1::getNewTransactionDetail).isEqualTo(null);
}
@When("Customer undo {string}th {string} transaction made on {string}")
public void undoNthTransactionType(String nthItemStr, String transactionType, String transactionDate) throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
List<GetLoansLoanIdTransactions> transactions = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute().body()
.getTransactions();
int nthItem = Integer.parseInt(nthItemStr) - 1;
GetLoansLoanIdTransactions targetTransaction = transactions//
.stream()//
.filter(t -> transactionDate.equals(formatter.format(t.getDate())) && transactionType.equals(t.getType().getValue()))//
.toList()//
.get(nthItem);//
PostLoansLoanIdTransactionsTransactionIdRequest transactionUndoRequest = LoanRequestFactory.defaultTransactionUndoRequest()
.transactionDate(transactionDate);
Response<PostLoansLoanIdTransactionsResponse> transactionUndoResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, "").execute();
ErrorHelper.checkSuccessfulApiCall(transactionUndoResponse);
testContext().set(TestContextKey.LOAN_TRANSACTION_UNDO_RESPONSE, transactionUndoResponse);
EventAssertion.EventAssertionBuilder<LoanTransactionAdjustmentDataV1> eventAssertionBuilder = eventAssertion
.assertEvent(LoanAdjustTransactionBusinessEvent.class, targetTransaction.getId());
eventAssertionBuilder
.extractingData(loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getId())
.isEqualTo(targetTransaction.getId());
eventAssertionBuilder
.extractingData(
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getManuallyReversed())
.isEqualTo(Boolean.TRUE);
eventAssertionBuilder.extractingData(LoanTransactionAdjustmentDataV1::getNewTransactionDetail).isEqualTo(null);
}
@Then("Repayment transaction is created with {double} amount and {string} type")
public void loanRepaymentStatus(double repaymentAmount, String paymentType) throws IOException {
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<GetLoansLoanIdTransactionsTransactionIdResponse> transactionResponse = loanTransactionsApi
.retrieveTransaction(loanId, repaymentResponse.body().getResourceId(), "").execute();
ErrorHelper.checkSuccessfulApiCall(transactionResponse);
assertThat(transactionResponse.body().getAmount()).isEqualTo(repaymentAmount);
assertThat(transactionResponse.body().getPaymentDetailData().getPaymentType().getName()).isEqualTo(paymentType);
}
@Then("Repayment failed because the repayment date is after the business date")
public void repaymentDateFailure() {
Response<PostLoansLoanIdTransactionsResponse> response = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
ErrorResponse errorDetails = ErrorResponse.from(response);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(403);
assertThat(errorDetails.getSingleError().getDeveloperMessage()).isEqualTo(ErrorMessageHelper.transactionDateInFutureFailureMsg());
}
@Then("Amounts are distributed equally in loan repayment schedule in case of total amount {double}")
public void amountsEquallyDistributedInSchedule(double totalAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId1 = loanResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> getLoansLoanIdResponseCall = loansApi
.retrieveLoan(loanId1, false, "all", "guarantors,futureSchedule", "").execute();
ErrorHelper.checkSuccessfulApiCall(getLoansLoanIdResponseCall);
List<GetLoansLoanIdRepaymentPeriod> periods = getLoansLoanIdResponseCall.body().getRepaymentSchedule().getPeriods();
BigDecimal expectedAmount = new BigDecimal(totalAmount / (periods.size() - 1)).setScale(0, RoundingMode.HALF_DOWN);
BigDecimal lastExpectedAmount = new BigDecimal(totalAmount).setScale(0, RoundingMode.HALF_DOWN);
for (int i = 1; i < periods.size(); i++) {
BigDecimal actualAmount = new BigDecimal(periods.get(i).getPrincipalOriginalDue()).setScale(0, RoundingMode.HALF_DOWN);
if (i == periods.size() - 1) {
assertThat(actualAmount.compareTo(lastExpectedAmount))
.as(ErrorMessageHelper.wrongAmountInRepaymentSchedule(i, actualAmount, lastExpectedAmount)).isEqualTo(0);
} else {
assertThat(actualAmount.compareTo(expectedAmount))
.as(ErrorMessageHelper.wrongAmountInRepaymentSchedule(i, actualAmount, expectedAmount)).isEqualTo(0);
lastExpectedAmount = lastExpectedAmount.subtract(actualAmount);
}
}
}
@When("Customer adjust {string}th repayment on {string} with amount {string} and check external owner")
public void adjustNthRepaymentWithExternalOwner(String nthItemStr, String transactionDate, String amount) throws IOException {
String transferExternalOwnerId = testContext().get(TestContextKey.ASSET_EXTERNALIZATION_OWNER_EXTERNAL_ID);
adjustNthRepaymentWithExternalOwnerCheck(nthItemStr, transactionDate, amount, transferExternalOwnerId);
}
@When("Customer adjust {string}th repayment on {string} with amount {string}")
public void adjustNthRepayment(String nthItemStr, String transactionDate, String amount) throws IOException {
adjustNthRepaymentWithExternalOwnerCheck(nthItemStr, transactionDate, amount, null);
}
private void adjustNthRepaymentWithExternalOwnerCheck(String nthItemStr, String transactionDate, String amount, String externalOwnerId)
throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
List<GetLoansLoanIdTransactions> transactions = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute().body()
.getTransactions();
int nthItem = Integer.parseInt(nthItemStr) - 1;
GetLoansLoanIdTransactions targetTransaction = transactions.stream().filter(t -> Boolean.TRUE.equals(t.getType().getRepayment()))
.toList().get(nthItem);
double amountValue = Double.parseDouble(amount);
PostLoansLoanIdTransactionsTransactionIdRequest repaymentUndoRequest = LoanRequestFactory.defaultRepaymentAdjustRequest(amountValue)
.transactionDate(transactionDate).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
Response<PostLoansLoanIdTransactionsResponse> repaymentAdjustmentResponse = loanTransactionsApi
.adjustLoanTransaction(loanId, targetTransaction.getId(), repaymentUndoRequest, "").execute();
ErrorHelper.checkSuccessfulApiCall(repaymentAdjustmentResponse);
testContext().set(TestContextKey.LOAN_REPAYMENT_UNDO_RESPONSE, repaymentAdjustmentResponse);
EventAssertion.EventAssertionBuilder<LoanTransactionAdjustmentDataV1> eventAssertionBuilder = eventAssertion
.assertEvent(LoanAdjustTransactionBusinessEvent.class, targetTransaction.getId());
eventAssertionBuilder
.extractingData(loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getId())
.isEqualTo(targetTransaction.getId());
eventAssertionBuilder.extractingData(
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getAmount().doubleValue())
.isEqualTo(targetTransaction.getAmount());
eventAssertionBuilder
.extractingData(
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getManuallyReversed())
.isEqualTo(Boolean.TRUE);
eventAssertionBuilder
.extractingData(
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getExternalOwnerId())
.isEqualTo(externalOwnerId);
if (amountValue > 0) {
eventAssertionBuilder
.extractingData(loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getNewTransactionDetail().getId())
.isEqualTo(repaymentAdjustmentResponse.body().getResourceId());
eventAssertionBuilder.extractingData(
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getNewTransactionDetail().getAmount().doubleValue())
.isEqualTo(amountValue);
eventAssertionBuilder.extractingData(
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getNewTransactionDetail().getExternalOwnerId())
.isEqualTo(externalOwnerId);
}
}
}
@@ -0,0 +1,144 @@
/**
* 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.test.stepdef.loan;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.gson.Gson;
import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.PostCreateRescheduleLoansRequest;
import org.apache.fineract.client.models.PostCreateRescheduleLoansResponse;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.models.PostUpdateRescheduleLoansRequest;
import org.apache.fineract.client.models.PostUpdateRescheduleLoansResponse;
import org.apache.fineract.client.services.RescheduleLoansApi;
import org.apache.fineract.client.util.JSON;
import org.apache.fineract.test.data.LoanRescheduleErrorMessage;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.helper.ErrorResponse;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@Slf4j
public class LoanRescheduleStepDef extends AbstractStepDef {
private static final Gson GSON = new JSON().getGson();
@Autowired
private RescheduleLoansApi rescheduleLoansApi;
@When("Admin creates and approves Loan reschedule with the following data:")
public void createAndApproveLoanReschedule(DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
List<List<String>> data = table.asLists();
List<String> rescheduleData = data.get(1);
String rescheduleFromDate = rescheduleData.get(0);
String submittedOnDate = rescheduleData.get(1);
String adjustedDueDate = rescheduleData.get(2);
Integer graceOfPrincipal = Integer.parseInt(rescheduleData.get(3)) == 0 ? null : Integer.valueOf(rescheduleData.get(3));
Integer graceOnInterest = Integer.parseInt(rescheduleData.get(4)) == 0 ? null : Integer.valueOf(rescheduleData.get(4));
Integer extraTerms = Integer.parseInt(rescheduleData.get(5)) == 0 ? null : Integer.valueOf(rescheduleData.get(5));
BigDecimal newInterestRate = new BigDecimal(rescheduleData.get(6));
PostCreateRescheduleLoansRequest request = new PostCreateRescheduleLoansRequest()//
.loanId(loanId)//
.rescheduleFromDate(rescheduleFromDate)//
.submittedOnDate(submittedOnDate)//
.adjustedDueDate(adjustedDueDate)//
.graceOnPrincipal(graceOfPrincipal)//
.graceOnInterest(graceOnInterest)//
.extraTerms(extraTerms)//
.newInterestRate(newInterestRate)//
.rescheduleReasonId(54L)//
.rescheduleReasonComment("")//
.dateFormat("dd MMMM yyyy")//
.locale("en");//
Response<PostCreateRescheduleLoansResponse> createResponse = rescheduleLoansApi.createLoanRescheduleRequest(request).execute();
ErrorHelper.checkSuccessfulApiCall(createResponse);
Long scheduleId = createResponse.body().getResourceId();
PostUpdateRescheduleLoansRequest approveRequest = new PostUpdateRescheduleLoansRequest()//
.approvedOnDate(submittedOnDate)//
.dateFormat("dd MMMM yyyy")//
.locale("en");//
Response<PostUpdateRescheduleLoansResponse> approveResponse = rescheduleLoansApi
.updateLoanRescheduleRequest(scheduleId, approveRequest, "approve").execute();
ErrorHelper.checkSuccessfulApiCall(approveResponse);
}
@Then("Loan reschedule with the following data results a {int} error and {string} error message")
public void createLoanRescheduleError(int errorCodeExpected, String errorMessageType, DataTable table) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
List<List<String>> data = table.asLists();
List<String> rescheduleData = data.get(1);
String rescheduleFromDate = rescheduleData.get(0);
String submittedOnDate = rescheduleData.get(1);
String adjustedDueDate = rescheduleData.get(2);
Integer graceOfPrincipal = Integer.parseInt(rescheduleData.get(3)) == 0 ? null : Integer.valueOf(rescheduleData.get(3));
Integer graceOnInterest = Integer.parseInt(rescheduleData.get(4)) == 0 ? null : Integer.valueOf(rescheduleData.get(4));
Integer extraTerms = Integer.parseInt(rescheduleData.get(5)) == 0 ? null : Integer.valueOf(rescheduleData.get(5));
BigDecimal newInterestRate = new BigDecimal(rescheduleData.get(6));
PostCreateRescheduleLoansRequest request = new PostCreateRescheduleLoansRequest()//
.loanId(loanId)//
.rescheduleFromDate(rescheduleFromDate)//
.submittedOnDate(submittedOnDate)//
.adjustedDueDate(adjustedDueDate)//
.graceOnPrincipal(graceOfPrincipal)//
.graceOnInterest(graceOnInterest)//
.extraTerms(extraTerms)//
.newInterestRate(newInterestRate)//
.rescheduleReasonId(54L)//
.rescheduleReasonComment("")//
.dateFormat("dd MMMM yyyy")//
.locale("en");//
Response<PostCreateRescheduleLoansResponse> createResponse = rescheduleLoansApi.createLoanRescheduleRequest(request).execute();
LoanRescheduleErrorMessage loanRescheduleErrorMessage = LoanRescheduleErrorMessage.valueOf(errorMessageType);
String errorMessageExpected = loanRescheduleErrorMessage.getValue(loanId);
String errorToString = createResponse.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(errorToString, ErrorResponse.class);
String errorMessageActual = errorResponse.getErrors().get(0).getDeveloperMessage();
int errorCodeActual = createResponse.code();
assertThat(errorCodeActual).as(ErrorMessageHelper.wrongErrorCode(errorCodeActual, errorCodeExpected)).isEqualTo(errorCodeExpected);
assertThat(errorMessageActual).as(ErrorMessageHelper.wrongErrorMessage(errorMessageActual, errorMessageExpected))
.isEqualTo(errorMessageExpected);
log.info("ERROR CODE: {}", errorCodeActual);
log.info("ERROR MESSAGE: {}", errorMessageActual);
}
}
@@ -0,0 +1,28 @@
@BusinessDateFeature
Feature: BusinessDate
@TestRailId:C38
Scenario: As a user I would like to enable the Business date configuration
Given Global configuration "enable_business_date" is enabled
@TestRailId:C39
Scenario: As a user I would like to disable the Business date configuration
Given Global configuration "enable_business_date" is disabled
@TestRailId:C40
Scenario: As a user I would like to set the business date
When Admin sets the business date to "10 July 2022"
Then Admin checks that the business date is correctly set to "10 July 2022"
@TestRailId:C41
Scenario: As a user I would like to change the business date manually
When Admin sets the business date to "10 July 2022"
Then Admin checks that the business date is correctly set to "10 July 2022"
When Admin sets the business date to "11 July 2022"
Then Admin checks that the business date is correctly set to "11 July 2022"
@TestRailId:C27
Scenario: As a user I would like to change the business date with scheduled job
When Admin sets the business date to "10 July 2022"
When Admin runs the Increase Business Date by 1 day job
Then Admin checks that the business date is correctly set to "11 July 2022"
@@ -0,0 +1,232 @@
@COBFeature
Feature: COBFeature
@TestRailId:C2501 @Skip
Scenario: As an admin I would like to see that last closed business date got updated after COB catch up job finished
When Admin sets the business date to "01 January 2022"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 January 2022"
And Admin successfully approves the loan on "01 January 2022" with "1000" amount and expected disbursement date on "01 January 2022"
When Admin successfully disburse the loan on "01 January 2022" with "1000" EUR transaction amount
Then Admin checks that last closed business date of loan is "null"
When Admin sets the business date to "02 January 2022"
When Admin runs COB job
Then Admin checks that last closed business date of loan is "01 January 2022"
When Admin sets the business date to "05 January 2022"
When Admin runs COB job
Then Admin checks that last closed business date of loan is "01 January 2022"
When Admin runs COB catch up
When Admin checks that Loan COB is running until the current business date
Then Admin checks that last closed business date of loan is "04 January 2022"
And Customer makes "AUTOPAY" repayment on "05 January 2022" with 1000 EUR transaction amount
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
@TestRailId:C2681 @Skip
Scenario: As an admin I would like to check that the Delinquency bucket set on the loan correctly when the arrears setting is 3 on the product
When Admin sets the business date to "1 January 2022"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "1 January 2022"
And Admin successfully approves the loan on "1 January 2022" with "1000" amount and expected disbursement date on "1 January 2022"
When Admin successfully disburse the loan on "1 January 2022" with "1000" EUR transaction amount
When Admin sets the business date to "4 February 2022"
When Admin runs COB job
Then Admin checks that delinquency range is: "RANGE_1" and has delinquentDate "2022-02-03"
@TestRailId:C2791 @Skip
Scenario: Verify that COB processes loans which are not closed/overpaid and has a last_closed_business_date exactly 1 day behind COB date
When Admin sets the business date to "01 July 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 July 2023"
And Admin successfully approves the loan on "01 July 2023" with "1000" amount and expected disbursement date on "01 July 2023"
When Admin successfully disburse the loan on "01 July 2023" with "1000" EUR transaction amount
Then Loan status will be "ACTIVE"
Then Admin checks that last closed business date of loan is "null"
When Admin sets the business date to "02 July 2023"
When Admin runs COB job
Then Admin checks that last closed business date of loan is "01 July 2023"
When Admin sets the business date to "03 July 2023"
When Admin runs COB job
Then Admin checks that last closed business date of loan is "02 July 2023"
@TestRailId:C2792
Scenario: Verify that COB doesn’t touch loans with last closed business date behind COB date
When Admin sets the business date to "01 July 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 July 2023"
And Admin successfully approves the loan on "01 July 2023" with "1000" amount and expected disbursement date on "01 July 2023"
When Admin successfully disburse the loan on "01 July 2023" with "1000" EUR transaction amount
Then Loan status will be "ACTIVE"
Then Admin checks that last closed business date of loan is "null"
When Admin sets the business date to "10 August 2023"
When Admin runs inline COB job for Loan
Then Admin checks that last closed business date of loan is "09 August 2023"
Then Admin checks that delinquency range is: "RANGE_3" and has delinquentDate "2023-08-03"
Then Loan delinquency history has the following details:
| Range (Classification) | Added on date | Lifted on date |
| RANGE_3 | 09 August 2023 | |
| RANGE_1 | 04 August 2023 | 09 August 2023 |
When Admin sets the business date to "12 August 2023"
When Admin runs COB job
Then Admin checks that last closed business date of loan is "09 August 2023"
Then Loan delinquency history has the following details:
| Range (Classification) | Added on date | Lifted on date |
| RANGE_3 | 09 August 2023 | |
| RANGE_1 | 04 August 2023 | 09 August 2023 |
@TestRailId:C2793
Scenario: Verify that COB doesn’t touch CLOSED loans
When Admin sets the business date to "01 July 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 July 2023"
And Admin successfully approves the loan on "01 July 2023" with "1000" amount and expected disbursement date on "01 July 2023"
When Admin successfully disburse the loan on "01 July 2023" with "1000" EUR transaction amount
Then Loan status will be "ACTIVE"
Then Admin checks that last closed business date of loan is "null"
When Admin sets the business date to "10 August 2023"
When Admin runs inline COB job for Loan
Then Admin checks that last closed business date of loan is "09 August 2023"
Then Admin checks that delinquency range is: "RANGE_3" and has delinquentDate "2023-08-03"
Then Loan delinquency history has the following details:
| Range (Classification) | Added on date | Lifted on date |
| RANGE_3 | 09 August 2023 | |
| RANGE_1 | 04 August 2023 | 09 August 2023 |
And Customer makes "AUTOPAY" repayment on "10 August 2023" with 1000 EUR transaction amount
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
Then Admin checks that delinquency range is: "NO_DELINQUENCY" and has delinquentDate ""
Then Loan delinquency history has the following details:
| Range (Classification) | Added on date | Lifted on date |
| RANGE_3 | 09 August 2023 | 10 August 2023 |
| RANGE_1 | 04 August 2023 | 09 August 2023 |
When Admin sets the business date to "11 August 2023"
When Admin runs COB job
Then Admin checks that last closed business date of loan is "09 August 2023"
Then Loan delinquency history has the following details:
| Range (Classification) | Added on date | Lifted on date |
| RANGE_3 | 09 August 2023 | 10 August 2023 |
| RANGE_1 | 04 August 2023 | 09 August 2023 |
@TestRailId:C2794
Scenario: Verify that COB doesn’t touch OVERPAID loans
When Admin sets the business date to "01 July 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 July 2023"
And Admin successfully approves the loan on "01 July 2023" with "1000" amount and expected disbursement date on "01 July 2023"
When Admin successfully disburse the loan on "01 July 2023" with "1000" EUR transaction amount
Then Loan status will be "ACTIVE"
Then Admin checks that last closed business date of loan is "null"
When Admin sets the business date to "10 August 2023"
When Admin runs inline COB job for Loan
Then Admin checks that last closed business date of loan is "09 August 2023"
Then Admin checks that delinquency range is: "RANGE_3" and has delinquentDate "2023-08-03"
Then Loan delinquency history has the following details:
| Range (Classification) | Added on date | Lifted on date |
| RANGE_3 | 09 August 2023 | |
| RANGE_1 | 04 August 2023 | 09 August 2023 |
And Customer makes "AUTOPAY" repayment on "10 August 2023" with 1200 EUR transaction amount
Then Loan status will be "OVERPAID"
Then Admin checks that delinquency range is: "NO_DELINQUENCY" and has delinquentDate ""
Then Loan delinquency history has the following details:
| Range (Classification) | Added on date | Lifted on date |
| RANGE_3 | 09 August 2023 | 10 August 2023 |
| RANGE_1 | 04 August 2023 | 09 August 2023 |
When Admin sets the business date to "11 August 2023"
When Admin runs COB job
Then Admin checks that last closed business date of loan is "09 August 2023"
Then Loan delinquency history has the following details:
| Range (Classification) | Added on date | Lifted on date |
| RANGE_3 | 09 August 2023 | 10 August 2023 |
| RANGE_1 | 04 August 2023 | 09 August 2023 |
@TestRailId:C2795 @Skip
Scenario: Verify that COB catch up runs properly on loan which is behind date because of locked with error
When Admin sets the business date to "01 January 2022"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 January 2022"
And Admin successfully approves the loan on "01 January 2022" with "1000" amount and expected disbursement date on "01 January 2022"
When Admin successfully disburse the loan on "01 January 2022" with "1000" EUR transaction amount
Then Loan status will be "ACTIVE"
Then Admin checks that last closed business date of loan is "null"
When Admin sets the business date to "02 January 2022"
When Admin runs COB job
Then Admin checks that last closed business date of loan is "01 January 2022"
When Admin sets the business date to "03 January 2022"
Then Admin places a lock on loan account with an error message
Then Admin checks that last closed business date of loan is "01 January 2022"
When Admin sets the business date to "05 January 2022"
Then Admin checks that last closed business date of loan is "01 January 2022"
When Admin runs COB catch up
When Admin checks that Loan COB is running until the current business date
Then Admin checks that last closed business date of loan is "04 January 2022"
And Customer makes "AUTOPAY" repayment on "05 January 2022" with 1000 EUR transaction amount
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
@TestRailId:C2796
Scenario: Verify that after COB runs there are no unreleased loan locks
When Admin sets the business date to "01 July 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 July 2023"
And Admin successfully approves the loan on "01 July 2023" with "1000" amount and expected disbursement date on "01 July 2023"
When Admin successfully disburse the loan on "01 July 2023" with "1000" EUR transaction amount
Then Loan status will be "ACTIVE"
Then Admin checks that last closed business date of loan is "null"
When Admin sets the business date to "02 July 2023"
When Admin runs COB job
Then The loan account is not locked
@TestRailId:C2797 @Skip
Scenario: Verify that Inline COB is executed for stuck loans - when payment happened on a loan with last closed business date in the past, COB got executed before the repayment
When Admin sets the business date to "01 July 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 July 2023"
And Admin successfully approves the loan on "01 July 2023" with "1000" amount and expected disbursement date on "01 July 2023"
When Admin successfully disburse the loan on "01 July 2023" with "1000" EUR transaction amount
Then Loan status will be "ACTIVE"
Then Admin checks that last closed business date of loan is "null"
When Admin sets the business date to "02 July 2023"
When Admin runs COB job
Then Admin checks that last closed business date of loan is "01 July 2023"
When Admin sets the business date to "04 July 2023"
When Admin creates new user with "NO_BYPASS_AUTOTEST" username, "NO_BYPASS_AUTOTEST_ROLE" role name and given permissions:
| REPAYMENT_LOAN |
When Created user makes externalID controlled "AUTOPAY" repayment on "04 July 2023" with 500 EUR transaction amount
Then Admin checks that last closed business date of loan is "03 July 2023"
# On a hard locked loan, in case of the lock has an error message, payment by a non-bypass user should trigger inlineCob and it should be executed
# this functionality is not implemented yet
@Skip @TestRailId:C2798
Scenario: Verify that Inline COB is executed for stuck loans - when payment happened on a locked loan COB got executed before the repayment
When Admin sets the business date to "01 July 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 July 2023"
And Admin successfully approves the loan on "01 July 2023" with "1000" amount and expected disbursement date on "01 July 2023"
When Admin successfully disburse the loan on "01 July 2023" with "1000" EUR transaction amount
Then Loan status will be "ACTIVE"
Then Admin checks that last closed business date of loan is "null"
When Admin sets the business date to "02 July 2023"
When Admin runs COB job
Then Admin checks that last closed business date of loan is "01 July 2023"
When Admin places a lock on loan account with an error message
When Admin sets the business date to "04 July 2023"
When Admin creates new user with "NO_BYPASS_AUTOTEST" username, "NO_BYPASS_AUTOTEST_ROLE" role name and given permissions:
| REPAYMENT_LOAN |
When Created user makes externalID controlled "AUTOPAY" repayment on "04 July 2023" with 500 EUR transaction amount
Then Admin checks that last closed business date of loan is "03 July 2023"
@TestRailId:C3044 @AdvancedPaymentAllocation
Scenario: Verify that LoanAccountCustomSnapshotBusinessEvent is created with proper business date when installment is due date and COB runs
Given Admin puts CHECK_DUE_INSTALLMENTS job into LOAN_CLOSE_OF_BUSINESS workflow
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "1000" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "1000" EUR transaction amount
When Admin sets the business date to "17 January 2024"
When Admin runs inline COB job for Loan
Then LoanAccountCustomSnapshotBusinessEvent is created with business date "17 January 2024"
Then Admin removes CHECK_DUE_INSTALLMENTS job from LOAN_CLOSE_OF_BUSINESS workflow
@@ -0,0 +1,143 @@
@InlineCOBFeature
Feature: InlineCOBFeature
@TestRailId:C2457
Scenario: As an admin I would like to see that inline COB Happy Path scenario is working properly
When Admin sets the business date to "1 January 2022"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "1 January 2022"
And Admin successfully approves the loan on "1 January 2022" with "1000" amount and expected disbursement date on "1 January 2022"
When Admin successfully disburse the loan on "1 January 2022" with "1000" EUR transaction amount
When Admin sets the business date to "1 March 2022"
When Admin runs inline COB job for Loan
Then Admin checks that delinquency range is: "RANGE_3" and has delinquentDate "2022-02-03"
@TestRailId:C2473
Scenario: As an admin I would like to see that loan was catching up with inline COB
When Admin sets the business date to "1 January 2022"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "1 January 2022"
And Admin successfully approves the loan on "1 January 2022" with "1000" amount and expected disbursement date on "1 January 2022"
When Admin successfully disburse the loan on "1 January 2022" with "1000" EUR transaction amount
Then Admin checks that last closed business date of loan is "null"
When Admin sets the business date to "2 January 2022"
When Admin runs inline COB job for Loan
Then Admin checks that last closed business date of loan is "01 January 2022"
When Admin sets the business date to "5 January 2022"
When Admin runs inline COB job for Loan
Then Admin checks that last closed business date of loan is "04 January 2022"
@TestRailId:C2554
Scenario: As an admin I would like to see that loan was catching up with inline COB on a repayment
When Admin sets the business date to "1 January 2022"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "1 January 2022"
And Admin successfully approves the loan on "1 January 2022" with "1000" amount and expected disbursement date on "1 January 2022"
When Admin successfully disburse the loan on "1 January 2022" with "1000" EUR transaction amount
Then Admin checks that last closed business date of loan is "null"
When Admin sets the business date to "2 January 2022"
When Admin runs inline COB job for Loan
Then Admin checks that last closed business date of loan is "01 January 2022"
When Admin sets the business date to "5 January 2022"
When Admin creates new user with "NO_BYPASS_AUTOTEST" username, "NO_BYPASS_AUTOTEST_ROLE" role name and given permissions:
| REPAYMENT_LOAN |
And Created user makes "AUTOPAY" repayment on "5 January 2022" with 100 EUR transaction amount
Then Admin checks that last closed business date of loan is "04 January 2022"
@TestRailId:C2602
Scenario: Verify that LoanCOBApiFilter works fine in the background and loanId controlled transaction does not trigger inlineCOB if COB had not been applied on loan before
When Admin sets the business date to "01 January 2023"
When Admin creates a client with random data
When Admin creates a new Loan with date: "01 January 2023" and with 1 day loan term and repayment
And Admin successfully approves the loan on "01 January 2023" with "1000" amount and expected disbursement date on "01 January 2023"
When Admin successfully disburse the loan on "01 January 2023" with "1000" EUR transaction amount
Then Admin checks that last closed business date of loan is "null"
When Admin sets the business date to "07 January 2023"
When Admin creates new user with "NO_BYPASS_AUTOTEST" username, "NO_BYPASS_AUTOTEST_ROLE" role name and given permissions:
| REPAYMENT_LOAN |
And Created user makes "AUTOPAY" repayment on "07 January 2023" with 1000 EUR transaction amount
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
Then Loan has 0 outstanding amount
Then Loan Transactions tab has a "REPAYMENT" transaction with date "07 January 2023" which has the following Journal entries:
| Type | Account code | Account name | Debit | Credit |
| ASSET | 112601 | Loans Receivable | | 1000.0 |
| LIABILITY | 145023 | Suspense/Clearing account | 1000.0 | |
Then Loan delinquency history has the following details:
| Range (Classification) | Added on date | Lifted on date |
| RANGE_1 | 06 January 2023 | 07 January 2023 |
Then Admin checks that last closed business date of loan is "null"
@TestRailId:C2603
Scenario: Verify that LoanCOBApiFilter works fine in the background and externalId controlled transaction does not trigger inlineCOB if COB had not been applied on loan before
When Admin sets the business date to "01 January 2023"
When Admin creates a client with random data
When Admin creates a new Loan with date: "01 January 2023" and with 1 day loan term and repayment
And Admin successfully approves the loan on "01 January 2023" with "1000" amount and expected disbursement date on "01 January 2023"
When Admin successfully disburse the loan on "01 January 2023" with "1000" EUR transaction amount
Then Admin checks that last closed business date of loan is "null"
When Admin sets the business date to "07 January 2023"
When Admin creates new user with "NO_BYPASS_AUTOTEST" username, "NO_BYPASS_AUTOTEST_ROLE" role name and given permissions:
| REPAYMENT_LOAN |
When Created user makes externalID controlled "AUTOPAY" repayment on "07 January 2023" with 1000 EUR transaction amount
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
Then Loan has 0 outstanding amount
Then Loan Transactions tab has a "REPAYMENT" transaction with date "07 January 2023" which has the following Journal entries:
| Type | Account code | Account name | Debit | Credit |
| ASSET | 112601 | Loans Receivable | | 1000.0 |
| LIABILITY | 145023 | Suspense/Clearing account | 1000.0 | |
Then Loan delinquency history has the following details:
| Range (Classification) | Added on date | Lifted on date |
| RANGE_1 | 06 January 2023 | 07 January 2023 |
Then Admin checks that last closed business date of loan is "null"
@TestRailId:C2604
Scenario: Verify that LoanCOBApiFilter works fine in the background and loanId controlled transaction triggers inlineCOB if COB had been applied on loan before
When Admin sets the business date to "01 January 2023"
When Admin creates a client with random data
When Admin creates a new Loan with date: "01 January 2023" and with 1 day loan term and repayment
And Admin successfully approves the loan on "01 January 2023" with "1000" amount and expected disbursement date on "01 January 2023"
When Admin successfully disburse the loan on "01 January 2023" with "1000" EUR transaction amount
Then Admin checks that last closed business date of loan is "null"
When Admin sets the business date to "2 January 2023"
When Admin runs inline COB job for Loan
Then Admin checks that last closed business date of loan is "01 January 2023"
When Admin sets the business date to "07 January 2023"
When Admin creates new user with "NO_BYPASS_AUTOTEST" username, "NO_BYPASS_AUTOTEST_ROLE" role name and given permissions:
| REPAYMENT_LOAN |
And Created user makes "AUTOPAY" repayment on "07 January 2023" with 1000 EUR transaction amount
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
Then Loan has 0 outstanding amount
Then Loan Transactions tab has a "REPAYMENT" transaction with date "07 January 2023" which has the following Journal entries:
| Type | Account code | Account name | Debit | Credit |
| ASSET | 112601 | Loans Receivable | | 1000.0 |
| LIABILITY | 145023 | Suspense/Clearing account | 1000.0 | |
Then Loan delinquency history has the following details:
| Range (Classification) | Added on date | Lifted on date |
| RANGE_1 | 06 January 2023 | 07 January 2023 |
Then Admin checks that last closed business date of loan is "06 January 2023"
@TestRailId:C2605
Scenario: Verify that LoanCOBApiFilter works fine in the background and externalId controlled transaction triggers inlineCOB if COB had been applied on loan before
When Admin sets the business date to "01 January 2023"
When Admin creates a client with random data
When Admin creates a new Loan with date: "01 January 2023" and with 1 day loan term and repayment
And Admin successfully approves the loan on "01 January 2023" with "1000" amount and expected disbursement date on "01 January 2023"
When Admin successfully disburse the loan on "01 January 2023" with "1000" EUR transaction amount
Then Admin checks that last closed business date of loan is "null"
When Admin sets the business date to "2 January 2023"
When Admin runs inline COB job for Loan
Then Admin checks that last closed business date of loan is "01 January 2023"
When Admin sets the business date to "07 January 2023"
When Admin creates new user with "NO_BYPASS_AUTOTEST" username, "NO_BYPASS_AUTOTEST_ROLE" role name and given permissions:
| REPAYMENT_LOAN |
When Created user makes externalID controlled "AUTOPAY" repayment on "07 January 2023" with 1000 EUR transaction amount
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
Then Loan has 0 outstanding amount
Then Loan Transactions tab has a "REPAYMENT" transaction with date "07 January 2023" which has the following Journal entries:
| Type | Account code | Account name | Debit | Credit |
| ASSET | 112601 | Loans Receivable | | 1000.0 |
| LIABILITY | 145023 | Suspense/Clearing account | 1000.0 | |
Then Loan delinquency history has the following details:
| Range (Classification) | Added on date | Lifted on date |
| RANGE_1 | 06 January 2023 | 07 January 2023 |
Then Admin checks that last closed business date of loan is "06 January 2023"
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,844 @@
@LoanAccrualFeature
Feature: LoanAccrualTransaction
@TestRailId:C2647
Scenario: Verify that after COB job Accrual event is raised when loan has a fee-charge on disbursal date
When Admin sets the business date to "01 January 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 January 2023"
And Admin successfully approves the loan on "01 January 2023" with "1000" amount and expected disbursement date on "01 January 2023"
When Admin successfully disburse the loan on "01 January 2023" with "1000" EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "01 January 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "02 January 2023"
When Admin runs inline COB job for Loan
Then Loan Transactions tab has a transaction with date: "01 January 2023", and with the following data:
| Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
Then LoanAccrualTransactionCreatedBusinessEvent is raised on "01 January 2023"
@TestRailId:C2648
Scenario: Verify that after COB job Accrual event is raised when loan has a fee-charge on disbursal date with partial repayment
When Admin sets the business date to "01 January 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 January 2023"
And Admin successfully approves the loan on "01 January 2023" with "1000" amount and expected disbursement date on "01 January 2023"
When Admin successfully disburse the loan on "01 January 2023" with "1000" EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "01 January 2023" due date and 10 EUR transaction amount
And Customer makes "AUTOPAY" repayment on "01 January 2023" with 500 EUR transaction amount
When Admin sets the business date to "02 January 2023"
When Admin runs inline COB job for Loan
Then Loan Transactions tab has a transaction with date: "01 January 2023", and with the following data:
| Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
Then LoanAccrualTransactionCreatedBusinessEvent is raised on "01 January 2023"
@TestRailId:C2649
Scenario: Verify that after COB job Accrual event is raised when loan has a fee-charge on disbursal date with full repayment and loan is closed
When Admin sets the business date to "01 January 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 January 2023"
And Admin successfully approves the loan on "01 January 2023" with "1000" amount and expected disbursement date on "01 January 2023"
When Admin successfully disburse the loan on "01 January 2023" with "1000" EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "01 January 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "02 January 2023"
And Customer makes "AUTOPAY" repayment on "02 January 2023" with 1010 EUR transaction amount
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
Then Loan Transactions tab has a transaction with date: "02 January 2023", and with the following data:
| Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
Then LoanAccrualTransactionCreatedBusinessEvent is raised on "02 January 2023"
@TestRailId:C2650
Scenario: Verify that after COB job Accrual event is raised when loan has a fee-charge added with chargeback
When Admin sets the business date to "01 January 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 January 2023"
And Admin successfully approves the loan on "01 January 2023" with "1000" amount and expected disbursement date on "01 January 2023"
When Admin successfully disburse the loan on "01 January 2023" with "1000" EUR transaction amount
When Admin sets the business date to "02 January 2023"
And Customer makes "AUTOPAY" repayment on "02 January 2023" with 250 EUR transaction amount
When Admin sets the business date to "03 January 2023"
And Customer makes "AUTOPAY" repayment on "03 January 2023" with 250 EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "04 January 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "04 January 2023"
And Admin runs COB job
When Admin sets the business date to "05 January 2023"
And Customer makes "AUTOPAY" repayment on "05 January 2023" with 510 EUR transaction amount
Then Loan has 0 outstanding amount
And Admin makes "REPAYMENT_ADJUSTMENT_CHARGEBACK" chargeback with 250 EUR transaction amount for Payment nr. 2
Then Loan has 250 outstanding amount
Then Loan Transactions tab has a transaction with date: "05 January 2023", and with the following data:
| Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
Then LoanAccrualTransactionCreatedBusinessEvent is raised on "05 January 2023"
@TestRailId:C2651
Scenario: Verify that after periodic accrual transaction job accrual event is raised when loan has a fee-charge added with waive charge and undo waive charge
When Admin sets the business date to "01 January 2023"
When Admin creates a client with random data
And Admin successfully creates a new customised Loan submitted on date: "01 January 2023", with Principal: "750", a loanTermFrequency: 3 months, and numberOfRepayments: 3
And Admin successfully approves the loan on "01 January 2023" with "750" amount and expected disbursement date on "01 January 2023"
When Admin successfully disburse the loan on "01 January 2023" with "750" EUR transaction amount
When Admin sets the business date to "01 February 2023"
And Customer makes "AUTOPAY" repayment on "01 February 2023" with 250 EUR transaction amount
When Admin sets the business date to "01 March 2023"
And Customer makes "AUTOPAY" repayment on "01 March 2023" with 250 EUR transaction amount
When Admin sets the business date to "01 April 2023"
And Customer makes "AUTOPAY" repayment on "01 April 2023" with 250 EUR transaction amount
When Customer makes a repayment undo on "01 April 2023"
When Admin sets the business date to "05 April 2023"
And Admin adds an NSF fee because of payment bounce with "05 April 2023" transaction date
When Admin sets the business date to "07 April 2023"
And Admin waives charge
When Admin sets the business date to "08 April 2023"
And Admin makes waive undone for charge
Then Loan status will be "ACTIVE"
Then Loan has 260 outstanding amount
And Admin runs the Add Periodic Accrual Transactions job
Then Loan Transactions tab has a transaction with date: "05 April 2023", and with the following data:
| Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| Accrual | 10.0 | 0.0 | 0.0 | 0.0 | 10.0 | 0.0 |
Then LoanAccrualTransactionCreatedBusinessEvent is raised on "05 April 2023"
@TestRailId:C2652
Scenario: Verify that after periodic accrual transaction job accrual event is raised when loan has a fee-charge added when loan is closed
When Admin sets the business date to "01 January 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 January 2023"
And Admin successfully approves the loan on "01 January 2023" with "1000" amount and expected disbursement date on "01 January 2023"
When Admin successfully disburse the loan on "01 January 2023" with "1000" EUR transaction amount
When Admin sets the business date to "02 January 2023"
And Customer makes "AUTOPAY" repayment on "02 January 2023" with 1000 EUR transaction amount
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "02 January 2023" due date and 10 EUR transaction amount
And Admin runs the Add Periodic Accrual Transactions job
Then Loan Transactions tab has a transaction with date: "02 January 2023", and with the following data:
| Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
Then LoanAccrualTransactionCreatedBusinessEvent is raised on "02 January 2023"
@TestRailId:C2653
Scenario: Verify that after disbursement and COB job Accrual event is raised when loan has a interest recalculation
When Admin sets the business date to "01 January 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN30_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_NONE | 01 January 2023 | 5000 | 12 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 0 | 0 | 0 | PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER |
And Admin successfully approves the loan on "01 January 2023" with "5000" amount and expected disbursement date on "01 January 2023"
When Admin successfully disburse the loan on "01 January 2023" with "5000" EUR transaction amount
When Admin sets the business date to "02 January 2023"
When Admin runs the Add Periodic Accrual Transactions job
Then Loan Transactions tab has a transaction with date: "02 January 2023", and with the following data:
| Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| Accrual | 1.64 | 0.0 | 1.64 | 0.0 | 0.0 | 0.0 |
Then LoanAccrualTransactionCreatedBusinessEvent is raised on "02 January 2023"
@TestRailId:C2654
Scenario: Verify that after loan is closed accrual event is raised when loan has a interest recalculation
When Admin sets the business date to "01 January 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN30_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_NONE | 01 January 2023 | 1000 | 12 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 1 | MONTHS | 1 | MONTHS | 1 | 0 | 0 | 0 | PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER |
And Admin successfully approves the loan on "01 January 2023" with "1000" amount and expected disbursement date on "01 January 2023"
When Admin successfully disburse the loan on "01 January 2023" with "1000" EUR transaction amount
When Admin sets the business date to "02 January 2023"
And Customer makes "AUTOPAY" repayment on "02 January 2023" with 1010.19 EUR transaction amount
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
Then Loan Transactions tab has a transaction with date: "02 January 2023", and with the following data:
| Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| Accrual | 10.19 | 0.0 | 10.19 | 0.0 | 0.0 | 0.0 |
Then LoanAccrualTransactionCreatedBusinessEvent is raised on "02 January 2023"
@TestRailId:C2683
Scenario: Verify that the final accrual is created when the loan goes to overpaid state
When Admin sets the business date to "1 July 2023"
When Admin creates a client with random data
And Admin successfully creates a new customised Loan submitted on date: "1 July 2023", with Principal: "5000", a loanTermFrequency: 1 months, and numberOfRepayments: 1
And Admin successfully approves the loan on "1 July 2023" with "5000" amount and expected disbursement date on "1 July 2023"
And Admin successfully disburse the loan on "1 July 2023" with "5000" EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "10 July 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "6 July 2023"
And Customer makes "AUTOPAY" repayment on "6 July 2023" with 5011 EUR transaction amount
Then Loan status will be "OVERPAID"
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 5000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 August 2023 | 06 July 2023 | 0.0 | 5000.0 | 0.0 | 10.0 | 0.0 | 5010.0 | 5010.0 | 5010.0 | 0.0 | 0.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 5000 | 0 | 10 | 0 | 5010 | 5010 | 5010 | 0 | 0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 July 2023 | Disbursement | 5000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 5000.0 |
| 06 July 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
| 06 July 2023 | Repayment | 5011.0 | 5000.0 | 0.0 | 10.0 | 0.0 | 0.0 |
@TestRailId:C2684
Scenario: Verify that the accrual transaction correctly created in case a CBR is applied on the loan
When Admin sets the business date to "1 July 2023"
When Admin creates a client with random data
And Admin successfully creates a new customised Loan submitted on date: "1 July 2023", with Principal: "5000", a loanTermFrequency: 1 months, and numberOfRepayments: 1
And Admin successfully approves the loan on "1 July 2023" with "5000" amount and expected disbursement date on "1 July 2023"
And Admin successfully disburse the loan on "1 July 2023" with "5000" EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "10 July 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "6 July 2023"
And Customer makes "AUTOPAY" repayment on "6 July 2023" with 5011 EUR transaction amount
Then Loan status will be "OVERPAID"
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 5000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 August 2023 | 06 July 2023 | 0.0 | 5000.0 | 0.0 | 10.0 | 0.0 | 5010.0 | 5010.0 | 5010.0 | 0.0 | 0.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 5000 | 0 | 10 | 0 | 5010 | 5010 | 5010 | 0 | 0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 July 2023 | Disbursement | 5000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 5000.0 |
| 06 July 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
| 06 July 2023 | Repayment | 5011.0 | 5000.0 | 0.0 | 10.0 | 0.0 | 0.0 |
When Admin makes Credit Balance Refund transaction on "06 July 2023" with 1 EUR transaction amount
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 5000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 August 2023 | 06 July 2023 | 0.0 | 5000.0 | 0.0 | 10.0 | 0.0 | 5010.0 | 5010.0 | 5010.0 | 0.0 | 0.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 5000 | 0 | 10 | 0 | 5010 | 5010 | 5010 | 0 | 0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 July 2023 | Disbursement | 5000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 5000.0 |
| 06 July 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
| 06 July 2023 | Repayment | 5011.0 | 5000.0 | 0.0 | 10.0 | 0.0 | 0.0 |
| 06 July 2023 | Credit Balance Refund | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
@TestRailId:C2685
Scenario: Verify that the accrual transaction correctly created (overpay, undo repayment, overpay)
When Admin sets the business date to "1 July 2023"
When Admin creates a client with random data
And Admin successfully creates a new customised Loan submitted on date: "1 July 2023", with Principal: "5000", a loanTermFrequency: 1 months, and numberOfRepayments: 1
And Admin successfully approves the loan on "1 July 2023" with "5000" amount and expected disbursement date on "1 July 2023"
And Admin successfully disburse the loan on "1 July 2023" with "5000" EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "10 July 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "5 July 2023"
And Customer makes "AUTOPAY" repayment on "5 July 2023" with 1000 EUR transaction amount
When Admin sets the business date to "6 July 2023"
And Customer makes "AUTOPAY" repayment on "6 July 2023" with 4011 EUR transaction amount
Then Loan status will be "OVERPAID"
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 5000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 August 2023 | 06 July 2023 | 0.0 | 5000.0 | 0.0 | 10.0 | 0.0 | 5010.0 | 5010.0 | 5010.0 | 0.0 | 0.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 5000 | 0 | 10 | 0 | 5010 | 5010 | 5010 | 0 | 0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 July 2023 | Disbursement | 5000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 5000.0 |
| 05 July 2023 | Repayment | 1000.0 | 990.0 | 0.0 | 10.0 | 0.0 | 4010.0 |
| 06 July 2023 | Repayment | 4011.0 | 4010.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 06 July 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
And Customer makes a repayment undo on "6 July 2023"
Then Loan status will be "ACTIVE"
When Admin sets the business date to "7 July 2023"
And Customer makes "AUTOPAY" repayment on "7 July 2023" with 4011 EUR transaction amount
Then Loan status will be "OVERPAID"
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 5000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 August 2023 | 07 July 2023 | 0.0 | 5000.0 | 0.0 | 10.0 | 0.0 | 5010.0 | 5010.0 | 5010.0 | 0.0 | 0.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 5000 | 0 | 10 | 0 | 5010 | 5010 | 5010 | 0 | 0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 July 2023 | Disbursement | 5000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 5000.0 |
| 05 July 2023 | Repayment | 1000.0 | 990.0 | 0.0 | 10.0 | 0.0 | 4010.0 |
| 06 July 2023 | Repayment | 4011.0 | 4010.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 06 July 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
| 07 July 2023 | Repayment | 4011.0 | 4010.0 | 0.0 | 0.0 | 0.0 | 0.0 |
@TestRailId:C2686
Scenario: Verify that the accrual transaction correctly created (overpay, undo repayment, add charge, overpay)
When Admin sets the business date to "1 July 2023"
When Admin creates a client with random data
And Admin successfully creates a new customised Loan submitted on date: "1 July 2023", with Principal: "5000", a loanTermFrequency: 1 months, and numberOfRepayments: 1
And Admin successfully approves the loan on "1 July 2023" with "5000" amount and expected disbursement date on "1 July 2023"
And Admin successfully disburse the loan on "1 July 2023" with "5000" EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "10 July 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "5 July 2023"
And Customer makes "AUTOPAY" repayment on "5 July 2023" with 1000 EUR transaction amount
When Admin sets the business date to "6 July 2023"
And Customer makes "AUTOPAY" repayment on "6 July 2023" with 4011 EUR transaction amount
Then Loan status will be "OVERPAID"
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 5000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 August 2023 | 06 July 2023 | 0.0 | 5000.0 | 0.0 | 10.0 | 0.0 | 5010.0 | 5010.0 | 5010.0 | 0.0 | 0.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 5000 | 0 | 10 | 0 | 5010 | 5010 | 5010 | 0 | 0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 July 2023 | Disbursement | 5000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 5000.0 |
| 05 July 2023 | Repayment | 1000.0 | 990.0 | 0.0 | 10.0 | 0.0 | 4010.0 |
| 06 July 2023 | Repayment | 4011.0 | 4010.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 06 July 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
And Customer makes a repayment undo on "6 July 2023"
Then Loan status will be "ACTIVE"
When Admin sets the business date to "7 July 2023"
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "20 July 2023" due date and 50 EUR transaction amount
And Customer makes "AUTOPAY" repayment on "7 July 2023" with 4061 EUR transaction amount
Then Loan status will be "OVERPAID"
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 5000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 August 2023 | 07 July 2023 | 0.0 | 5000.0 | 0.0 | 60.0 | 0.0 | 5060.0 | 5060.0 | 5060.0 | 0.0 | 0.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 5000 | 0 | 60 | 0 | 5060 | 5060 | 5060 | 0 | 0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 July 2023 | Disbursement | 5000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 5000.0 |
| 05 July 2023 | Repayment | 1000.0 | 940.0 | 0.0 | 60.0 | 0.0 | 4060.0 |
| 06 July 2023 | Repayment | 4011.0 | 4010.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 06 July 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
| 07 July 2023 | Repayment | 4061.0 | 4060.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 07 July 2023 | Accrual | 50.0 | 0.0 | 0.0 | 50.0 | 0.0 | 0.0 |
@TestRailId:C2707 @Skip
Scenario: Verify that the accrual transaction is not reversed when multi disbursement happens
When Admin sets the business date to "26 April 2023"
When Admin creates a client with random data
And Admin successfully creates a new customised Loan submitted on date: "26 April 2023", with Principal: "1000", a loanTermFrequency: 1 months, and numberOfRepayments: 1
And Admin successfully approves the loan on "26 April 2023" with "1000" amount and expected disbursement date on "1 May 2023"
And Admin successfully disburse the loan on "26 April 2023" with "1000" EUR transaction amount
Then Loan has 1000 outstanding amount
When Admin sets the business date to "27 April 2023"
And Admin successfully disburse the loan on "27 April 2023" with "30" EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "27 April 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "28 April 2023"
And Admin runs COB job
And Admin successfully disburse the loan on "28 April 2023" with "20" EUR transaction amount
When Admin sets the business date to "29 April 2023"
And Admin runs COB job
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 26 April 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| | | 27 April 2023 | | 30.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| | | 28 April 2023 | | 20.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 30 | 26 May 2023 | | 0.0 | 1050.0 | 0.0 | 10.0 | 0.0 | 1060.0 | 0.0 | 0.0 | 0.0 | 1060.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1050 | 0 | 10 | 0 | 1060 | 0 | 0 | 0 | 1060 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 26 April 2023 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 27 April 2023 | Disbursement | 30.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1030.0 |
| 27 April 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
| 28 April 2023 | Disbursement | 20.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1050.0 |
@TestRailId:C2708 @Skip
Scenario: Verify that the accrual is correct when it is on the installment start date
When Admin sets the business date to "1 May 2023"
When Admin creates a client with random data
And Admin successfully creates a new customised Loan submitted on date: "1 May 2023", with Principal: "1000", a loanTermFrequency: 1 months, and numberOfRepayments: 1
And Admin successfully approves the loan on "1 May 2023" with "1000" amount and expected disbursement date on "1 May 2023"
And Admin successfully disburse the loan on "1 May 2023" with "1000" EUR transaction amount
Then Loan has 1000 outstanding amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "1 May 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "2 May 2023"
And Admin runs COB job
When Admin sets the business date to "8 May 2023"
And Admin successfully disburse the loan on "8 May 2023" with "20" EUR transaction amount
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 May 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| | | 08 May 2023 | | 20.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 June 2023 | | 0.0 | 1020.0 | 0.0 | 10.0 | 0.0 | 1030.0 | 0.0 | 0.0 | 0.0 | 1030.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1020 | 0 | 10 | 0 | 1030 | 0 | 0 | 0 | 1030 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 May 2023 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 01 May 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
| 08 May 2023 | Disbursement | 20.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1020.0 |
@TestRailId:C2709
Scenario:Verify that the accrual transaction is created for disbursement fee
When Admin sets the business date to "1 May 2023"
When Admin creates a client with random data
And Admin successfully creates a new customised Loan submitted on date: "1 May 2023", with Principal: "1000", a loanTermFrequency: 1 months, and numberOfRepayments: 1
And Admin successfully approves the loan on "1 May 2023" with "1000" amount and expected disbursement date on "1 May 2023"
When Admin adds "LOAN_DISBURSEMENT_PERCENTAGE_FEE" charge with 1 % of transaction amount
And Admin successfully disburse the loan on "1 May 2023" with "1000" EUR transaction amount
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 May 2023 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 01 May 2023 | Repayment (at time of disbursement) | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 1000.0 |
| 01 May 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
@TestRailId:C2710 @Specific
Scenario: Verify global config charge-accrual-date function: single installment loan, charge-accrual-date = submitted-date, multiple charges with different submitted date
When Global config "charge-accrual-date" value set to "submitted-date"
When Admin sets the business date to "01 February 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 February 2023"
And Admin successfully approves the loan on "01 February 2023" with "1000" amount and expected disbursement date on "1 February 2023"
When Admin successfully disburse the loan on "01 February 2023" with "1000" EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "06 February 2023" due date and 10 EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "16 February 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "02 February 2023"
When Admin runs inline COB job for Loan
Then Loan Charges tab has the following data:
| Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding |
| Snooze fee | false | Specified due date | 06 February 2023 | Flat | 10.0 | 0.0 | 0.0 | 10.0 |
| Snooze fee | false | Specified due date | 16 February 2023 | Flat | 10.0 | 0.0 | 0.0 | 10.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 February 2023 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 01 February 2023 | Accrual | 20.0 | 0.0 | 0.0 | 20.0 | 0.0 | 0.0 |
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 February 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 30 | 03 March 2023 | | 0.0 | 1000.0 | 0.0 | 20.0 | 0.0 | 1020.0 | 0.0 | 0.0 | 0.0 | 1020.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000 | 0 | 20 | 0 | 1020 | 0 | 0 | 0 | 1020 |
When Global config "charge-accrual-date" value set to "due-date"
@TestRailId:C2711
Scenario: Verify global config charge-accrual-date function: single installment loan, charge-accrual-date = due-date, multiple charges with different submitted date
When Global config "charge-accrual-date" value set to "due-date"
When Admin sets the business date to "01 February 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 February 2023"
And Admin successfully approves the loan on "01 February 2023" with "1000" amount and expected disbursement date on "1 February 2023"
When Admin successfully disburse the loan on "01 February 2023" with "1000" EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "06 February 2023" due date and 10 EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "16 February 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "07 February 2023"
When Admin runs inline COB job for Loan
When Admin sets the business date to "17 February 2023"
When Admin runs inline COB job for Loan
Then Loan Charges tab has the following data:
| Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding |
| Snooze fee | false | Specified due date | 06 February 2023 | Flat | 10.0 | 0.0 | 0.0 | 10.0 |
| Snooze fee | false | Specified due date | 16 February 2023 | Flat | 10.0 | 0.0 | 0.0 | 10.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 February 2023 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 06 February 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
| 16 February 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 February 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 30 | 03 March 2023 | | 0.0 | 1000.0 | 0.0 | 20.0 | 0.0 | 1020.0 | 0.0 | 0.0 | 0.0 | 1020.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000 | 0 | 20 | 0 | 1020 | 0 | 0 | 0 | 1020 |
When Global config "charge-accrual-date" value set to "due-date"
@TestRailId:C2712 @Specific
Scenario: Verify global config charge-accrual-date function: multiple installment loan, charge-accrual-date = submitted-date, multiple charges with different submitted date, due dates in same repayment period
When Global config "charge-accrual-date" value set to "submitted-date"
When Admin sets the business date to "01 February 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN30 | 1 February 2023 | 3000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER |
And Admin successfully approves the loan on "01 February 2023" with "3000" amount and expected disbursement date on "1 February 2023"
When Admin successfully disburse the loan on "01 February 2023" with "3000" EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "06 February 2023" due date and 10 EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "16 February 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "02 February 2023"
When Admin runs inline COB job for Loan
Then Loan Charges tab has the following data:
| Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding |
| Snooze fee | false | Specified due date | 06 February 2023 | Flat | 10.0 | 0.0 | 0.0 | 10.0 |
| Snooze fee | false | Specified due date | 16 February 2023 | Flat | 10.0 | 0.0 | 0.0 | 10.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 February 2023 | Disbursement | 3000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 3000.0 |
| 01 February 2023 | Accrual | 20.0 | 0.0 | 0.0 | 20.0 | 0.0 | 0.0 |
Then Loan Repayment schedule has 3 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 February 2023 | | 3000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 28 | 01 March 2023 | | 2000.0 | 1000.0 | 0.0 | 20.0 | 0.0 | 1020.0 | 0.0 | 0.0 | 0.0 | 1020.0 |
| 2 | 31 | 01 April 2023 | | 1000.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 3 | 30 | 01 May 2023 | | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 3000 | 0 | 20 | 0 | 3020 | 0 | 0 | 0 | 3020 |
When Global config "charge-accrual-date" value set to "due-date"
@TestRailId:C2713
Scenario: Verify global config charge-accrual-date function: multiple installment loan, charge-accrual-date = due-date, multiple charges with different submitted date, due dates in same repayment period
When Global config "charge-accrual-date" value set to "due-date"
When Admin sets the business date to "01 February 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN30 | 1 February 2023 | 3000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER |
And Admin successfully approves the loan on "01 February 2023" with "3000" amount and expected disbursement date on "1 February 2023"
When Admin successfully disburse the loan on "01 February 2023" with "3000" EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "06 February 2023" due date and 10 EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "16 February 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "07 February 2023"
When Admin runs inline COB job for Loan
When Admin sets the business date to "17 February 2023"
When Admin runs inline COB job for Loan
Then Loan Charges tab has the following data:
| Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding |
| Snooze fee | false | Specified due date | 06 February 2023 | Flat | 10.0 | 0.0 | 0.0 | 10.0 |
| Snooze fee | false | Specified due date | 16 February 2023 | Flat | 10.0 | 0.0 | 0.0 | 10.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 February 2023 | Disbursement | 3000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 3000.0 |
| 06 February 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
| 16 February 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
Then Loan Repayment schedule has 3 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 February 2023 | | 3000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 28 | 01 March 2023 | | 2000.0 | 1000.0 | 0.0 | 20.0 | 0.0 | 1020.0 | 0.0 | 0.0 | 0.0 | 1020.0 |
| 2 | 31 | 01 April 2023 | | 1000.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 3 | 30 | 01 May 2023 | | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 3000 | 0 | 20 | 0 | 3020 | 0 | 0 | 0 | 3020 |
When Global config "charge-accrual-date" value set to "due-date"
@TestRailId:C2714 @Specific
Scenario: Verify global config charge-accrual-date function: multiple installment loan, charge-accrual-date = submitted-date, multiple charges with different submitted date, due dates in different repayment periods
When Global config "charge-accrual-date" value set to "submitted-date"
When Admin sets the business date to "01 February 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN30 | 1 February 2023 | 3000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER |
And Admin successfully approves the loan on "01 February 2023" with "3000" amount and expected disbursement date on "1 February 2023"
When Admin successfully disburse the loan on "01 February 2023" with "3000" EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "06 February 2023" due date and 10 EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "06 March 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "02 February 2023"
When Admin runs inline COB job for Loan
Then Loan Charges tab has the following data:
| Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding |
| Snooze fee | false | Specified due date | 06 February 2023 | Flat | 10.0 | 0.0 | 0.0 | 10.0 |
| Snooze fee | false | Specified due date | 06 March 2023 | Flat | 10.0 | 0.0 | 0.0 | 10.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 February 2023 | Disbursement | 3000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 3000.0 |
| 01 February 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
| 01 February 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
Then Loan Repayment schedule has 3 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 February 2023 | | 3000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 28 | 01 March 2023 | | 2000.0 | 1000.0 | 0.0 | 10.0 | 0.0 | 1010.0 | 0.0 | 0.0 | 0.0 | 1010.0 |
| 2 | 31 | 01 April 2023 | | 1000.0 | 1000.0 | 0.0 | 10.0 | 0.0 | 1010.0 | 0.0 | 0.0 | 0.0 | 1010.0 |
| 3 | 30 | 01 May 2023 | | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 3000 | 0 | 20 | 0 | 3020 | 0 | 0 | 0 | 3020 |
When Global config "charge-accrual-date" value set to "due-date"
@TestRailId:C2715
Scenario: Verify global config charge-accrual-date function: multiple installment loan, charge-accrual-date = due-date, multiple charges with different submitted date, due dates in different repayment periods
When Global config "charge-accrual-date" value set to "due-date"
When Admin sets the business date to "01 February 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN30 | 1 February 2023 | 3000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER |
And Admin successfully approves the loan on "01 February 2023" with "3000" amount and expected disbursement date on "1 February 2023"
When Admin successfully disburse the loan on "01 February 2023" with "3000" EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "06 February 2023" due date and 10 EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "06 March 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "07 February 2023"
When Admin runs inline COB job for Loan
When Admin sets the business date to "07 March 2023"
When Admin runs inline COB job for Loan
Then Loan Charges tab has the following data:
| Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding |
| Snooze fee | false | Specified due date | 06 February 2023 | Flat | 10.0 | 0.0 | 0.0 | 10.0 |
| Snooze fee | false | Specified due date | 06 March 2023 | Flat | 10.0 | 0.0 | 0.0 | 10.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 February 2023 | Disbursement | 3000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 3000.0 |
| 06 February 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
| 06 March 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
Then Loan Repayment schedule has 3 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 February 2023 | | 3000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 28 | 01 March 2023 | | 2000.0 | 1000.0 | 0.0 | 10.0 | 0.0 | 1010.0 | 0.0 | 0.0 | 0.0 | 1010.0 |
| 2 | 31 | 01 April 2023 | | 1000.0 | 1000.0 | 0.0 | 10.0 | 0.0 | 1010.0 | 0.0 | 0.0 | 0.0 | 1010.0 |
| 3 | 30 | 01 May 2023 | | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 3000 | 0 | 20 | 0 | 3020 | 0 | 0 | 0 | 3020 |
When Global config "charge-accrual-date" value set to "due-date"
@TestRailId:C2716 @Specific
Scenario: Verify global config charge-accrual-date function: single installment loan, charge-accrual-date = submitted-date, multi disbursement
When Global config "charge-accrual-date" value set to "submitted-date"
When Admin sets the business date to "01 February 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 February 2023"
And Admin successfully approves the loan on "01 February 2023" with "1000" amount and expected disbursement date on "1 February 2023"
When Admin successfully disburse the loan on "01 February 2023" with "500" EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "06 February 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "02 February 2023"
When Admin runs inline COB job for Loan
When Admin sets the business date to "04 February 2023"
When Admin successfully disburse the loan on "04 February 2023" with "500" EUR transaction amount
When Admin runs inline COB job for Loan
Then Loan Charges tab has the following data:
| Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding |
| Snooze fee | false | Specified due date | 06 February 2023 | Flat | 10.0 | 0.0 | 0.0 | 10.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 February 2023 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 |
| 01 February 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
| 04 February 2023 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 04 February 2023 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| | | 01 February 2023 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 30 | 03 March 2023 | | 0.0 | 1000.0 | 0.0 | 10.0 | 0.0 | 1010.0 | 0.0 | 0.0 | 0.0 | 1010.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000 | 0 | 10 | 0 | 1010 | 0 | 0 | 0 | 1010 |
When Global config "charge-accrual-date" value set to "due-date"
@TestRailId:C2717 @Specific
Scenario: Verify global config charge-accrual-date function: single installment loan, charge-accrual-date = submitted-date, repayment reversal
When Global config "charge-accrual-date" value set to "submitted-date"
When Admin sets the business date to "01 February 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 February 2023"
And Admin successfully approves the loan on "01 February 2023" with "1000" amount and expected disbursement date on "1 February 2023"
When Admin successfully disburse the loan on "01 February 2023" with "1000" EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "06 February 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "02 February 2023"
When Admin runs inline COB job for Loan
When Admin sets the business date to "04 February 2023"
And Customer makes "AUTOPAY" repayment on "04 February 2023" with 500 EUR transaction amount
When Admin runs inline COB job for Loan
When Admin sets the business date to "05 February 2023"
When Customer undo "1"th "Repayment" transaction made on "04 February 2023"
When Admin runs inline COB job for Loan
When Admin sets the business date to "06 February 2023"
When Admin runs inline COB job for Loan
Then Loan Charges tab has the following data:
| Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding |
| Snooze fee | false | Specified due date | 06 February 2023 | Flat | 10.0 | 0.0 | 0.0 | 10.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 February 2023 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 01 February 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
| 04 February 2023 | Repayment | 500.0 | 490.0 | 0.0 | 10.0 | 0.0 | 510.0 |
Then On Loan Transactions tab the "Repayment" Transaction with date "04 February 2023" is reverted
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 February 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 30 | 03 March 2023 | | 0.0 | 1000.0 | 0.0 | 10.0 | 0.0 | 1010.0 | 0.0 | 0.0 | 0.0 | 1010.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000 | 0 | 10 | 0 | 1010 | 0 | 0 | 0 | 1010 |
When Global config "charge-accrual-date" value set to "due-date"
@TestRailId:C2718 @Specific
Scenario: Verify global config charge-accrual-date function: single installment loan, charge-accrual-date = submitted-date, waive charge, undo waive
When Global config "charge-accrual-date" value set to "submitted-date"
When Admin sets the business date to "01 February 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 February 2023"
And Admin successfully approves the loan on "01 February 2023" with "1000" amount and expected disbursement date on "1 February 2023"
When Admin successfully disburse the loan on "01 February 2023" with "1000" EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "06 February 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "02 February 2023"
When Admin runs inline COB job for Loan
When Admin sets the business date to "04 February 2023"
And Admin waives due date charge
When Admin runs inline COB job for Loan
When Admin sets the business date to "05 February 2023"
And Admin makes waive undone for charge
When Admin runs inline COB job for Loan
When Admin sets the business date to "06 February 2023"
When Admin runs inline COB job for Loan
Then Loan Charges tab has the following data:
| Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding |
| Snooze fee | false | Specified due date | 06 February 2023 | Flat | 10.0 | 0.0 | 0.0 | 10.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 February 2023 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 01 February 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
| 04 February 2023 | Waive loan charges | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 1000.0 |
Then On Loan Transactions tab the "Waive loan charges" Transaction with date "04 February 2023" is reverted
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 February 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 30 | 03 March 2023 | | 0.0 | 1000.0 | 0.0 | 10.0 | 0.0 | 1010.0 | 0.0 | 0.0 | 0.0 | 1010.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000 | 0 | 10 | 0 | 1010 | 0 | 0 | 0 | 1010 |
When Global config "charge-accrual-date" value set to "due-date"
@TestRailId:C2719 @Specific
Scenario: Verify global config charge-accrual-date function: multiple installment loan, charge-accrual-date = submitted-date, multi disbursement
When Global config "charge-accrual-date" value set to "submitted-date"
When Admin sets the business date to "01 February 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN30 | 1 February 2023 | 3000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER |
And Admin successfully approves the loan on "01 February 2023" with "3000" amount and expected disbursement date on "1 February 2023"
When Admin successfully disburse the loan on "01 February 2023" with "2000" EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "06 March 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "02 February 2023"
When Admin runs inline COB job for Loan
When Admin sets the business date to "04 February 2023"
When Admin successfully disburse the loan on "04 February 2023" with "1000" EUR transaction amount
When Admin runs inline COB job for Loan
Then Loan Charges tab has the following data:
| Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding |
| Snooze fee | false | Specified due date | 06 March 2023 | Flat | 10.0 | 0.0 | 0.0 | 10.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 February 2023 | Disbursement | 2000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 2000.0 |
| 01 February 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
| 04 February 2023 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 3000.0 |
Then Loan Repayment schedule has 3 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 February 2023 | | 2000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| | | 04 February 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 28 | 01 March 2023 | | 2000.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 2 | 31 | 01 April 2023 | | 1000.0 | 1000.0 | 0.0 | 10.0 | 0.0 | 1010.0 | 0.0 | 0.0 | 0.0 | 1010.0 |
| 3 | 30 | 01 May 2023 | | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 3000 | 0 | 10 | 0 | 3010 | 0 | 0 | 0 | 3010 |
When Global config "charge-accrual-date" value set to "due-date"
@TestRailId:C2720 @Specific
Scenario: Verify global config charge-accrual-date function: multiple installment loan, charge-accrual-date = submitted-date, repayment reversal
When Global config "charge-accrual-date" value set to "submitted-date"
When Admin sets the business date to "01 February 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN30 | 1 February 2023 | 3000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER |
And Admin successfully approves the loan on "01 February 2023" with "3000" amount and expected disbursement date on "1 February 2023"
When Admin successfully disburse the loan on "01 February 2023" with "3000" EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "06 March 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "02 February 2023"
When Admin runs inline COB job for Loan
When Admin sets the business date to "04 February 2023"
And Customer makes "AUTOPAY" repayment on "04 February 2023" with 500 EUR transaction amount
When Admin runs inline COB job for Loan
When Admin sets the business date to "05 February 2023"
When Customer undo "1"th "Repayment" transaction made on "04 February 2023"
When Admin runs inline COB job for Loan
When Admin sets the business date to "06 February 2023"
When Admin runs inline COB job for Loan
Then Loan Charges tab has the following data:
| Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding |
| Snooze fee | false | Specified due date | 06 March 2023 | Flat | 10.0 | 0.0 | 0.0 | 10.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 February 2023 | Disbursement | 3000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 3000.0 |
| 01 February 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
| 04 February 2023 | Repayment | 500.0 | 500.0 | 0.0 | 0.0 | 0.0 | 2500.0 |
Then On Loan Transactions tab the "Repayment" Transaction with date "04 February 2023" is reverted
Then Loan Repayment schedule has 3 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 February 2023 | | 3000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 28 | 01 March 2023 | | 2000.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 2 | 31 | 01 April 2023 | | 1000.0 | 1000.0 | 0.0 | 10.0 | 0.0 | 1010.0 | 0.0 | 0.0 | 0.0 | 1010.0 |
| 3 | 30 | 01 May 2023 | | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 3000 | 0 | 10 | 0 | 3010 | 0 | 0 | 0 | 3010 |
When Global config "charge-accrual-date" value set to "due-date"
@TestRailId:C2721 @Specific
Scenario: Verify global config charge-accrual-date function: multiple installment loan, charge-accrual-date = submitted-date, waive charge, undo waive
When Global config "charge-accrual-date" value set to "submitted-date"
When Admin sets the business date to "01 February 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN30 | 1 February 2023 | 3000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER |
And Admin successfully approves the loan on "01 February 2023" with "3000" amount and expected disbursement date on "1 February 2023"
When Admin successfully disburse the loan on "01 February 2023" with "3000" EUR transaction amount
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "06 March 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "02 February 2023"
When Admin runs inline COB job for Loan
When Admin sets the business date to "04 February 2023"
And Admin waives due date charge
When Admin runs inline COB job for Loan
When Admin sets the business date to "05 February 2023"
And Admin makes waive undone for charge
When Admin runs inline COB job for Loan
When Admin sets the business date to "06 February 2023"
When Admin runs inline COB job for Loan
Then Loan Charges tab has the following data:
| Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding |
| Snooze fee | false | Specified due date | 06 March 2023 | Flat | 10.0 | 0.0 | 0.0 | 10.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 February 2023 | Disbursement | 3000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 3000.0 |
| 01 February 2023 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
| 04 February 2023 | Waive loan charges | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 3000.0 |
Then On Loan Transactions tab the "Waive loan charges" Transaction with date "04 February 2023" is reverted
Then Loan Repayment schedule has 3 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 February 2023 | | 3000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 28 | 01 March 2023 | | 2000.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 2 | 31 | 01 April 2023 | | 1000.0 | 1000.0 | 0.0 | 10.0 | 0.0 | 1010.0 | 0.0 | 0.0 | 0.0 | 1010.0 |
| 3 | 30 | 01 May 2023 | | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 3000 | 0 | 10 | 0 | 3010 | 0 | 0 | 0 | 3010 |
When Global config "charge-accrual-date" value set to "due-date"
@TestRailId:C2789 @Specific
Scenario: Verify accrual transaction for new fee for loan with accrued snooze fee and schedule adjustment
When Global config "charge-accrual-date" value set to "submitted-date"
When Admin sets the business date to "19 May 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN30 | 19 May 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 30 | DAYS | 30 | DAYS | 1 | 0 | 0 | 0 | PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER |
And Admin successfully approves the loan on "19 May 2023" with "1000" amount and expected disbursement date on "19 May 2023"
When Admin successfully disburse the loan on "19 May 2023" with "1000" EUR transaction amount
When Admin sets the business date to "12 June 2023"
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "18 July 2023" due date and 10 EUR transaction amount
When Batch API call with steps: rescheduleLoan from "18 June 2023" to "18 July 2023" submitted on date: "19 May 2023", approveReschedule on date: "19 May 2023" runs with enclosingTransaction: "true"
When Admin sets the business date to "13 June 2023"
When Admin runs inline COB job for Loan
Then Loan Transactions tab has a transaction with date: "12 June 2023", and with the following data:
| Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
When Admin sets the business date to "18 July 2023"
And Customer makes "AUTOPAY" repayment on "18 July 2023" with 1010 EUR transaction amount
When Admin sets the business date to "19 July 2023"
When Customer makes a repayment undo on "19 July 2023"
When Admin adds "LOAN_NSF_FEE" due date charge with "19 July 2023" due date and 10 EUR transaction amount
When Admin sets the business date to "20 July 2023"
When Admin runs inline COB job for Loan
Then Loan Transactions tab has a transaction with date: "19 July 2023", and with the following data:
| Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| Accrual | 10.0 | 0.0 | 0.0 | 0.0 | 10.0 | 0.0 |
Then Loan Repayment schedule has 2 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 19 May 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 60 | 18 July 2023 | | 0.0 | 1000.0 | 0.0 | 10.0 | 0.0 | 1010.0 | 0.0 | 0.0 | 0.0 | 1010.0 |
| 2 | 1 | 19 July 2023 | | 0.0 | 0.0 | 0.0 | 0.0 | 10.0 | 10.0 | 0.0 | 0.0 | 0.0 | 10.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000 | 0 | 10 | 10 | 1020 | 0 | 0 | 0 | 1020 |
When Global config "charge-accrual-date" value set to "due-date"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,183 @@
@Product
Feature: LoanProduct
@Scenario1 @TestRailId:C52
Scenario: As a user I would like to fully repay the loan in time
When Admin sets the business date to "12 December 2021"
When Admin creates a client with random data
And Admin successfully creates a new customised Loan submitted on date: "12 December 2021", with Principal: "1000", a loanTermFrequency: 1 months, and numberOfRepayments: 1
And Admin successfully approves the loan on "12 December 2021" with "1000" amount and expected disbursement date on "1 July 2022"
And Admin successfully disburse the loan on "12 December 2021" with "1000" EUR transaction amount
Then Loan has 1000 outstanding amount
When Admin sets the business date to "30 December 2021"
And Customer makes "AUTOPAY" repayment on "30 December 2021" with 1000 EUR transaction amount
Then Repayment transaction is created with 1000 amount and "AUTOPAY" type
Then Loan has 0 outstanding amount
@Scenario2 @TestRailId:C53
Scenario: As a user I would like to fully repay a loan which was disbursed 2 times
When Admin sets the business date to "1 June 2022"
When Admin creates a client with random data
And Admin successfully creates a new customised Loan submitted on date: "1 June 2022", with Principal: "1000", a loanTermFrequency: 1 months, and numberOfRepayments: 1
And Admin successfully approves the loan on "1 June 2022" with "1000" amount and expected disbursement date on "1 July 2022"
And Admin successfully disburse the loan on "1 June 2022" with "1000" EUR transaction amount
Then Loan has 1000 outstanding amount
When Admin sets the business date to "5 June 2022"
And Admin successfully disburse the loan on "5 June 2022" with "500" EUR transaction amount
Then Loan has 1500 outstanding amount
When Admin sets the business date to "1 July 2022"
And Customer makes "AUTOPAY" repayment on "1 July 2022" with 1500 EUR transaction amount
Then Repayment transaction is created with 1500 amount and "AUTOPAY" type
Then Loan has 0 outstanding amount
@Scenario3 @TestRailId:C54
Scenario: As a user I would like to fully repay a multi disbursed loan with 2 repayments
When Admin sets the business date to "1 June 2022"
When Admin creates a client with random data
And Admin successfully creates a new customised Loan submitted on date: "1 June 2022", with Principal: "1000", a loanTermFrequency: 1 months, and numberOfRepayments: 1
And Admin successfully approves the loan on "1 June 2022" with "1000" amount and expected disbursement date on "1 July 2022"
And Admin successfully disburse the loan on "1 June 2022" with "1000" EUR transaction amount
Then Loan has 1000 outstanding amount
When Admin sets the business date to "15 June 2022"
And Customer makes "AUTOPAY" repayment on "15 June 2022" with 500 EUR transaction amount
Then Repayment transaction is created with 500 amount and "AUTOPAY" type
Then Loan has 500 outstanding amount
When Admin runs the Increase Business Date by 1 day job
And Admin successfully disburse the loan on "16 June 2022" with "500" EUR transaction amount
Then Loan has 1000 outstanding amount
When Admin sets the business date to "15 July 2022"
And Customer makes "AUTOPAY" repayment on "15 July 2022" with 1000 EUR transaction amount
Then Loan has 0 outstanding amount
@Scenario4 @TestRailId:C55
Scenario: As a user I would like to multi disburse a loan which was previously fully paid
When Admin sets the business date to "1 June 2022"
When Admin creates a client with random data
And Admin successfully creates a new customised Loan submitted on date: "1 June 2022", with Principal: "1000", a loanTermFrequency: 1 months, and numberOfRepayments: 1
And Admin successfully approves the loan on "1 June 2022" with "1000" amount and expected disbursement date on "1 July 2022"
And Admin successfully disburse the loan on "1 June 2022" with "1000" EUR transaction amount
Then Loan has 1000 outstanding amount
When Admin sets the business date to "15 June 2022"
And Customer makes "AUTOPAY" repayment on "15 June 2022" with 1000 EUR transaction amount
Then Repayment transaction is created with 1000 amount and "AUTOPAY" type
Then Loan has 0 outstanding amount
When Admin runs the Increase Business Date by 1 day job
And Admin successfully disburse the loan on "16 June 2022" with "500" EUR transaction amount
Then Loan has 500 outstanding amount
When Admin sets the business date to "15 July 2022"
And Customer makes "AUTOPAY" repayment on "15 July 2022" with 500 EUR transaction amount
Then Loan has 0 outstanding amount
@Scenario5 @TestRailId:C57
Scenario: As a user I would like to fully repay a loan and check a repayment reversal with NSF fee
When Admin sets the business date to "1 June 2022"
When Admin creates a client with random data
And Admin successfully creates a new customised Loan submitted on date: "1 June 2022", with Principal: "1000", a loanTermFrequency: 1 months, and numberOfRepayments: 1
And Admin successfully approves the loan on "1 June 2022" with "1000" amount and expected disbursement date on "1 July 2022"
And Admin successfully disburse the loan on "1 June 2022" with "1000" EUR transaction amount
Then Loan has 1000 outstanding amount
When Admin sets the business date to "15 June 2022"
And Customer makes "AUTOPAY" repayment on "15 June 2022" with 1000 EUR transaction amount
Then Repayment transaction is created with 1000 amount and "AUTOPAY" type
Then Loan has 0 outstanding amount
When Customer makes a repayment undo on "15 June 2022"
Then Loan has 1000 outstanding amount
When Admin sets the business date to "18 June 2022"
And Admin adds an NSF fee because of payment bounce with "18 June 2022" transaction date
Then Loan has 1010 outstanding amount
And Customer makes "AUTOPAY" repayment on "18 June 2022" with 1010 EUR transaction amount
Then Loan has 0 outstanding amount
@Scenario6 @TestRailId:C58
Scenario: As a user I would like to repay the half amount of the loan and check a repayment reversal with NSF fee
When Admin sets the business date to "1 June 2022"
When Admin creates a client with random data
And Admin successfully creates a new customised Loan submitted on date: "1 June 2022", with Principal: "1000", a loanTermFrequency: 1 months, and numberOfRepayments: 1
And Admin successfully approves the loan on "1 June 2022" with "1000" amount and expected disbursement date on "1 July 2022"
And Admin successfully disburse the loan on "1 June 2022" with "1000" EUR transaction amount
Then Loan has 1000 outstanding amount
When Admin sets the business date to "15 June 2022"
And Customer makes "AUTOPAY" repayment on "15 June 2022" with 500 EUR transaction amount
Then Repayment transaction is created with 500 amount and "AUTOPAY" type
Then Loan has 500 outstanding amount
When Customer makes a repayment undo on "15 June 2022"
Then Loan has 1000 outstanding amount
When Admin sets the business date to "18 June 2022"
And Admin adds an NSF fee because of payment bounce with "18 June 2022" transaction date
Then Loan has 1010 outstanding amount
And Customer makes "AUTOPAY" repayment on "18 June 2022" with 1010 EUR transaction amount
Then Loan has 0 outstanding amount
@Scenario7 @TestRailId:C59
Scenario: As a user I would like to fully repay a loan then reverse the repayment + add an NSF fee after the 1 month period
When Admin sets the business date to "1 June 2022"
When Admin creates a client with random data
And Admin successfully creates a new customised Loan submitted on date: "1 June 2022", with Principal: "1000", a loanTermFrequency: 1 months, and numberOfRepayments: 1
And Admin successfully approves the loan on "1 June 2022" with "1000" amount and expected disbursement date on "1 July 2022"
And Admin successfully disburse the loan on "1 June 2022" with "1000" EUR transaction amount
Then Loan has 1000 outstanding amount
When Admin sets the business date to "1 July 2022"
And Customer makes "AUTOPAY" repayment on "1 July 2022" with 1000 EUR transaction amount
Then Repayment transaction is created with 1000 amount and "AUTOPAY" type
Then Loan has 0 outstanding amount
When Customer makes a repayment undo on "1 July 2022"
Then Loan has 1000 outstanding amount
When Admin sets the business date to "5 July 2022"
And Admin adds an NSF fee because of payment bounce with "5 July 2022" transaction date
Then Loan has 1010 outstanding amount
And Customer makes "AUTOPAY" repayment on "5 July 2022" with 1010 EUR transaction amount
Then Loan has 0 outstanding amount
@Scenario8 @TestRailId:C60
Scenario: As a user I would like to repay the half amount of the loan and do a refund
When Admin sets the business date to "1 June 2022"
When Admin creates a client with random data
And Admin successfully creates a new customised Loan submitted on date: "1 June 2022", with Principal: "1000", a loanTermFrequency: 1 months, and numberOfRepayments: 1
And Admin successfully approves the loan on "1 June 2022" with "1000" amount and expected disbursement date on "1 July 2022"
And Admin successfully disburse the loan on "1 June 2022" with "1000" EUR transaction amount
Then Loan has 1000 outstanding amount
When Admin sets the business date to "25 June 2022"
And Customer makes "AUTOPAY" repayment on "25 June 2022" with 500 EUR transaction amount
Then Repayment transaction is created with 500 amount and "AUTOPAY" type
Then Loan has 500 outstanding amount
When Admin sets the business date to "1 July 2022"
When Refund happens on "1 July 2022" with 500 EUR transaction amount
Then Loan has 0 outstanding amount
@Scenario9 @TestRailId:C61
Scenario: As a user I would like to repay the half amount of the loan and do a refund + repayment reversal on the repayment
When Admin sets the business date to "1 June 2022"
When Admin creates a client with random data
And Admin successfully creates a new customised Loan submitted on date: "1 June 2022", with Principal: "1000", a loanTermFrequency: 1 months, and numberOfRepayments: 1
And Admin successfully approves the loan on "1 June 2022" with "1000" amount and expected disbursement date on "1 July 2022"
And Admin successfully disburse the loan on "1 June 2022" with "1000" EUR transaction amount
Then Loan has 1000 outstanding amount
And Admin sets the business date to "25 June 2022"
And Customer makes "AUTOPAY" repayment on "25 June 2022" with 500 EUR transaction amount
Then Repayment transaction is created with 500 amount and "AUTOPAY" type
Then Loan has 500 outstanding amount
When Admin sets the business date to "1 July 2022"
When Refund happens on "1 July 2022" with 500 EUR transaction amount
Then Loan has 0 outstanding amount
When Customer makes a repayment undo on "25 June 2022"
Then Loan has 500 outstanding amount
@Scenario10 @TestRailId:C62
Scenario: As a user I would like to repay the half amount of the loan and do a refund + repayment reversal + refund reversal
When Admin sets the business date to "1 June 2022"
When Admin creates a client with random data
And Admin successfully creates a new customised Loan submitted on date: "1 June 2022", with Principal: "1000", a loanTermFrequency: 1 months, and numberOfRepayments: 1
And Admin successfully approves the loan on "1 June 2022" with "1000" amount and expected disbursement date on "1 July 2022"
And Admin successfully disburse the loan on "1 June 2022" with "1000" EUR transaction amount
Then Loan has 1000 outstanding amount
When Admin sets the business date to "25 June 2022"
And Customer makes "AUTOPAY" repayment on "25 June 2022" with 500 EUR transaction amount
Then Repayment transaction is created with 500 amount and "AUTOPAY" type
Then Loan has 500 outstanding amount
When Admin sets the business date to "1 July 2022"
When Refund happens on "1 July 2022" with 500 EUR transaction amount
Then Loan has 0 outstanding amount
When Customer makes a repayment undo on "25 June 2022"
Then Loan has 500 outstanding amount
When Refund undo happens on "1 July 2022"
Then Loan has 1000 outstanding amount
@@ -0,0 +1,572 @@
@LoanReAgingFeature
Feature: LoanReAging
@TestRailId:C3050 @AdvancedPaymentAllocation
Scenario: Verify that Loan re-aging transaction happy path works properly
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "1000" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "1000" EUR transaction amount
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 31 January 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
When Admin sets the business date to "20 February 2024"
When Admin runs inline COB job for Loan
Then Admin checks that delinquency range is: "RANGE_30" and has delinquentDate "2024-01-19"
When Admin creates a Loan re-aging transaction with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 1 | MONTHS | 01 March 2024 | 6 |
Then Loan Repayment schedule has 10 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | 20 February 2024| 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 15 | 31 January 2024 | 20 February 2024| 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 15 | 15 February 2024 | 20 February 2024| 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 5 | 15 | 01 March 2024 | | 625.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
| 6 | 31 | 01 April 2024 | | 500.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
| 7 | 30 | 01 May 2024 | | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
| 8 | 31 | 01 June 2024 | | 250.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
| 9 | 30 | 01 July 2024 | | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
| 10 | 31 | 01 August 2024 | | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 250.0| 0.0 | 0.0 | 750.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
| 01 January 2024 | Down Payment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
| 20 February 2024 | Re-age | 750.0 | 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | false |
Then Admin checks that delinquency range is: "NO_DELINQUENCY" and has delinquentDate ""
@TestRailId:C3051 @AdvancedPaymentAllocation
Scenario: Verify that Loan re-aging transaction made by Loan external ID happy path works properly
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "1000" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "1000" EUR transaction amount
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 31 January 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
When Admin sets the business date to "20 February 2024"
When Admin creates a Loan re-aging transaction by Loan external ID with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 30 | DAYS | 10 March 2024 | 2 |
Then Loan Repayment schedule has 6 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | 20 February 2024| 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 15 | 31 January 2024 | 20 February 2024| 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 15 | 15 February 2024 | 20 February 2024| 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 5 | 24 | 10 March 2024 | | 375.0 | 375.0 | 0.0 | 0.0 | 0.0 | 375.0 | 0.0 | 0.0 | 0.0 | 375.0 |
| 6 | 30 | 09 April 2024 | | 0.0 | 375.0 | 0.0 | 0.0 | 0.0 | 375.0 | 0.0 | 0.0 | 0.0 | 375.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
| 01 January 2024 | Down Payment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
| 20 February 2024 | Re-age | 750.0 | 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | false |
@TestRailId:C3052 @AdvancedPaymentAllocation
Scenario: Verify that Loan re-aging transaction undo works properly
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "1000" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "1000" EUR transaction amount
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 31 January 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
When Admin sets the business date to "20 February 2024"
When Admin runs inline COB job for Loan
Then Admin checks that delinquency range is: "RANGE_30" and has delinquentDate "2024-01-19"
When Admin creates a Loan re-aging transaction by Loan external ID with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 1 | DAYS | 10 March 2024 | 1 |
When Admin sets the business date to "25 February 2024"
Then Loan Repayment schedule has 5 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | 20 February 2024| 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 15 | 31 January 2024 | 20 February 2024| 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 15 | 15 February 2024 | 20 February 2024| 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 5 | 24 | 10 March 2024 | | 0.0 | 750.0 | 0.0 | 0.0 | 0.0 | 750.0 | 0.0 | 0.0 | 0.0 | 750.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
| 01 January 2024 | Down Payment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
| 20 February 2024 | Re-age | 750.0 | 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | false |
Then Admin checks that delinquency range is: "NO_DELINQUENCY" and has delinquentDate ""
When Admin successfully undo Loan re-aging transaction
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 31 January 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
| 01 January 2024 | Down Payment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
| 20 February 2024 | Re-age | 750.0 | 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | true |
Then Admin checks that delinquency range is: "RANGE_30" and has delinquentDate "2024-01-19"
@TestRailId:C3053 @AdvancedPaymentAllocation
Scenario: Verify that Loan re-aging transaction works properly when chargeback happens after re-aging
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 January 2024 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "1000" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "1000" EUR transaction amount
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 2 | 15 | 16 January 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 31 January 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
When Admin sets the business date to "20 February 2024"
When Admin runs inline COB job for Loan
Then Admin checks that delinquency range is: "RANGE_30" and has delinquentDate "2024-01-04"
And Customer makes "AUTOPAY" repayment on "01 January 2024" with 250 EUR transaction amount
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 31 January 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
| 01 January 2024 | Repayment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
When Admin sets the business date to "21 February 2024"
When Admin creates a Loan re-aging transaction by Loan external ID with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 2 | MONTHS | 10 March 2024 | 3 |
Then Loan Repayment schedule has 7 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | 21 February 2024| 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 15 | 31 January 2024 | 21 February 2024| 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 15 | 15 February 2024 | 21 February 2024| 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 5 | 24 | 10 March 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 6 | 61 | 10 May 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 7 | 61 | 10 July 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
| 01 January 2024 | Repayment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
| 21 February 2024 | Re-age | 750.0 | 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | false |
When Admin sets the business date to "25 February 2024"
When Admin makes "REPAYMENT_ADJUSTMENT_CHARGEBACK" chargeback with 250 EUR transaction amount
Then Loan Repayment schedule has 7 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | 21 February 2024| 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 15 | 31 January 2024 | 21 February 2024| 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 15 | 15 February 2024 | 21 February 2024| 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 5 | 24 | 10 March 2024 | | 500.0 | 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 0.0 | 0.0 | 0.0 | 500.0 |
| 6 | 61 | 10 May 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 7 | 61 | 10 July 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1250.0 | 0.0 | 0.0 | 0.0 | 1250.0 | 250.0| 0.0 | 0.0 | 1000.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
| 01 January 2024 | Repayment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
| 21 February 2024 | Re-age | 750.0 | 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | false |
| 25 February 2024 | Chargeback | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
@Skip @TestRailId:C3054 @AdvancedPaymentAllocation
Scenario: Verify Loan re-aging transaction - reverse-replay scenario 1
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 500 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "500" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "500" EUR transaction amount
When Admin sets the business date to "27 February 2024"
When Admin creates a Loan re-aging transaction with the following data:
| frequencyType | startDate | numberOfInstallments |
| MONTHS | 01 March 2024 | 6 |
And Customer makes "AUTOPAY" repayment on "01 February 2024" with 125 EUR transaction amount
When Admin successfully undo Loan re-aging transaction
# TODO fill with proper expected data
# Then Loan Repayment schedule has 10 periods, with the following data for periods:
# | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
# | | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
# | 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
# | 2 | 15 | 16 January 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
# | 3 | 15 | 31 January 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
# | 4 | 15 | 15 February 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
# | 5 | 15 | 01 March 2024 | | 750.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
# | 6 | 15 | 01 April 2024 | | 625.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
# | 7 | 15 | 01 May 2024 | | 500.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
# | 8 | 15 | 01 June 2024 | | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
# | 9 | 15 | 01 July 2024 | | 250.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
# | 10 | 15 | 01 August 2024 | | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
# Then Loan Repayment schedule has the following data in Total row:
# | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
# | 1000.0 | 0.0 | 0.0 | 20.0 | 1020.0 | 0.0 | 0.0 | 0.0 | 1020.0 |
# Then Loan Transactions tab has the following data:
# | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
# | 01 January 2024 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 | false |
# | 01 January 2024 | Down Payment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 375.0 | false |
# | 01 February 2024 | Repayment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 250.0 | false |
# | 27 February 2024 | Re-age | 375.0 | 375.0 | 0.0 | 0.0 | 0.0 | 125.0 | true |
# Then Admin checks that delinquency range is: "RANGE_30" and has delinquentDate "2024-01-19"
@Skip @TestRailId:C3055 @AdvancedPaymentAllocation
Scenario: Verify Loan re-aging transaction - reverse-replay scenario 2
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 500 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "500" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "500" EUR transaction amount
When Admin sets the business date to "27 February 2024"
When Admin creates a Loan re-aging transaction with the following data:
| frequencyType | startDate | numberOfInstallments |
| MONTHS | 01 March 2024 | 6 |
When Customer undo "1"th "Down Payment" transaction made on "01 January 2024"
When Admin successfully undo Loan re-aging transaction
# TODO fill with proper expected data and checks
@TestRailId:C3056 @AdvancedPaymentAllocation
Scenario: Verify that Loan re-aging transaction - chargeback before maturity and prior to re-aging
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 January 2024 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "1000" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "1000" EUR transaction amount
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 2 | 15 | 16 January 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 31 January 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
When Admin sets the business date to "01 January 2024"
And Customer makes "AUTOPAY" repayment on "01 January 2024" with 250 EUR transaction amount
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 31 January 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
| 01 January 2024 | Repayment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
When Admin sets the business date to "02 February 2024"
When Admin makes "REPAYMENT_ADJUSTMENT_CHARGEBACK" chargeback with 125 EUR transaction amount
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 31 January 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 375.0 | 0.0 | 0.0 | 0.0 | 375.0 | 0.0 | 0.0 | 0.0 | 375.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
| 01 January 2024 | Repayment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
| 02 February 2024 | Chargeback | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 875.0 | false |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1125.0 | 0.0 | 0.0 | 0.0 | 1125.0 | 250.0| 0.0 | 0.0 | 875.0 |
When Admin sets the business date to "21 February 2024"
When Admin creates a Loan re-aging transaction by Loan external ID with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 2 | MONTHS | 10 March 2024 | 3 |
Then Loan Repayment schedule has 7 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | 21 February 2024| 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 15 | 31 January 2024 | 21 February 2024| 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 15 | 15 February 2024 | 21 February 2024| 875.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 5 | 24 | 10 March 2024 | | 583.33 | 291.67 | 0.0 | 0.0 | 0.0 | 291.67| 0.0 | 0.0 | 0.0 | 291.67 |
| 6 | 61 | 10 May 2024 | | 291.66 | 291.67 | 0.0 | 0.0 | 0.0 | 291.67| 0.0 | 0.0 | 0.0 | 291.67 |
| 7 | 61 | 10 July 2024 | | 0.0 | 291.66 | 0.0 | 0.0 | 0.0 | 291.66| 0.0 | 0.0 | 0.0 | 291.66 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
| 01 January 2024 | Repayment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
| 02 February 2024 | Chargeback | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 875.0 | false |
| 21 February 2024 | Re-age | 875.0 | 875.0 | 0.0 | 0.0 | 0.0 | 0.0 | false |
@TestRailId:C3057 @AdvancedPaymentAllocation
Scenario: Verify that Loan re-aging transaction - chargeback after maturity date and prior to re-aging
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 January 2024 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "1000" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "1000" EUR transaction amount
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 2 | 15 | 16 January 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 31 January 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
When Admin sets the business date to "01 January 2024"
And Customer makes "AUTOPAY" repayment on "01 January 2024" with 250 EUR transaction amount
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 31 January 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
| 01 January 2024 | Repayment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
When Admin sets the business date to "20 February 2024"
When Admin makes "REPAYMENT_ADJUSTMENT_CHARGEBACK" chargeback with 125 EUR transaction amount
Then Loan Repayment schedule has 5 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 31 January 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 5 | 5 | 20 February 2024 | | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
| 01 January 2024 | Repayment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
| 20 February 2024 | Chargeback | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 875.0 | false |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1125.0 | 0.0 | 0.0 | 0.0 | 1125.0 | 250.0| 0.0 | 0.0 | 875.0 |
When Admin sets the business date to "21 February 2024"
When Admin creates a Loan re-aging transaction by Loan external ID with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 2 | MONTHS | 10 March 2024 | 3 |
Then Loan Repayment schedule has 8 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | 21 February 2024| 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 15 | 31 January 2024 | 21 February 2024| 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 15 | 15 February 2024 | 21 February 2024| 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 5 | 5 | 20 February 2024 | 21 February 2024| 875.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 6 | 19 | 10 March 2024 | | 583.33 | 291.67 | 0.0 | 0.0 | 0.0 | 291.67| 0.0 | 0.0 | 0.0 | 291.67 |
| 7 | 61 | 10 May 2024 | | 291.66 | 291.67 | 0.0 | 0.0 | 0.0 | 291.67| 0.0 | 0.0 | 0.0 | 291.67 |
| 8 | 61 | 10 July 2024 | | 0.0 | 291.66 | 0.0 | 0.0 | 0.0 | 291.66| 0.0 | 0.0 | 0.0 | 291.66 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
| 01 January 2024 | Repayment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
| 20 February 2024 | Chargeback | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 875.0 | false |
| 21 February 2024 | Re-age | 875.0 | 875.0 | 0.0 | 0.0 | 0.0 | 0.0 | false |
@TestRailId:C3058 @AdvancedPaymentAllocation
Scenario: Verify that Loan re-aging transaction - chargeback after maturity date and prior to re-aging with charge N+1 installment
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 January 2024 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "1000" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "1000" EUR transaction amount
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 2 | 15 | 16 January 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 31 January 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
When Admin sets the business date to "01 January 2024"
And Customer makes "AUTOPAY" repayment on "01 January 2024" with 250 EUR transaction amount
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 31 January 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
| 01 January 2024 | Repayment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
When Admin sets the business date to "26 February 2024"
When Admin makes "REPAYMENT_ADJUSTMENT_CHARGEBACK" chargeback with 125 EUR transaction amount
When Admin adds "LOAN_NSF_FEE" due date charge with "26 February 2024" due date and 20 EUR transaction amount
Then Loan Repayment schedule has 5 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 31 January 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 5 | 11 | 26 February 2024 | | 0.0 | 125.0 | 0.0 | 0.0 | 20.0 | 145.0 | 0.0 | 0.0 | 0.0 | 145.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
| 01 January 2024 | Repayment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
| 26 February 2024 | Chargeback | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 875.0 | false |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1125.0 | 0.0 | 0.0 | 20.0 | 1145.0 | 250.0| 0.0 | 0.0 | 895.0 |
When Admin sets the business date to "27 February 2024"
When Admin makes a charge adjustment for the last "LOAN_NSF_FEE" type charge which is due on "26 February 2024" with 20 EUR transaction amount and externalId ""
When Admin creates a Loan re-aging transaction by Loan external ID with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 2 | MONTHS | 10 March 2024 | 3 |
Then Loan Repayment schedule has 8 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | 27 February 2024| 730.0 | 20.0 | 0.0 | 0.0 | 0.0 | 20.0 | 20.0 | 0.0 | 20.0 | 0.0 |
| 3 | 15 | 31 January 2024 | 27 February 2024| 730.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 15 | 15 February 2024 | 27 February 2024| 730.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 5 | 11 | 26 February 2024 | | 855.0 | 0.0 | 0.0 | 0.0 | 20.0 | 20.0 | 0.0 | 0.0 | 0.0 | 20.0 |
| 6 | 13 | 10 March 2024 | | 570.0 | 285.0 | 0.0 | 0.0 | 0.0 | 285.0 | 0.0 | 0.0 | 0.0 | 285.0 |
| 7 | 61 | 10 May 2024 | | 285.0 | 285.0 | 0.0 | 0.0 | 0.0 | 285.0 | 0.0 | 0.0 | 0.0 | 285.0 |
| 8 | 61 | 10 July 2024 | | 0.0 | 285.0 | 0.0 | 0.0 | 0.0 | 285.0 | 0.0 | 0.0 | 0.0 | 285.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
| 01 January 2024 | Repayment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
| 26 February 2024 | Chargeback | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 875.0 | false |
| 27 February 2024 | Charge Adjustment| 20.0 | 20.0 | 0.0 | 0.0 | 0.0 | 855.0 | false |
| 27 February 2024 | Re-age | 855.0 | 855.0 | 0.0 | 0.0 | 0.0 | 0.0 | false |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1125.0 | 0.0 | 0.0 | 20.0 | 1145.0 | 270.0| 0.0 | 20.0 | 875.0 |
@TestRailId:C3059 @AdvancedPaymentAllocation
Scenario: Verify Loan re-aging transaction - partial principal payment scenario + undo re-ageing
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 500 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "500" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "500" EUR transaction amount
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | | 250.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
| 3 | 15 | 31 January 2024 | | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
When Admin sets the business date to "16 January 2024"
And Customer makes "AUTOPAY" repayment on "16 January 2024" with 50 EUR transaction amount
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | | 250.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 50.0 | 0.0 | 0.0 | 75.0 |
| 3 | 15 | 31 January 2024 | | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 | false |
| 01 January 2024 | Down Payment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 375.0 | false |
| 16 January 2024 | Repayment | 50.0 | 50.0 | 0.0 | 0.0 | 0.0 | 325.0 | false |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 175.0 | 0.0 | 0.0 | 325.0 |
When Admin sets the business date to "27 February 2024"
When Admin creates a Loan re-aging transaction with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 1 | MONTHS | 01 March 2024 | 6 |
Then Loan Repayment schedule has 10 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | 27 February 2024| 325.0 | 50.0 | 0.0 | 0.0 | 0.0 | 50.0 | 50.0 | 0.0 | 0.0 | 0.0 |
| 3 | 15 | 31 January 2024 | 27 February 2024| 325.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 15 | 15 February 2024 | 27 February 2024| 325.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 5 | 15 | 01 March 2024 | | 271.0 | 54.0 | 0.0 | 0.0 | 0.0 | 54.0 | 0.0 | 0.0 | 0.0 | 54.0 |
| 6 | 31 | 01 April 2024 | | 217.0 | 54.0 | 0.0 | 0.0 | 0.0 | 54.0 | 0.0 | 0.0 | 0.0 | 54.0 |
| 7 | 30 | 01 May 2024 | | 163.0 | 54.0 | 0.0 | 0.0 | 0.0 | 54.0 | 0.0 | 0.0 | 0.0 | 54.0 |
| 8 | 31 | 01 June 2024 | | 109.0 | 54.0 | 0.0 | 0.0 | 0.0 | 54.0 | 0.0 | 0.0 | 0.0 | 54.0 |
| 9 | 30 | 01 July 2024 | | 55.0 | 54.0 | 0.0 | 0.0 | 0.0 | 54.0 | 0.0 | 0.0 | 0.0 | 54.0 |
| 10 | 31 | 01 August 2024 | | 0.0 | 55.0 | 0.0 | 0.0 | 0.0 | 55.0 | 0.0 | 0.0 | 0.0 | 55.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 | false |
| 01 January 2024 | Down Payment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 375.0 | false |
| 16 January 2024 | Repayment | 50.0 | 50.0 | 0.0 | 0.0 | 0.0 | 325.0 | false |
| 27 February 2024 | Re-age | 325.0 | 325.0 | 0.0 | 0.0 | 0.0 | 0.0 | false |
When Admin sets the business date to "29 February 2024"
When Admin successfully undo Loan re-aging transaction
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | | 250.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 50.0 | 0.0 | 0.0 | 75.0 |
| 3 | 15 | 31 January 2024 | | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 | false |
| 01 January 2024 | Down Payment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 375.0 | false |
| 16 January 2024 | Repayment | 50.0 | 50.0 | 0.0 | 0.0 | 0.0 | 325.0 | false |
| 27 February 2024 | Re-age | 325.0 | 325.0 | 0.0 | 0.0 | 0.0 | 0.0 | true |
@TestRailId:C3091 @AdvancedPaymentAllocation
Scenario: Verify Loan re-age transaction - Event check
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 500 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "500" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "500" EUR transaction amount
When Admin sets the business date to "27 February 2024"
When Admin runs inline COB job for Loan
Then LoanDelinquencyRangeChangeBusinessEvent is created
When Admin creates a Loan re-aging transaction with the following data:
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
| 1 | MONTHS | 01 March 2024 | 6 |
Then LoanDelinquencyRangeChangeBusinessEvent is created
Then LoanReAgeBusinessEvent is created
@@ -0,0 +1,377 @@
@LoanReAmortizationFeature
Feature: LoanReAmortization
@TestRailId:C3069 @AdvancedPaymentAllocation
Scenario: Verify Loan re-amortization transaction - re-amortization happy path
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 500 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "500" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "500" EUR transaction amount
When Admin sets the business date to "25 January 2024"
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | | 250.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
| 3 | 15 | 31 January 2024 | | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 125.0 | 0.0 | 0.0 | 375.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 January 2024 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 |
| 01 January 2024 | Down Payment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 375.0 |
When When Admin creates a Loan re-amortization transaction on current business date
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | 25 January 2024 | 375.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 15 | 31 January 2024 | | 187.0 | 188.0 | 0.0 | 0.0 | 0.0 | 188.0 | 0.0 | 0.0 | 0.0 | 188.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 187.0 | 0.0 | 0.0 | 0.0 | 187.0 | 0.0 | 0.0 | 0.0 | 187.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 125.0 | 0.0 | 0.0 | 375.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 January 2024 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 |
| 01 January 2024 | Down Payment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 375.0 |
| 25 January 2024 | Re-amortize | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 0.0 |
@TestRailId:C3070 @AdvancedPaymentAllocation
Scenario: Verify Loan re-amortization transaction - re-amortization happy path with loan externalId
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 500 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "500" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "500" EUR transaction amount
When Admin sets the business date to "25 January 2024"
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | | 250.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
| 3 | 15 | 31 January 2024 | | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 125.0 | 0.0 | 0.0 | 375.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 January 2024 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 |
| 01 January 2024 | Down Payment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 375.0 |
When When Admin creates a Loan re-amortization transaction on current business date by loan external ID
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | 25 January 2024 | 375.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 15 | 31 January 2024 | | 187.0 | 188.0 | 0.0 | 0.0 | 0.0 | 188.0 | 0.0 | 0.0 | 0.0 | 188.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 187.0 | 0.0 | 0.0 | 0.0 | 187.0 | 0.0 | 0.0 | 0.0 | 187.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 125.0 | 0.0 | 0.0 | 375.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 January 2024 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 |
| 01 January 2024 | Down Payment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 375.0 |
| 25 January 2024 | Re-amortize | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 0.0 |
@TestRailId:C3071 @AdvancedPaymentAllocation
Scenario: Verify Loan re-amortization transaction - re-amortization undo happy path
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 500 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "500" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "500" EUR transaction amount
When Admin sets the business date to "25 January 2024"
When When Admin creates a Loan re-amortization transaction on current business date
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | 25 January 2024 | 375.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 15 | 31 January 2024 | | 187.0 | 188.0 | 0.0 | 0.0 | 0.0 | 188.0 | 0.0 | 0.0 | 0.0 | 188.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 187.0 | 0.0 | 0.0 | 0.0 | 187.0 | 0.0 | 0.0 | 0.0 | 187.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 125.0 | 0.0 | 0.0 | 375.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 | false |
| 01 January 2024 | Down Payment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 375.0 | false |
| 25 January 2024 | Re-amortize | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 0.0 | false |
When Admin sets the business date to "26 January 2024"
When When Admin undo Loan re-amortization transaction on current business date
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | | 250.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
| 3 | 15 | 31 January 2024 | | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 125.0 | 0.0 | 0.0 | 375.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 | false |
| 01 January 2024 | Down Payment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 375.0 | false |
| 25 January 2024 | Re-amortize | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 0.0 | true |
Then Admin checks that delinquency range is: "RANGE_3" and has delinquentDate "2024-01-19"
@TestRailId:C3072 @AdvancedPaymentAllocation
Scenario: Verify Loan re-amortization transaction - delinquency calculation triggered upon re-amortization transaction
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 500 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "500" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "500" EUR transaction amount
When Admin sets the business date to "24 January 2024"
When Admin runs inline COB job for Loan
Then Admin checks that delinquency range is: "RANGE_3" and has delinquentDate "2024-01-19"
When Admin sets the business date to "25 January 2024"
When When Admin creates a Loan re-amortization transaction on current business date
Then Admin checks that delinquency range is: "NO_DELINQUENCY" and has delinquentDate ""
@TestRailId:C3073 @AdvancedPaymentAllocation
Scenario: Verify Loan re-amortization transaction - UC1: re-amortization after charge applied on loan
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 500 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "500" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "500" EUR transaction amount
When Admin sets the business date to "16 January 2024"
When Admin adds "LOAN_NSF_FEE" due date charge with "16 January 2024" due date and 10 EUR transaction amount
When Admin sets the business date to "25 January 2024"
When When Admin creates a Loan re-amortization transaction on current business date
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | | 375.0 | 0.0 | 0.0 | 0.0 | 10.0 | 10.0 | 0.0 | 0.0 | 0.0 | 10.0 |
| 3 | 15 | 31 January 2024 | | 187.0 | 188.0 | 0.0 | 0.0 | 0.0 | 188.0 | 0.0 | 0.0 | 0.0 | 188.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 187.0 | 0.0 | 0.0 | 0.0 | 187.0 | 0.0 | 0.0 | 0.0 | 187.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 500.0 | 0.0 | 0.0 | 10.0 | 510.0 | 125.0 | 0.0 | 0.0 | 385.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 January 2024 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 |
| 01 January 2024 | Down Payment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 375.0 |
| 25 January 2024 | Re-amortize | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 0.0 |
@TestRailId:C3074 @AdvancedPaymentAllocation
Scenario: Verify Loan re-amortization transaction - UC2: complete past due principal amount reamortization scenario
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 500 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "500" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "500" EUR transaction amount
When Admin sets the business date to "01 February 2024"
When When Admin creates a Loan re-amortization transaction on current business date
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | 01 February 2024 | 375.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 15 | 31 January 2024 | 01 February 2024 | 375.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 375.0 | 0.0 | 0.0 | 0.0 | 375.0 | 0.0 | 0.0 | 0.0 | 375.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 125.0 | 0.0 | 0.0 | 375.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 January 2024 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 |
| 01 January 2024 | Down Payment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 375.0 |
| 01 February 2024 | Re-amortize | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 0.0 |
@TestRailId:C3075 @AdvancedPaymentAllocation
Scenario: Verify Loan re-amortization transaction - UC3: reverse replay scenario
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 500 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "500" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "500" EUR transaction amount
When Admin sets the business date to "01 February 2024"
When When Admin creates a Loan re-amortization transaction on current business date
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | 01 February 2024 | 375.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 15 | 31 January 2024 | 01 February 2024 | 375.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 375.0 | 0.0 | 0.0 | 0.0 | 375.0 | 0.0 | 0.0 | 0.0 | 375.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 125.0 | 0.0 | 0.0 | 375.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 January 2024 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 |
| 01 January 2024 | Down Payment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 375.0 |
| 01 February 2024 | Re-amortize | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 0.0 |
When Admin sets the business date to "02 February 2024"
And Customer makes "AUTOPAY" repayment on "15 January 2024" with 125 EUR transaction amount
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | 15 January 2024 | 250.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 125.0 | 0.0 | 0.0 |
| 3 | 15 | 31 January 2024 | 01 February 2024 | 250.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 250.0 | 125.0 | 0.0 | 250.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 January 2024 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 |
| 01 January 2024 | Down Payment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 375.0 |
| 15 January 2024 | Repayment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 01 February 2024 | Re-amortize | 375.0 | 375.0 | 0.0 | 0.0 | 0.0 | 0.0 |
@TestRailId:C3076 @AdvancedPaymentAllocation
Scenario: Verify Loan re-amortization transaction - UC4: N+1 Installment scenario
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 500 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "500" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "500" EUR transaction amount
When Admin sets the business date to "01 February 2024"
When Admin adds "LOAN_NSF_FEE" due date charge with "27 February 2024" due date and 10 EUR transaction amount
When When Admin creates a Loan re-amortization transaction on current business date
Then Loan Repayment schedule has 5 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | 01 February 2024 | 375.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 15 | 31 January 2024 | 01 February 2024 | 375.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 375.0 | 0.0 | 0.0 | 0.0 | 375.0 | 0.0 | 0.0 | 0.0 | 375.0 |
| 5 | 12 | 27 February 2024 | | 0.0 | 0.0 | 0.0 | 0.0 | 10.0 | 10.0 | 0.0 | 0.0 | 0.0 | 10.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 500.0 | 0.0 | 0.0 | 10.0 | 510.0 | 125.0 | 0.0 | 0.0 | 385.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 January 2024 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 |
| 01 January 2024 | Down Payment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 375.0 |
| 01 February 2024 | Re-amortize | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 0.0 |
@TestRailId:C3077 @AdvancedPaymentAllocation
Scenario: Verify Loan re-amortization transaction - UC5: re-amortization on same day of installment
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 500 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "500" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "500" EUR transaction amount
When Admin sets the business date to "16 January 2024"
When Admin adds "LOAN_NSF_FEE" due date charge with "16 January 2024" due date and 10 EUR transaction amount
When Admin sets the business date to "31 January 2024"
When When Admin creates a Loan re-amortization transaction on current business date
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | | 375.0 | 0.0 | 0.0 | 0.0 | 10.0 | 10.0 | 0.0 | 0.0 | 0.0 | 10.0 |
| 3 | 15 | 31 January 2024 | 31 January 2024 | 375.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 375.0 | 0.0 | 0.0 | 0.0 | 375.0 | 0.0 | 0.0 | 0.0 | 375.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 500.0 | 0.0 | 0.0 | 10.0 | 510.0 | 125.0 | 0.0 | 0.0 | 385.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 January 2024 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 |
| 01 January 2024 | Down Payment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 375.0 |
| 31 January 2024 | Re-amortize | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 0.0 |
@TestRailId:C3078 @AdvancedPaymentAllocation
Scenario: Verify Loan re-amortization transaction - UC6: Parital Paid Scenario
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 500 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "500" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "500" EUR transaction amount
When Admin sets the business date to "17 January 2024"
And Customer makes "AUTOPAY" repayment on "17 January 2024" with 50 EUR transaction amount
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | | 250.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 50.0 | 0.0 | 50.0 | 75.0 |
| 3 | 15 | 31 January 2024 | | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 175.0 | 0.0 | 50.0 | 325.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 January 2024 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 |
| 01 January 2024 | Down Payment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 375.0 |
| 17 January 2024 | Repayment | 50.0 | 50.0 | 0.0 | 0.0 | 0.0 | 325.0 |
When Admin sets the business date to "30 January 2024"
When When Admin creates a Loan re-amortization transaction on current business date by loan external ID
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 January 2024 | 01 January 2024 | 375.0 | 125.0 | 0.0 | 0.0 | 0.0 | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 January 2024 | 30 January 2024 | 325.0 | 50.0 | 0.0 | 0.0 | 0.0 | 50.0 | 50.0 | 0.0 | 50.0 | 0.0 |
| 3 | 15 | 31 January 2024 | | 162.0 | 163.0 | 0.0 | 0.0 | 0.0 | 163.0 | 0.0 | 0.0 | 0.0 | 163.0 |
| 4 | 15 | 15 February 2024 | | 0.0 | 162.0 | 0.0 | 0.0 | 0.0 | 162.0 | 0.0 | 0.0 | 0.0 | 162.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 175.0 | 0.0 | 50.0 | 325.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 January 2024 | Disbursement | 500.0 | 0.0 | 0.0 | 0.0 | 0.0 | 500.0 |
| 01 January 2024 | Down Payment | 125.0 | 125.0 | 0.0 | 0.0 | 0.0 | 375.0 |
| 17 January 2024 | Repayment | 50.0 | 50.0 | 0.0 | 0.0 | 0.0 | 325.0 |
| 30 January 2024 | Re-amortize | 75.0 | 75.0 | 0.0 | 0.0 | 0.0 | 0.0 |
@TestRailId:C3089 @AdvancedPaymentAllocation
Scenario: Verify Loan re-amortization transaction - Event check
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 500 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "500" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "500" EUR transaction amount
When Admin sets the business date to "31 January 2024"
When Admin runs inline COB job for Loan
Then LoanDelinquencyRangeChangeBusinessEvent is created
When Admin sets the business date to "01 February 2024"
When When Admin creates a Loan re-amortization transaction on current business date
Then LoanDelinquencyRangeChangeBusinessEvent is created
Then LoanReAmortizeBusinessEvent is created
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,681 @@
@LoanRescheduleFeature
Feature: LoanReschedule
@TestRailId:C2680
Scenario: As a user I would like to see a loan changed event was triggered when the loan got rescheduled
When Admin sets the business date to "1 July 2022"
When Admin creates a client with random data
And Admin successfully creates a new customised Loan submitted on date: "1 July 2022", with Principal: "5000", a loanTermFrequency: 1 months, and numberOfRepayments: 1
And Admin successfully approves the loan on "1 July 2022" with "5000" amount and expected disbursement date on "1 July 2022"
When Admin successfully disburse the loan on "1 July 2022" with "5000" EUR transaction amount
When Batch API call with steps: rescheduleLoan from "1 August 2022" to "31 August 2022" submitted on date: "1 July 2022", approveReschedule on date: "1 July 2022" runs with enclosingTransaction: "true"
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2022 | | 5000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 61 | 31 August 2022 | | 0.0 | 5000.0 | 0.0 | 0.0 | 0.0 | 5000.0 | 0.0 | 0.0 | 0.0 | 5000.0 |
@TestRailId:C2801
Scenario: Verify that loan is rescheduled properly in case of: undo disbursement
When Admin sets the business date to "01 July 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 July 2023"
And Admin successfully approves the loan on "01 July 2023" with "1000" amount and expected disbursement date on "01 July 2023"
And Admin successfully disburse the loan on "01 July 2023" with "1000" EUR transaction amount
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 30 | 31 July 2023 | | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000 | 0 | 0 | 0 | 1000 | 0 | 0 | 0 | 1000 |
When Admin sets the business date to "02 July 2023"
When Admin successfully undo disbursal
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 1000.0 | | | 0.0 | | 0.0 | | | | 0.0 |
| 1 | 30 | 31 July 2023 | | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000 | 0 | 0 | 0 | 1000 | 0 | 0 | 0 | 1000 |
@TestRailId:C2802
Scenario: Verify that loan is rescheduled properly in case of: multiple disbursement, second disbursement placed within payment period
When Admin sets the business date to "01 July 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 July 2023"
And Admin successfully approves the loan on "01 July 2023" with "1000" amount and expected disbursement date on "01 July 2023"
And Admin successfully disburse the loan on "01 July 2023" with "500" EUR transaction amount
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 30 | 31 July 2023 | | 0.0 | 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 0.0 | 0.0 | 0.0 | 500.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 500 | 0 | 0 | 0 | 500 | 0 | 0 | 0 | 500 |
When Admin sets the business date to "10 July 2023"
And Admin successfully disburse the loan on "10 July 2023" with "500" EUR transaction amount
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| | | 10 July 2023 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 30 | 31 July 2023 | | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000 | 0 | 0 | 0 | 1000 | 0 | 0 | 0 | 1000 |
@TestRailId:C2803
Scenario: Verify that loan is rescheduled properly in case of: multiple disbursement, second disbursement placed after payment period
When Admin sets the business date to "01 July 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 July 2023"
And Admin successfully approves the loan on "01 July 2023" with "1000" amount and expected disbursement date on "01 July 2023"
And Admin successfully disburse the loan on "01 July 2023" with "500" EUR transaction amount
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 30 | 31 July 2023 | | 0.0 | 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 0.0 | 0.0 | 0.0 | 500.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 500 | 0 | 0 | 0 | 500 | 0 | 0 | 0 | 500 |
When Admin sets the business date to "10 August 2023"
And Admin successfully disburse the loan on "10 August 2023" with "500" EUR transaction amount
Then Loan Repayment schedule has 2 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 30 | 31 July 2023 | | 0.0 | 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 0.0 | 0.0 | 0.0 | 500.0 |
| | | 10 August 2023 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 2 | 30 | 30 August 2023 | | 0.0 | 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 0.0 | 0.0 | 0.0 | 500.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000 | 0 | 0 | 0 | 1000 | 0 | 0 | 0 | 1000 |
When Admin successfully undo last disbursal
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 500.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 30 | 31 July 2023 | | 0.0 | 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 0.0 | 0.0 | 0.0 | 500.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 500 | 0 | 0 | 0 | 500 | 0 | 0 | 0 | 500 |
@TestRailId:C2804
Scenario: Verify that loan is rescheduled properly in case of: single installment, changing installment date
When Admin sets the business date to "01 July 2023"
When Admin creates a client with random data
When Admin creates a new default Loan with date: "01 July 2023"
And Admin successfully approves the loan on "01 July 2023" with "1000" amount and expected disbursement date on "01 July 2023"
And Admin successfully disburse the loan on "01 July 2023" with "1000" EUR transaction amount
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 30 | 31 July 2023 | | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000 | 0 | 0 | 0 | 1000 | 0 | 0 | 0 | 1000 |
When Admin sets the business date to "05 July 2023"
When Admin creates and approves Loan reschedule with the following data:
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
| 31 July 2023 | 05 July 2023 | 31 August 2023 | 0 | 0 | 0 | 0 |
Then Loan Repayment schedule has 1 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 61 | 31 August 2023 | | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000 | 0 | 0 | 0 | 1000 | 0 | 0 | 0 | 1000 |
@TestRailId:C2805
Scenario: Verify that loan is rescheduled properly in case of: multiple installments, changing installment date
When Admin sets the business date to "01 July 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN30 | 01 July 2023 | 3000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER |
And Admin successfully approves the loan on "01 July 2023" with "3000" amount and expected disbursement date on "01 July 2023"
And Admin successfully disburse the loan on "01 July 2023" with "3000" EUR transaction amount
Then Loan Repayment schedule has 3 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 3000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 August 2023 | | 2000.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 2 | 31 | 01 September 2023 | | 1000.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 3 | 30 | 01 October 2023 | | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 3000 | 0 | 0 | 0 | 3000 | 0 | 0 | 0 | 3000 |
When Admin sets the business date to "05 July 2023"
When Admin creates and approves Loan reschedule with the following data:
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
| 01 September 2023 | 05 July 2023 | 15 September 2023 | 0 | 0 | 0 | 0 |
Then Loan Repayment schedule has 3 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 3000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 August 2023 | | 2000.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 2 | 45 | 15 September 2023 | | 1000.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 3 | 30 | 15 October 2023 | | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 3000 | 0 | 0 | 0 | 3000 | 0 | 0 | 0 | 3000 |
@TestRailId:C2806
Scenario: Verify that loan is rescheduled properly in case of: multiple installments, extending repayment schedule
When Admin sets the business date to "01 July 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN30 | 01 July 2023 | 3000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER |
And Admin successfully approves the loan on "01 July 2023" with "3000" amount and expected disbursement date on "01 July 2023"
And Admin successfully disburse the loan on "01 July 2023" with "3000" EUR transaction amount
Then Loan Repayment schedule has 3 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 3000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 August 2023 | | 2000.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 2 | 31 | 01 September 2023 | | 1000.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 3 | 30 | 01 October 2023 | | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 3000 | 0 | 0 | 0 | 3000 | 0 | 0 | 0 | 3000 |
When Admin sets the business date to "05 July 2023"
When Admin creates and approves Loan reschedule with the following data:
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
| 01 September 2023 | 05 July 2023 | | 0 | 0 | 2 | 0 |
Then Loan Repayment schedule has 5 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 3000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 August 2023 | | 2000.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 2 | 31 | 01 September 2023 | | 1500.0 | 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 0.0 | 0.0 | 0.0 | 500.0 |
| 3 | 30 | 01 October 2023 | | 1000.0 | 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 0.0 | 0.0 | 0.0 | 500.0 |
| 4 | 31 | 01 November 2023 | | 500.0 | 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 0.0 | 0.0 | 0.0 | 500.0 |
| 5 | 30 | 01 December 2023 | | 0.0 | 500.0 | 0.0 | 0.0 | 0.0 | 500.0 | 0.0 | 0.0 | 0.0 | 500.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 3000 | 0 | 0 | 0 | 3000 | 0 | 0 | 0 | 3000 |
@TestRailId:C2807
Scenario: Verify that loan is rescheduled properly in case of: multiple installments, mid-term grace period on principal
When Admin sets the business date to "01 July 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN30 | 01 July 2023 | 3000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER |
And Admin successfully approves the loan on "01 July 2023" with "3000" amount and expected disbursement date on "01 July 2023"
And Admin successfully disburse the loan on "01 July 2023" with "3000" EUR transaction amount
Then Loan Repayment schedule has 3 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 3000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 August 2023 | | 2000.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 2 | 31 | 01 September 2023 | | 1000.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 3 | 30 | 01 October 2023 | | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 3000 | 0 | 0 | 0 | 3000 | 0 | 0 | 0 | 3000 |
When Admin sets the business date to "05 July 2023"
When Admin creates and approves Loan reschedule with the following data:
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
| 01 September 2023 | 05 July 2023 | | 1 | 0 | 0 | 0 |
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 3000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 August 2023 | | 2000.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 2 | 31 | 01 September 2023 | 01 July 2023 | 2000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 30 | 01 October 2023 | | 1000.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
| 4 | 31 | 01 November 2023 | | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 3000 | 0 | 0 | 0 | 3000 | 0 | 0 | 0 | 3000 |
# TODO check and fix after Loan reschedule with graceOnInterest deletes all future interest portions when called from API was fixed (periods back to 3 or 5)
@Skip @TestRailId:C2808
Scenario: Verify that loan is rescheduled properly in case of: multiple installments, mid-term grace period on interest
When Admin sets the business date to "01 July 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN30_INTEREST_FLAT | 01 July 2023 | 3000 | 12 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 10 | MONTHS | 1 | MONTHS | 10 | 0 | 0 | 0 | PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER |
And Admin successfully approves the loan on "01 July 2023" with "3000" amount and expected disbursement date on "01 July 2023"
And Admin successfully disburse the loan on "01 July 2023" with "3000" EUR transaction amount
# Then Loan Repayment schedule has 3 periods, with the following data for periods:
# | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
# | | | 01 July 2023 | | 3000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
# | 1 | 31 | 01 August 2023 | | 2000.0 | 1000.0 | 30.0 | 0.0 | 0.0 | 1030.0 | 0.0 | 0.0 | 0.0 | 1030.0 |
# | 2 | 31 | 01 September 2023 | | 1000.0 | 1000.0 | 30.0 | 0.0 | 0.0 | 1030.0 | 0.0 | 0.0 | 0.0 | 1030.0 |
# | 3 | 30 | 01 October 2023 | | 0.0 | 1000.0 | 30.0 | 0.0 | 0.0 | 1030.0 | 0.0 | 0.0 | 0.0 | 1030.0 |
# Then Loan Repayment schedule has the following data in Total row:
# | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
# | 3000 | 90 | 0 | 0 | 3090 | 0 | 0 | 0 | 3090 |
When Admin sets the business date to "05 July 2023"
When Admin creates and approves Loan reschedule with the following data:
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
| 01 September 2023 | 05 July 2023 | | 0 | 2 | 0 | 0 |
# Then Loan Repayment schedule has 3 periods, with the following data for periods:
# | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
# | | | 01 July 2023 | | 3000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
# | 1 | 31 | 01 August 2023 | | 2000.0 | 1000.0 | 30.0 | 0.0 | 0.0 | 1030.0 | 0.0 | 0.0 | 0.0 | 1030.0 |
# | 2 | 31 | 01 September 2023 | | 1000.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
# | 3 | 30 | 01 October 2023 | | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
# Then Loan Repayment schedule has the following data in Total row:
# | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
# | 3000 | 30 | 0 | 0 | 3030 | 0 | 0 | 0 | 3030 |
@TestRailId:C2809
Scenario: Verify that loan is rescheduled properly in case of: multiple installments, new interest rate
When Admin sets the business date to "01 July 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN30_INTEREST_FLAT | 01 July 2023 | 3000 | 12 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER |
And Admin successfully approves the loan on "01 July 2023" with "3000" amount and expected disbursement date on "01 July 2023"
And Admin successfully disburse the loan on "01 July 2023" with "3000" EUR transaction amount
Then Loan Repayment schedule has 3 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 3000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 August 2023 | | 2000.0 | 1000.0 | 30.0 | 0.0 | 0.0 | 1030.0 | 0.0 | 0.0 | 0.0 | 1030.0 |
| 2 | 31 | 01 September 2023 | | 1000.0 | 1000.0 | 30.0 | 0.0 | 0.0 | 1030.0 | 0.0 | 0.0 | 0.0 | 1030.0 |
| 3 | 30 | 01 October 2023 | | 0.0 | 1000.0 | 30.0 | 0.0 | 0.0 | 1030.0 | 0.0 | 0.0 | 0.0 | 1030.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 3000 | 90 | 0 | 0 | 3090 | 0 | 0 | 0 | 3090 |
When Admin sets the business date to "05 July 2023"
When Admin creates and approves Loan reschedule with the following data:
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
| 01 September 2023 | 05 July 2023 | | 0 | 0 | 0 | 6 |
Then Loan Repayment schedule has 3 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 July 2023 | | 3000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 August 2023 | | 2000.0 | 1000.0 | 30.0 | 0.0 | 0.0 | 1030.0 | 0.0 | 0.0 | 0.0 | 1030.0 |
| 2 | 31 | 01 September 2023 | | 1000.0 | 1000.0 | 15.0 | 0.0 | 0.0 | 1015.0 | 0.0 | 0.0 | 0.0 | 1015.0 |
| 3 | 30 | 01 October 2023 | | 0.0 | 1000.0 | 15.0 | 0.0 | 0.0 | 1015.0 | 0.0 | 0.0 | 0.0 | 1015.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 3000 | 60 | 0 | 0 | 3060 | 0 | 0 | 0 | 3060 |
@TestRailId:C2996
Scenario: Verify that reschedule: add extra terms working properly with auto downpayment
When Admin sets the business date to "01 October 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO | 01 October 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE |
And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
When Admin creates and approves Loan reschedule with the following data:
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
| 16 October 2023 | 01 October 2023 | | 0 | 0 | 2 | 0 |
Then Loan Repayment schedule has 6 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 October 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 October 2023 | 01 October 2023 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 October 2023 | | 600.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 |
| 3 | 15 | 31 October 2023 | | 450.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 |
| 4 | 15 | 15 November 2023 | | 300.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 |
| 5 | 15 | 30 November 2023 | | 150.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 |
| 6 | 15 | 15 December 2023 | | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 250.0 | 0.0 | 0.0 | 750.0 |
@TestRailId:C2997
Scenario: Verify that reschedule: add extra terms working properly with auto downpayment and 2nd disbursement before reschedule
When Admin sets the business date to "01 October 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO | 01 October 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE |
And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
When Admin sets the business date to "10 October 2023"
When Admin successfully disburse the loan on "10 October 2023" with "400" EUR transaction amount
When Admin sets the business date to "11 October 2023"
When Admin creates and approves Loan reschedule with the following data:
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
| 16 October 2023 | 01 October 2023 | | 0 | 0 | 2 | 0 |
Then Loan Repayment schedule has 7 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 October 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 October 2023 | 01 October 2023 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| | | 10 October 2023 | | 400.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 2 | 0 | 10 October 2023 | 10 October 2023 | 1050.0 | 100.0 | 0.0 | 0.0 | 0.0 | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 |
| 3 | 15 | 16 October 2023 | | 840.0 | 210.0 | 0.0 | 0.0 | 0.0 | 210.0 | 0.0 | 0.0 | 0.0 | 210.0 |
| 4 | 15 | 31 October 2023 | | 630.0 | 210.0 | 0.0 | 0.0 | 0.0 | 210.0 | 0.0 | 0.0 | 0.0 | 210.0 |
| 5 | 15 | 15 November 2023 | | 420.0 | 210.0 | 0.0 | 0.0 | 0.0 | 210.0 | 0.0 | 0.0 | 0.0 | 210.0 |
| 6 | 15 | 30 November 2023 | | 210.0 | 210.0 | 0.0 | 0.0 | 0.0 | 210.0 | 0.0 | 0.0 | 0.0 | 210.0 |
| 7 | 15 | 15 December 2023 | | 0.0 | 210.0 | 0.0 | 0.0 | 0.0 | 210.0 | 0.0 | 0.0 | 0.0 | 210.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1400.0 | 0.0 | 0.0 | 0.0 | 1400.0 | 350.0 | 0.0 | 0.0 | 1050.0 |
# TODO remove Skip when Reschedule does not work properly in case of PIN4_DOWNPAYMENT_AUTO and with 2nd disbursement after reschedule and first installment is fixed
@Skip
@TestRailId:C2998
Scenario: Verify that reschedule: add extra terms working properly with auto downpayment and 2nd disbursement after reschedule and first installment
When Admin sets the business date to "01 October 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO | 01 October 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE |
And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
When Admin sets the business date to "11 October 2023"
When Admin creates and approves Loan reschedule with the following data:
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
| 16 October 2023 | 01 October 2023 | | 0 | 0 | 2 | 0 |
When Admin sets the business date to "20 October 2023"
When Admin successfully disburse the loan on "20 October 2023" with "400" EUR transaction amount
Then Loan Repayment schedule has 7 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 October 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 October 2023 | 01 October 2023 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 15 | 16 October 2023 | | 600.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 | 100.0 | 0.0 | 100.0 | 50.0 |
| | | 20 October 2023 | | 400.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 3 | 0 | 20 October 2023 | | 900.0 | 100.0 | 0.0 | 0.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | 100.0 |
| 4 | 15 | 31 October 2023 | | 675.0 | 225.0 | 0.0 | 0.0 | 0.0 | 225.0 | 0.0 | 0.0 | 0.0 | 225.0 |
| 5 | 15 | 15 November 2023 | | 450.0 | 225.0 | 0.0 | 0.0 | 0.0 | 225.0 | 0.0 | 0.0 | 0.0 | 225.0 |
| 6 | 15 | 30 November 2023 | | 225.0 | 225.0 | 0.0 | 0.0 | 0.0 | 225.0 | 0.0 | 0.0 | 0.0 | 225.0 |
| 7 | 15 | 15 December 2023 | | 0.0 | 225.0 | 0.0 | 0.0 | 0.0 | 225.0 | 0.0 | 0.0 | 0.0 | 225.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1400.0 | 0.0 | 0.0 | 0.0 | 1400.0 | 350.0 | 0.0 | 100.0 | 1050.0 |
@TestRailId:C3022
Scenario: Verify that Payment Holiday (Reschedule with adjustedDueDate) works properly in case of loan schedule adjusted by 1 installment period after successful downpayment
When Admin sets the business date to "01 October 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO | 01 October 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE |
And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
When Admin sets the business date to "05 October 2023"
When Admin creates and approves Loan reschedule with the following data:
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
| 16 October 2023 | 05 October 2023 | 31 October 2023 | 0 | 0 | 0 | 0 |
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 October 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 October 2023 | 01 October 2023 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 30 | 31 October 2023 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 15 November 2023 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 30 November 2023 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 250.0 | 0.0 | 0.0 | 750.0 |
@TestRailId:C3023
Scenario: Verify that Payment Holiday (Reschedule with adjustedDueDate) works properly in case of loan schedule adjusted by 2 installment period after successful downpayment
When Admin sets the business date to "01 October 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO | 01 October 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE |
And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
When Admin sets the business date to "05 October 2023"
When Admin creates and approves Loan reschedule with the following data:
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
| 16 October 2023 | 05 October 2023 | 15 November 2023 | 0 | 0 | 0 | 0 |
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 October 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 October 2023 | 01 October 2023 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 45 | 15 November 2023 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 30 November 2023 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 15 December 2023 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 250.0 | 0.0 | 0.0 | 750.0 |
@TestRailId:C3024
Scenario: Verify that Payment Holiday (Reschedule with adjustedDueDate) works properly in case of loan schedule adjusted by 2 installment period while downpayment is still due
When Admin sets the business date to "01 October 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT | 01 October 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE |
And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
When Admin sets the business date to "05 October 2023"
When Admin creates and approves Loan reschedule with the following data:
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
| 16 October 2023 | 05 October 2023 | 15 November 2023 | 0 | 0 | 0 | 0 |
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 October 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 October 2023 | | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 2 | 45 | 15 November 2023 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 30 November 2023 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 15 December 2023 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
@TestRailId:C3025
Scenario: Verify that Payment Holiday (Reschedule with adjustedDueDate) works properly in case of loan schedule adjusted by 1 installment period after downpayment was reverted, fee added and downpayment paid by autopayment
When Admin sets the business date to "01 October 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT | 01 October 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE |
And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
And Customer makes "AUTOPAY" repayment on "01 October 2023" with 250 EUR transaction amount
When Admin sets the business date to "02 October 2023"
When Customer undo "1"th repayment on "01 October 2023"
When Admin adds "LOAN_NSF_FEE" due date charge with "16 October 2023" due date and 15 EUR transaction amount
When Admin sets the business date to "03 October 2023"
And Customer makes "AUTOPAY" repayment on "03 October 2023" with 250 EUR transaction amount
When Admin sets the business date to "05 October 2023"
When Admin creates and approves Loan reschedule with the following data:
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
| 16 October 2023 | 05 October 2023 | 31 October 2023 | 0 | 0 | 0 | 0 |
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 October 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 October 2023 | 03 October 2023 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 250.0 | 0.0 |
| 2 | 30 | 31 October 2023 | | 500.0 | 250.0 | 0.0 | 0.0 | 15.0 | 265.0 | 0.0 | 0.0 | 0.0 | 265.0 |
| 3 | 15 | 15 November 2023 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 30 November 2023 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000.0 | 0.0 | 0.0 | 15.0 | 1015.0 | 250.0 | 0.0 | 250.0 | 765.0 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 October 2023 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
| 01 October 2023 | Repayment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | true |
| 03 October 2023 | Repayment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
@TestRailId:C3026
Scenario: Verify that Payment Holiday (Reschedule with adjustedDueDate) works properly in case of auto downpayment
When Admin sets the business date to "01 October 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO | 01 October 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE |
And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
When Admin creates and approves Loan reschedule with the following data:
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
| 16 October 2023 | 01 October 2023 | 31 October 2023 | 0 | 0 | 0 | 0 |
Then Loan Repayment schedule has 4 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 October 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 October 2023 | 01 October 2023 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 30 | 31 October 2023 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 15 November 2023 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 30 November 2023 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 250.0 | 0.0 | 0.0 | 750.0 |
@TestRailId:C3027
Scenario: Verify that Payment Holiday (Reschedule with adjustedDueDate) works properly in case of auto downpayment and 2nd disbursement before reschedule
When Admin sets the business date to "01 October 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO | 01 October 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE |
And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
When Admin sets the business date to "10 October 2023"
When Admin successfully disburse the loan on "10 October 2023" with "400" EUR transaction amount
When Admin sets the business date to "11 October 2023"
When Admin creates and approves Loan reschedule with the following data:
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
| 16 October 2023 | 01 October 2023 | 31 October 2023 | 0 | 0 | 0 | 0 |
Then Loan Repayment schedule has 5 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 October 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 October 2023 | 01 October 2023 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| | | 10 October 2023 | | 400.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 2 | 0 | 10 October 2023 | 10 October 2023 | 1050.0 | 100.0 | 0.0 | 0.0 | 0.0 | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 |
| 3 | 30 | 31 October 2023 | | 700.0 | 350.0 | 0.0 | 0.0 | 0.0 | 350.0 | 0.0 | 0.0 | 0.0 | 350.0 |
| 4 | 15 | 15 November 2023 | | 350.0 | 350.0 | 0.0 | 0.0 | 0.0 | 350.0 | 0.0 | 0.0 | 0.0 | 350.0 |
| 5 | 15 | 30 November 2023 | | 0.0 | 350.0 | 0.0 | 0.0 | 0.0 | 350.0 | 0.0 | 0.0 | 0.0 | 350.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1400.0 | 0.0 | 0.0 | 0.0 | 1400.0 | 350.0 | 0.0 | 0.0 | 1050.0 |
@TestRailId:C3028
Scenario: Verify that Payment Holiday (Reschedule with adjustedDueDate) works properly in case of loan schedule adjusted by 1 installment period and 2 extra terms
When Admin sets the business date to "01 October 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO | 01 October 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE |
And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
When Admin sets the business date to "05 October 2023"
When Admin creates and approves Loan reschedule with the following data:
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
| 16 October 2023 | 05 October 2023 | 31 October 2023 | 0 | 0 | 2 | 0 |
Then Loan Repayment schedule has 6 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 October 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 October 2023 | 01 October 2023 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 30 | 31 October 2023 | | 600.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 |
| 3 | 15 | 15 November 2023 | | 450.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 |
| 4 | 15 | 30 November 2023 | | 300.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 |
| 5 | 15 | 15 December 2023 | | 150.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 |
| 6 | 15 | 30 December 2023 | | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 | 0.0 | 0.0 | 0.0 | 150.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 250.0 | 0.0 | 0.0 | 750.0 |
@TestRailId:C3029
Scenario: Verify that Payment Holiday (Reschedule with adjustedDueDate) works properly in case of loan schedule adjusted by 1 installment period and 1 grace on principal
When Admin sets the business date to "01 October 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO | 01 October 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE |
And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
When Admin sets the business date to "05 October 2023"
When Admin creates and approves Loan reschedule with the following data:
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
| 16 October 2023 | 05 October 2023 | 31 October 2023 | 1 | 0 | 0 | 0 |
Then Loan Repayment schedule has 5 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 October 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 October 2023 | 01 October 2023 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
| 2 | 30 | 31 October 2023 | 01 October 2023 | 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 3 | 15 | 15 November 2023 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 30 November 2023 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 5 | 15 | 15 December 2023 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000.0 | 0.0 | 0.0 | 0.0 | 1000.0 | 250.0 | 0.0 | 0.0 | 750.0 |
@TestRailId:C3030
Scenario: Verify that Payment Holiday (Reschedule with adjustedDueDate) gives error in case of charged-off loan
When Admin sets the business date to "01 October 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO | 01 October 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE |
And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
When Admin sets the business date to "03 October 2023"
And Admin does charge-off the loan on "03 October 2023"
When Admin sets the business date to "05 October 2023"
Then Loan reschedule with the following data results a 403 error and "LOAN_CHARGED_OFF" error message
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
| 16 October 2023 | 05 October 2023 | 31 October 2023 | 0 | 0 | 0 | 0 |
@TestRailId:C3033
Scenario: Verify that reschedule keeps the N+1 installment
When Admin sets the business date to "01 October 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT | 01 October 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE |
And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
When Admin adds "LOAN_NSF_FEE" due date charge with "17 December 2023" due date and 20 EUR transaction amount
Then Loan Repayment schedule has 5 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 October 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 October 2023 | | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 2 | 15 | 16 October 2023 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 31 October 2023 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 15 November 2023 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 5 | 32 | 17 December 2023 | | 0.0 | 0.0 | 0.0 | 0.0 | 20.0 | 20.0 | 0.0 | 0.0 | 0.0 | 20.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000.0 | 0.0 | 0.0 | 20.0 | 1020.0 | 0.0 | 0.0 | 0.0 | 1020.0 |
When Admin sets the business date to "05 October 2023"
When Admin creates and approves Loan reschedule with the following data:
| rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate |
| 16 October 2023 | 05 October 2023 | 31 October 2023 | 0 | 0 | 0 | 0 |
Then Loan Repayment schedule has 5 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 October 2023 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 0 | 01 October 2023 | | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 2 | 30 | 31 October 2023 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 3 | 15 | 15 November 2023 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 4 | 15 | 30 November 2023 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
| 5 | 17 | 17 December 2023 | | 0.0 | 0.0 | 0.0 | 0.0 | 20.0 | 20.0 | 0.0 | 0.0 | 0.0 | 20.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000.0 | 0.0 | 0.0 | 20.0 | 1020.0 | 0.0 | 0.0 | 0.0 | 1020.0 |
@TestRailId:C3045 @AdvancedPaymentAllocation
Scenario: Verify that inline COB execution is taking place in case of a BatchAPI request of Loan reschedule creation and approval
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "1000" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "1000" EUR transaction amount
When Admin sets the business date to "02 January 2024"
When Admin runs inline COB job for Loan
When Admin creates new user with "NO_BYPASS_AUTOTEST" username, "NO_BYPASS_AUTOTEST_ROLE" role name and given permissions:
| APPROVE_RESCHEDULELOAN |
| CREATE_RESCHEDULELOAN |
| READ_RESCHEDULELOAN |
| REJECT_RESCHEDULELOAN |
When Admin sets the business date to "10 January 2024"
When Batch API call with created user and with steps: rescheduleLoan from "16 January 2024" to "31 January 2024" submitted on date: "10 January 2024", approveReschedule on date: "10 January 2024" runs with enclosingTransaction: "true"
Then Admin checks that last closed business date of loan is "09 January 2024"
@TestRailId:C3048 @AdvancedPaymentAllocation
Scenario: Verify that in case of Loan is hard locked for COB execution, BatchAPI request of Loan reschedule creation and approval will result a 409 error and a LOAN_LOCKED_BY_COB error message
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 January 2024" with "1000" amount and expected disbursement date on "01 January 2024"
When Admin successfully disburse the loan on "01 January 2024" with "1000" EUR transaction amount
When Admin sets the business date to "02 January 2024"
When Admin runs inline COB job for Loan
When Admin sets the business date to "10 January 2024"
When Admin places a lock on loan account with an error message
When Admin creates new user with "NO_BYPASS_AUTOTEST" username, "NO_BYPASS_AUTOTEST_ROLE" role name and given permissions:
| APPROVE_RESCHEDULELOAN |
| CREATE_RESCHEDULELOAN |
| READ_RESCHEDULELOAN |
| REJECT_RESCHEDULELOAN |
When Batch API call with created user and the following data results a 409 error and a "LOAN_LOCKED_BY_COB" error message:
| rescheduleFromDate | submittedOnDate | adjustedDueDate | approvedOnDate | enclosingTransaction |
| 16 January 2024 | 10 January 2024 | 31 January 2024 | 10 January 2024 | true |
@@ -0,0 +1,67 @@
@WriteOffFeature
Feature: Write-off
@TestRailId:C2934
Scenario: As a user I want to do Write-off a loan and verify that undo repayment post write-off results in error
When Admin sets the business date to "1 January 2023"
And Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "1 January 2023" with "1000" amount and expected disbursement date on "1 January 2023"
And Admin successfully disburse the loan on "1 January 2023" with "1000" EUR transaction amount
When Admin sets the business date to "22 January 2023"
And Customer makes "AUTOPAY" repayment on "22 January 2023" with 100 EUR transaction amount
When Admin sets the business date to "29 January 2023"
And Admin does write-off the loan on "29 January 2023"
Then Loan status will be "CLOSED_WRITTEN_OFF"
Then Loan Transactions tab has a transaction with date: "29 January 2023", and with the following data:
| Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| Close (as written-off) | 650.0 | 650.0 | 0.0 | 0.0 | 0.0 | 0.0 |
Then Admin fails to undo "1"th transaction made on "22 January 2023"
@TestRailId:C2935
Scenario: As a user I want to do Write-off a loan and verify that backdate repayment post write-off results in error
When Admin sets the business date to "1 January 2023"
And Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "1 January 2023" with "1000" amount and expected disbursement date on "1 January 2023"
And Admin successfully disburse the loan on "1 January 2023" with "1000" EUR transaction amount
When Admin sets the business date to "22 January 2023"
And Customer makes "AUTOPAY" repayment on "22 January 2023" with 100 EUR transaction amount
When Admin sets the business date to "29 January 2023"
And Admin does write-off the loan on "29 January 2023"
Then Loan status will be "CLOSED_WRITTEN_OFF"
Then Loan Transactions tab has a transaction with date: "29 January 2023", and with the following data:
| Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| Close (as written-off) | 650.0 | 650.0 | 0.0 | 0.0 | 0.0 | 0.0 |
Then Loan "AUTOPAY" repayment transaction on "26 January 2023" with 50 EUR transaction amount results in error
@TestRailId:C2936
Scenario: As a user I want to do Write-off a loan and verify that undo write-off results in error
When Admin sets the business date to "1 January 2023"
And Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| PIN4_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "1 January 2023" with "1000" amount and expected disbursement date on "1 January 2023"
And Admin successfully disburse the loan on "1 January 2023" with "1000" EUR transaction amount
When Admin sets the business date to "22 January 2023"
And Customer makes "AUTOPAY" repayment on "22 January 2023" with 100 EUR transaction amount
When Admin sets the business date to "29 January 2023"
And Admin does write-off the loan on "29 January 2023"
Then Loan status will be "CLOSED_WRITTEN_OFF"
Then Loan Transactions tab has a transaction with date: "29 January 2023", and with the following data:
| Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| Close (as written-off) | 650.0 | 650.0 | 0.0 | 0.0 | 0.0 | 0.0 |
Then Admin fails to undo "1"th transaction made on "29 January 2023"