FINERACT-2326: Fix buydown fee amounts missing from Transaction Summary Reports
This commit is contained in:
committed by
Adam Saghy
parent
a656dbde18
commit
7b545beb16
+1
@@ -190,6 +190,7 @@ public enum DefaultLoanProduct implements LoanProduct {
|
||||
LP2_ADV_PYMNT_INT_DAILY_EMI_360_30_INT_RECALC_DAILY_MULTIDISB_FULL_TERM_TRANCHE_CHARGEBACK, //
|
||||
LP2_ADV_PYMNT_INT_DAILY_EMI_360_30_INT_RECALC_DAILY_MULTIDISB_FULL_TERM_TRANCHE_ZERO_INT_CHARGE_OFF, //
|
||||
LP2_ADV_PYMNT_INT_DAILY_EMI_360_30_INT_RECALC_DAILY_MULTIDISB_FULL_TERM_TRANCHE_ACCELERATE_MATURITY, //
|
||||
LP2_PROGRESSIVE_ADVANCED_PAYMENT_ALLOCATION_BUYDOWN_FEES_FEE_INCOME, //
|
||||
;
|
||||
|
||||
@Override
|
||||
|
||||
+5
@@ -1770,6 +1770,11 @@ public class LoanProductsRequestFactory {
|
||||
.incomeFromBuyDownAccountId(accountTypeResolver.resolve(DefaultAccountType.INCOME_FROM_BUY_DOWN));//
|
||||
}
|
||||
|
||||
public PostLoanProductsRequest defaultLoanProductsRequestLP2BuyDownFeesFeeIncome() {
|
||||
return defaultLoanProductsRequestLP2BuyDownFees()//
|
||||
.buyDownFeeIncomeType(PostLoanProductsRequest.BuyDownFeeIncomeTypeEnum.FEE);//
|
||||
}
|
||||
|
||||
public PostLoanProductsRequest defaultLoanProductsRequestLP2ChargeOffReasonToExpenseAccountMappingsWithBuyDownFee() {
|
||||
|
||||
Long chargeOffReasonId = codeHelper.retrieveCodeByName(CHARGE_OFF_REASONS).getId();
|
||||
|
||||
+19
-9
@@ -24,26 +24,23 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import io.cucumber.java.en.Then;
|
||||
import io.cucumber.java.en.When;
|
||||
import java.util.Collections;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.client.feign.FineractFeignClient;
|
||||
import org.apache.fineract.client.models.ClientAddressRequest;
|
||||
import org.apache.fineract.client.models.PostClientsRequest;
|
||||
import org.apache.fineract.client.models.PostClientsResponse;
|
||||
import org.apache.fineract.client.models.PostOfficesResponse;
|
||||
import org.apache.fineract.test.factory.ClientRequestFactory;
|
||||
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;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ClientStepDef extends AbstractStepDef {
|
||||
|
||||
@Autowired
|
||||
private FineractFeignClient fineractClient;
|
||||
|
||||
@Autowired
|
||||
private ClientRequestFactory clientRequestFactory;
|
||||
|
||||
@Autowired
|
||||
private EventCheckHelper eventCheckHelper;
|
||||
private final FineractFeignClient fineractClient;
|
||||
private final ClientRequestFactory clientRequestFactory;
|
||||
private final EventCheckHelper eventCheckHelper;
|
||||
|
||||
@When("Admin creates a client with random data")
|
||||
public void createClientRandomFirstNameLastName() {
|
||||
@@ -55,6 +52,19 @@ public class ClientStepDef extends AbstractStepDef {
|
||||
eventCheckHelper.clientEventCheck(response);
|
||||
}
|
||||
|
||||
@When("Admin creates a client with random data in the last created office")
|
||||
public void createClientInLastCreatedOffice() {
|
||||
final PostOfficesResponse officeResponse = testContext().get(TestContextKey.OFFICE_CREATE_RESPONSE);
|
||||
assertThat(officeResponse).as("No office was created. Use 'Admin creates a new office' step first.").isNotNull();
|
||||
final PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest()//
|
||||
.officeId(officeResponse.getOfficeId());
|
||||
|
||||
final PostClientsResponse response = ok(() -> fineractClient.clients().create6(clientsRequest));
|
||||
testContext().set(TestContextKey.CLIENT_CREATE_RESPONSE, response);
|
||||
|
||||
eventCheckHelper.clientEventCheck(response);
|
||||
}
|
||||
|
||||
@When("Admin creates a second client with random data")
|
||||
public void createSecondClientRandomFirstNameLastName() {
|
||||
PostClientsRequest clientsRequest = clientRequestFactory.defaultClientCreationRequest();
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 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.apache.fineract.client.feign.util.FeignCalls.ok;
|
||||
|
||||
import io.cucumber.java.en.When;
|
||||
import java.time.LocalDate;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.client.feign.FineractFeignClient;
|
||||
import org.apache.fineract.client.models.PostOfficesRequest;
|
||||
import org.apache.fineract.client.models.PostOfficesResponse;
|
||||
import org.apache.fineract.test.helper.Utils;
|
||||
import org.apache.fineract.test.stepdef.AbstractStepDef;
|
||||
import org.apache.fineract.test.support.TestContextKey;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class OfficeStepDef extends AbstractStepDef {
|
||||
|
||||
private final FineractFeignClient fineractClient;
|
||||
|
||||
@When("Admin creates a new office")
|
||||
public void createNewOffice() {
|
||||
final PostOfficesRequest request = new PostOfficesRequest()//
|
||||
.name(Utils.randomStringGenerator("Office_", 5))//
|
||||
.parentId(1L)//
|
||||
.openingDate(LocalDate.of(2000, 1, 1))//
|
||||
.dateFormat("yyyy-MM-dd")//
|
||||
.locale("en");//
|
||||
|
||||
final PostOfficesResponse response = ok(() -> fineractClient.offices().createOffice(request));
|
||||
testContext().set(TestContextKey.OFFICE_CREATE_RESPONSE, response);
|
||||
}
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* 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.reporting;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
|
||||
import io.cucumber.datatable.DataTable;
|
||||
import io.cucumber.java.en.Then;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.IntStream;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.client.feign.FineractFeignClient;
|
||||
import org.apache.fineract.client.models.PostOfficesResponse;
|
||||
import org.apache.fineract.client.models.ResultsetColumnHeaderData;
|
||||
import org.apache.fineract.client.models.RunReportsResponse;
|
||||
import org.apache.fineract.test.stepdef.AbstractStepDef;
|
||||
import org.apache.fineract.test.support.TestContextKey;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ReportingStepDef extends AbstractStepDef {
|
||||
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("dd MMMM yyyy", Locale.ENGLISH);
|
||||
|
||||
private final FineractFeignClient fineractClient;
|
||||
|
||||
@Then("Transaction Summary Report for date {string} has the following data:")
|
||||
public void transactionSummaryReportHasData(final String dateStr, final DataTable dataTable) {
|
||||
verifyReportData("Transaction Summary Report", dateStr, dataTable);
|
||||
}
|
||||
|
||||
@Then("Transaction Summary Report with Asset Owner for date {string} has the following data:")
|
||||
public void transactionSummaryReportWithAssetOwnerHasData(final String dateStr, final DataTable dataTable) {
|
||||
verifyReportData("Transaction Summary Report with Asset Owner", dateStr, dataTable);
|
||||
}
|
||||
|
||||
private void verifyReportData(final String reportName, final String dateStr, final DataTable dataTable) {
|
||||
final PostOfficesResponse officeResponse = testContext().get(TestContextKey.OFFICE_CREATE_RESPONSE);
|
||||
assertThat(officeResponse).as("No office was created. Use 'Admin creates a new office' step first.").isNotNull();
|
||||
|
||||
final String date = LocalDate.parse(dateStr, FORMATTER).toString();
|
||||
final RunReportsResponse response = fineractClient.runReports().runReportGetData(reportName, Map.of("R_endDate", date, "R_officeId",
|
||||
String.valueOf(officeResponse.getOfficeId()), "locale", "en", "dateFormat", "yyyy-MM-dd"));
|
||||
assertThat(response.getData()).as("Report '%s' returned no data", reportName).isNotNull();
|
||||
|
||||
final List<List<String>> expected = dataTable.asLists();
|
||||
final List<String> headers = expected.getFirst();
|
||||
|
||||
assertThat(response.getColumnHeaders()).isNotNull();
|
||||
final int[] colIdx = headers.stream().mapToInt(h -> findColumnIndex(response.getColumnHeaders(), h)).toArray();
|
||||
|
||||
final List<List<String>> actual = response.getData().stream().map(row -> {
|
||||
assertThat(row.getRow()).as("Report '%s' returned a row with null cell list", reportName).isNotNull();
|
||||
return IntStream.of(colIdx).mapToObj(i -> {
|
||||
assertThat(i).as("Report '%s': column index %d is out of bounds (row size: %d)", reportName, i, row.getRow().size())
|
||||
.isLessThan(row.getRow().size());
|
||||
return stringify(row.getRow().get(i));
|
||||
}).toList();
|
||||
}).toList();
|
||||
|
||||
assertThat(actual).as("Report '%s' row count mismatch.\nActual rows:\n%s", reportName, formatRows(actual))
|
||||
.hasSize(expected.size() - 1);
|
||||
|
||||
for (int i = 1; i < expected.size(); i++) {
|
||||
final List<String> expRow = expected.get(i).stream().map(v -> v == null ? "" : v).toList();
|
||||
final List<String> actRow = actual.get(i - 1);
|
||||
for (int j = 0; j < headers.size(); j++) {
|
||||
if (expRow.get(j).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (!valuesMatch(expRow.get(j), actRow.get(j))) {
|
||||
fail("Report '%s', row %d, column '%s': expected='%s', actual='%s'\nAll actual rows:\n%s", reportName, i,
|
||||
headers.get(j), expRow.get(j), actRow.get(j), formatRows(actual));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean valuesMatch(final String expected, final String actual) {
|
||||
if (Objects.equals(expected, actual)) {
|
||||
return true;
|
||||
}
|
||||
if (isBooleanMatch(expected, actual)) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
return new BigDecimal(expected).compareTo(new BigDecimal(actual)) == 0;
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isBooleanMatch(final String expected, final String actual) {
|
||||
return ("0".equals(expected) && "false".equals(actual)) || ("false".equals(expected) && "0".equals(actual))
|
||||
|| ("1".equals(expected) && "true".equals(actual)) || ("true".equals(expected) && "1".equals(actual));
|
||||
}
|
||||
|
||||
private String stringify(final Object val) {
|
||||
return val == null ? "null" : String.valueOf(val);
|
||||
}
|
||||
|
||||
private String formatRows(final List<List<String>> rows) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < rows.size(); i++) {
|
||||
sb.append(String.format(" [%d] %s%n", i + 1, String.join(" | ", rows.get(i))));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private int findColumnIndex(final List<ResultsetColumnHeaderData> headers, final String name) {
|
||||
return IntStream.range(0, headers.size()).filter(i -> name.equalsIgnoreCase(headers.get(i).getColumnName())).findFirst()
|
||||
.orElseThrow(() -> new IllegalArgumentException("Column '" + name + "' not found in report"));
|
||||
}
|
||||
}
|
||||
+2
@@ -169,6 +169,7 @@ public abstract class TestContextKey {
|
||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_PROGRESSIVE_ADV_PYMNT_BUYDOWN_FEES_NON_MERCHANT = "loanProductCreateResponseLP2ProgressiveAdvancedPaymentBuyDownFeesNonMerchant";
|
||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_PROGRESSIVE_ADV_PYMNT_BUYDOWN_FEES_NON_MERCHANT_CHARGE_OFF_REASON = "loanProductCreateResponseLP2ProgressiveAdvancedPaymentBuyDownFeesNonMerchantWithChargeOffReason";
|
||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_PROGRESSIVE_ADV_PYMNT_BUYDOWN_FEES_CLASSIFICATION_INCOME_MAP = "loanProductCreateResponseLP2ProgressiveAdvancedPaymentBuyDownFeesClassificationIncomeMap";
|
||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_PROGRESSIVE_ADV_PYMNT_BUYDOWN_FEES_FEE_INCOME = "loanProductCreateResponseLP2ProgressiveAdvancedPaymentBuyDownFeesFeeIncome";
|
||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALC_DAILY_CAPITALIZED_INCOME_ADJ_CUSTOM_ALLOC = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyCapitalizedIncomeAdjCustomAlloc";
|
||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_PROGRESSIVE_ADV_PMNT_ALLOCATION_CAPITALIZED_INCOME_ADJ_CUSTOM_ALLOC_CLASSIFICATION_INCOME_MAP = "loanProductCreateResponseLP2AdvancedPaymentCapitalizedIncomeAdjCustomAllocClassificationIncomeMao";
|
||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_RECALC_EMI_360_30_MULTIDISB_OVER_APPLIED_PERCENTAGE_CAPITALIZED_INCOME = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyMultidisbursalApprovedOVerAppliedPercentageCapitalizedIncome";
|
||||
@@ -311,4 +312,5 @@ public abstract class TestContextKey {
|
||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INT_DAILY_EMI_360_30_INT_RECALC_DAILY_MULTIDISB_FULL_TERM_TRANCHE_CHARGEBACK = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyMultidisburseFullTermTrancheChargeback";
|
||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INT_DAILY_EMI_360_30_INT_RECALC_DAILY_MULTIDISB_FULL_TERM_TRANCHE_ZERO_CHARGE_OFF = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyMultidisburseFullTermTrancheZeroIntChargeOff";
|
||||
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INT_DAILY_EMI_360_30_INT_RECALC_DAILY_MULTIDISB_FULL_TERM_TRANCHE_ACCELERATE_MATURITY = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyMultidisburseFullTermTrancheAccelerateMaturity";
|
||||
public static final String OFFICE_CREATE_RESPONSE = "officeCreateResponse";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user