Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@
import org.apache.fineract.client.feign.services.TwoFactorApi;
import org.apache.fineract.client.feign.services.UserGeneratedDocumentsApi;
import org.apache.fineract.client.feign.services.UsersApi;
import org.apache.fineract.client.feign.services.WorkingCapitalBreachApi;
import org.apache.fineract.client.feign.services.WorkingCapitalLoanCobCatchUpApi;
import org.apache.fineract.client.feign.services.WorkingCapitalLoanDelinquencyActionsApi;
import org.apache.fineract.client.feign.services.WorkingCapitalLoanDelinquencyRangeScheduleApi;
Expand Down Expand Up @@ -777,6 +778,10 @@ public WorkingCapitalLoanTransactionsApi workingCapitalLoanTransactions() {
return create(WorkingCapitalLoanTransactionsApi.class);
}

public WorkingCapitalBreachApi workingCapitalBreaches() {
return create(WorkingCapitalBreachApi.class);
}

public WorkingDaysApi workingDays() {
return create(WorkingDaysApi.class);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,30 @@ public CommandWrapperBuilder deleteWorkingCapitalLoanProduct(final Long productI
return this;
}

public CommandWrapperBuilder createWorkingCapitalBreach() {
this.actionName = "CREATE";
this.entityName = "WORKINGCAPITALBREACH";
this.entityId = null;
this.href = "/working-capital-breach/breaches";
return this;
}

public CommandWrapperBuilder updateWorkingCapitalBreach(final Long breachId) {
this.actionName = "UPDATE";
this.entityName = "WORKINGCAPITALBREACH";
this.entityId = breachId;
this.href = "/working-capital-breach/breaches/" + breachId;
return this;
}

public CommandWrapperBuilder deleteWorkingCapitalBreach(final Long breachId) {
this.actionName = "DELETE";
this.entityName = "WORKINGCAPITALBREACH";
this.entityId = breachId;
this.href = "/working-capital-breach/breaches/" + breachId;
return this;
}

public CommandWrapperBuilder createWorkingCapitalLoanApplication() {
this.actionName = "CREATE";
this.entityName = "WORKINGCAPITALLOAN";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
public enum DefaultWorkingCapitalLoanProduct implements WorkingCapitalLoanProduct {

WCLP, //
WCLP_DISALLOW_ATTRIBUTES_OVERRIDE, //
WCLP_FOR_UPDATE; //

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.apache.fineract.client.models.PostPaymentAllocation;
import org.apache.fineract.client.models.PostWorkingCapitalLoanProductsRequest;
import org.apache.fineract.client.models.PutWorkingCapitalLoanProductsProductIdRequest;
import org.apache.fineract.client.models.WorkingCapitalBreachRequest;
import org.apache.fineract.test.data.delinquency.DelinquencyBucketType;
import org.apache.fineract.test.data.delinquency.DelinquencyFrequencyType;
import org.apache.fineract.test.data.delinquency.DelinquencyMinimumPayment;
Expand All @@ -60,6 +61,10 @@ public class WorkingCapitalRequestFactory {
public static final String PENALTY = "PENALTY";
public static final String FEE = "FEE";
public static final String PRINCIPAL = "PRINCIPAL";
public static final Integer DEFAULT_WC_BREACH_FREQUENCY = 2;
public static final String DEFAULT_WC_BREACH_FREQUENCY_TYPE = "MONTHS";
public static final String DEFAULT_WC_BREACH_AMOUNT_CALCULATION_TYPE = "PERCENTAGE";
public static final BigDecimal DEFAULT_WC_BREACH_AMOUNT = new BigDecimal("1.23");

public PostWorkingCapitalLoanProductsRequest defaultWorkingCapitalLoanProductRequest() {
String name = Utils.randomStringGenerator(WCLP_NAME_PREFIX, 10);
Expand Down Expand Up @@ -91,6 +96,18 @@ public PostWorkingCapitalLoanProductsRequest defaultWorkingCapitalLoanProductReq
List.of(PENALTY, FEE, PRINCIPAL))));//
}

public PostWorkingCapitalLoanProductsRequest defaultWorkingCapitalLoanProductAllowAttributesOverrideRequest() {
String name = Utils.randomStringGenerator(WCLP_NAME_PREFIX, 10);
String shortName = loanProductsRequestFactory.generateShortNameSafely();

PostAllowAttributeOverrides allowAttributeOverrides = new PostAllowAttributeOverrides().delinquencyBucketClassification(true)
.breach(true).discountDefault(true).periodPaymentFrequencyType(true).periodPaymentFrequency(true);

return defaultWorkingCapitalLoanProductRequest().name(name)//
.shortName(shortName)//
.allowAttributeOverrides(allowAttributeOverrides);
}

public PutWorkingCapitalLoanProductsProductIdRequest defaultWorkingCapitalLoanProductRequestUpdate() {
String name = Utils.randomStringGenerator(WCLP_NAME_PREFIX, 10);
String shortName = loanProductsRequestFactory.generateShortNameSafely();
Expand Down Expand Up @@ -177,6 +194,14 @@ public DelinquencyBucketRequest defaultWorkingCapitalDelinquencyBucketRequest()
.minimumPayment(new BigDecimal("1.23")));
}

public WorkingCapitalBreachRequest defaultWorkingCapitalBreachRequest() {
return new WorkingCapitalBreachRequest() //
.breachFrequency(DEFAULT_WC_BREACH_FREQUENCY) //
.breachFrequencyType(DEFAULT_WC_BREACH_FREQUENCY_TYPE) //
.breachAmountCalculationType(DEFAULT_WC_BREACH_AMOUNT_CALCULATION_TYPE) //
.breachAmount(DEFAULT_WC_BREACH_AMOUNT);
}

private Long getWCDelinquencyBucketIdByName(String bucketName) {
try {
List<DelinquencyBucketResponse> buckets = fineractClient.delinquencyRangeAndBucketsManagement().getBuckets(Map.of());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1057,6 +1057,10 @@ public static String workingCapitalDelinquencyBucketDoesntExistFailure(Long id)
return String.format("Delinquency bucket with id `%d` does not exist.", id);
}

public static String workingCapitalBreachNotFoundFailure(final Long id) {
return String.format("Working Capital Breach with id %d was not found.", id);
}

public static String disburseNotApprovedFailure(String status) {
return String.format("Disbursement is not allowed from current status %s", status);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
/**
* 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.client.feign.util.FeignCalls.fail;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import static org.assertj.core.api.Assertions.assertThat;

import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.math.BigDecimal;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.feign.util.CallFailedRuntimeException;
import org.apache.fineract.client.models.CommandProcessingResult;
import org.apache.fineract.client.models.StringEnumOptionData;
import org.apache.fineract.client.models.WorkingCapitalBreachData;
import org.apache.fineract.client.models.WorkingCapitalBreachRequest;
import org.apache.fineract.client.models.WorkingCapitalBreachTemplateResponse;
import org.apache.fineract.test.factory.WorkingCapitalRequestFactory;
import org.apache.fineract.test.helper.ErrorMessageHelper;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContext;
import org.apache.fineract.test.support.TestContextKey;
import org.assertj.core.api.SoftAssertions;
import org.springframework.beans.factory.annotation.Autowired;

@Slf4j
@RequiredArgsConstructor
public class WorkingCapitalBreachConfigStepDef extends AbstractStepDef {

@Autowired
private WorkingCapitalRequestFactory workingCapitalRequestFactory;

private final FineractFeignClient fineractFeignClient;

@When("Admin Calls Breach Template")
public void adminCallsBreachTemplate() {
final WorkingCapitalBreachTemplateResponse template = ok(
() -> fineractFeignClient.workingCapitalBreaches().retrieveWorkingCapitalBreachTemplate());
assertThat(template).isNotNull();
assertThat(template.getBreachFrequencyTypeOptions()).isNotNull().isNotEmpty();
assertThat(template.getBreachAmountCalculationTypeOptions()).isNotNull().isNotEmpty();
log.info("Template WorkingCapitalBreach: {}", template);
}

@When("Admin creates WC Breach With Values")
public void adminCreatesWCBreachWithValues() {
final WorkingCapitalBreachRequest request = workingCapitalRequestFactory.defaultWorkingCapitalBreachRequest();
final CommandProcessingResult response = ok(() -> fineractFeignClient.workingCapitalBreaches().createWorkingCapitalBreach(request));
assertThat(response).isNotNull();
assertThat(response.getResourceId()).isNotNull();
TestContext.GLOBAL.set(TestContextKey.WORKING_CAPITAL_BREACH_ID, response.getResourceId());
TestContext.GLOBAL.set(TestContextKey.WORKING_CAPITAL_BREACH_CREATE_REQUEST, request);
}

@When("Admin creates WC Breach With Values for update")
public void adminCreatesWCBreachWithValuesForUpdate() {
final WorkingCapitalBreachRequest request = workingCapitalRequestFactory.defaultWorkingCapitalBreachRequest()
.breachAmount(new BigDecimal("2.34"));
final CommandProcessingResult response = ok(() -> fineractFeignClient.workingCapitalBreaches().createWorkingCapitalBreach(request));
assertThat(response).isNotNull();
assertThat(response.getResourceId()).isNotNull();
TestContext.GLOBAL.set(TestContextKey.WORKING_CAPITAL_BREACH_ID_FOR_UPDATE, response.getResourceId());
TestContext.GLOBAL.set(TestContextKey.WORKING_CAPITAL_BREACH_CREATE_REQUEST_FOR_UPDATE, request);
}

@Then("Check created Breach has the following values")
public void checkCreatedBreachHasTheFollowingValues() {
final Long id = TestContext.GLOBAL.get(TestContextKey.WORKING_CAPITAL_BREACH_ID);
final WorkingCapitalBreachData data = ok(() -> fineractFeignClient.workingCapitalBreaches().retrieveWorkingCapitalBreach(id));
final WorkingCapitalBreachRequest request = TestContext.GLOBAL.get(TestContextKey.WORKING_CAPITAL_BREACH_CREATE_REQUEST);
checkBreachData(request, data);
}

@Then("Get Breach With Template has the following values")
public void getBreachWithTemplateHasTheFollowingValues() {
final Long id = TestContext.GLOBAL.get(TestContextKey.WORKING_CAPITAL_BREACH_ID);
final WorkingCapitalBreachData data = ok(() -> fineractFeignClient.workingCapitalBreaches().retrieveWorkingCapitalBreach(id));
final WorkingCapitalBreachTemplateResponse template = ok(
() -> fineractFeignClient.workingCapitalBreaches().retrieveWorkingCapitalBreachTemplate());
assertThat(data).isNotNull();
assertThat(template).isNotNull();
assertThat(template.getBreachFrequencyTypeOptions()).isNotNull().isNotEmpty();
assertThat(template.getBreachAmountCalculationTypeOptions()).isNotNull().isNotEmpty();
}

@When("Admin modifies WC Breach With Values")
public void adminModifiesWCBreachWithValues() {
final Long id = TestContext.GLOBAL.get(TestContextKey.WORKING_CAPITAL_BREACH_ID);
final WorkingCapitalBreachRequest request = workingCapitalRequestFactory.defaultWorkingCapitalBreachRequest() //
.breachFrequency(4) //
.breachFrequencyType("MONTHS") //
.breachAmountCalculationType("FLAT") //
.breachAmount(new BigDecimal("7.89"));
ok(() -> fineractFeignClient.workingCapitalBreaches().updateWorkingCapitalBreach(id, request));
TestContext.GLOBAL.set(TestContextKey.WORKING_CAPITAL_BREACH_UPDATE_REQUEST, request);
}

@Then("Check updated Breach has the following values")
public void checkUpdatedBreachHasTheFollowingValues() {
final Long id = TestContext.GLOBAL.get(TestContextKey.WORKING_CAPITAL_BREACH_ID);
final WorkingCapitalBreachData data = ok(() -> fineractFeignClient.workingCapitalBreaches().retrieveWorkingCapitalBreach(id));
final WorkingCapitalBreachRequest request = TestContext.GLOBAL.get(TestContextKey.WORKING_CAPITAL_BREACH_UPDATE_REQUEST);
checkBreachData(request, data);
}

@When("Admin deletes WC Breach With Values")
public void adminDeletesWCBreachWithValues() {
final Long id = TestContext.GLOBAL.get(TestContextKey.WORKING_CAPITAL_BREACH_ID);
ok(() -> fineractFeignClient.workingCapitalBreaches().deleteWorkingCapitalBreach(id));
}

@When("Admin deletes WC Breach With Values for update")
public void adminDeletesWCBreachWithValuesForUpdate() {
final Long id = TestContext.GLOBAL.get(TestContextKey.WORKING_CAPITAL_BREACH_ID_FOR_UPDATE);
ok(() -> fineractFeignClient.workingCapitalBreaches().deleteWorkingCapitalBreach(id));
}

@Then("Admin failed to create a new WC Breach for field {string} with invalid data {string} results with an error {}")
public void createWCBreachWithInvalidDataFailed(final String fieldName, final String value, final String errorMessage) {
final WorkingCapitalBreachRequest request = setWCBreachFieldValue(workingCapitalRequestFactory.defaultWorkingCapitalBreachRequest(),
fieldName, value);
checkCreateWCBreachWithInvalidDataFailure(request, errorMessage, 400);
}

@Then("Admin failed to update WC Breach for field {string} with invalid data {string} results with an error {}")
public void updateWCBreachWithInvalidDataFailed(final String fieldName, final String value, final String errorMessage) {
Long id = TestContext.GLOBAL.get(TestContextKey.WORKING_CAPITAL_BREACH_ID_FOR_UPDATE);
if (id == null) {
adminCreatesWCBreachWithValuesForUpdate();
id = TestContext.GLOBAL.get(TestContextKey.WORKING_CAPITAL_BREACH_ID_FOR_UPDATE);
}
final WorkingCapitalBreachRequest request = setWCBreachFieldValue(workingCapitalRequestFactory.defaultWorkingCapitalBreachRequest(),
fieldName, value);
checkUpdateWCBreachWithInvalidDataFailure(id, request, errorMessage, 400);
}

@Then("Admin failed to delete WC Breach that is already deleted")
public void adminDeleteWCBreachAlreadyDeletedFailure() {
final Long id = TestContext.GLOBAL.get(TestContextKey.WORKING_CAPITAL_BREACH_ID_FOR_UPDATE);
checkDeleteWCBreachNotFoundFailure(id);
}

@Then("Admin failed to retrieve WC Breach that is already deleted")
public void adminRetrieveWCBreachAlreadyDeletedFailure() {
final Long id = TestContext.GLOBAL.get(TestContextKey.WORKING_CAPITAL_BREACH_ID_FOR_UPDATE);
checkRetrieveWCBreachNotFoundFailure(id);
}

@Then("Admin failed to delete WC Breach with id {int} that doesn't exist")
public void adminDeleteWCBreachWithIncorrectIdFailure(final Integer id) {
checkDeleteWCBreachNotFoundFailure(Long.valueOf(id));
}

@Then("Admin failed to retrieve WC Breach with id {int} that is not found")
public void adminRetrieveWCBreachWithIncorrectIdFailure(final Integer id) {
checkRetrieveWCBreachNotFoundFailure(Long.valueOf(id));
}

private void checkCreateWCBreachWithInvalidDataFailure(final WorkingCapitalBreachRequest request, final String errorMessage,
final int errorCode) {
final CallFailedRuntimeException exception = fail(
() -> fineractFeignClient.workingCapitalBreaches().createWorkingCapitalBreach(request));
assertThat(exception.getStatus()).as(ErrorMessageHelper.incorrectExpectedValueInResponse()).isEqualTo(errorCode);
assertThat(exception.getDeveloperMessage()).contains(errorMessage);
}

private void checkUpdateWCBreachWithInvalidDataFailure(final Long id, final WorkingCapitalBreachRequest request,
final String errorMessage, final int errorCode) {
final CallFailedRuntimeException exception = fail(
() -> fineractFeignClient.workingCapitalBreaches().updateWorkingCapitalBreach(id, request));
assertThat(exception.getStatus()).as(ErrorMessageHelper.incorrectExpectedValueInResponse()).isEqualTo(errorCode);
assertThat(exception.getDeveloperMessage()).contains(errorMessage);
}

private void checkDeleteWCBreachNotFoundFailure(final Long id) {
final CallFailedRuntimeException exception = fail(
() -> fineractFeignClient.workingCapitalBreaches().deleteWorkingCapitalBreach(id));
assertThat(exception.getStatus()).isEqualTo(404);
assertThat(exception.getDeveloperMessage()).contains(ErrorMessageHelper.workingCapitalBreachNotFoundFailure(id));
}

private void checkRetrieveWCBreachNotFoundFailure(final Long id) {
final CallFailedRuntimeException exception = fail(
() -> fineractFeignClient.workingCapitalBreaches().retrieveWorkingCapitalBreach(id));
assertThat(exception.getStatus()).isEqualTo(404);
assertThat(exception.getDeveloperMessage()).contains(ErrorMessageHelper.workingCapitalBreachNotFoundFailure(id));
}

private WorkingCapitalBreachRequest setWCBreachFieldValue(final WorkingCapitalBreachRequest request, final String fieldName,
String fieldValue) {
if ("null".equals(fieldValue)) {
fieldValue = null;
}
final Integer intValue = fieldValue != null && "breachFrequency".equals(fieldName) ? Integer.valueOf(fieldValue) : null;
final BigDecimal decimalValue = fieldValue != null && "breachAmount".equals(fieldName) ? new BigDecimal(fieldValue) : null;
switch (fieldName) {
case "breachFrequency":
request.setBreachFrequency(intValue);
break;
case "breachFrequencyType":
request.setBreachFrequencyType(fieldValue);
break;
case "breachAmountCalculationType":
request.setBreachAmountCalculationType(fieldValue);
break;
case "breachAmount":
request.setBreachAmount(decimalValue);
break;
default:
break;
}
return request;
}

private void checkBreachData(final WorkingCapitalBreachRequest request, final WorkingCapitalBreachData response) {
final SoftAssertions assertions = new SoftAssertions();
assertions.assertThat(response.getBreachFrequency()).isEqualTo(request.getBreachFrequency());
assertions.assertThat(enumId(response.getBreachFrequencyType())).isEqualTo(request.getBreachFrequencyType());
assertions.assertThat(enumId(response.getBreachAmountCalculationType())).isEqualTo(request.getBreachAmountCalculationType());
assert response.getBreachAmount() != null;
assertions.assertThat(response.getBreachAmount().compareTo(request.getBreachAmount())).isEqualTo(0);
assertions.assertAll();
}

private String enumId(final StringEnumOptionData option) {
return option != null ? option.getId() : null;
}
}
Loading
Loading