FINERACT-2070: e2e test framework update

This commit is contained in:
Peter Kovacs
2024-11-07 21:49:46 +01:00
committed by Adam Saghy
parent 492ec44cef
commit 6feb10a87e
57 changed files with 2456 additions and 462 deletions
@@ -20,7 +20,10 @@ package org.apache.fineract.test.data;
public enum AdvancePaymentsAdjustmentType {
RESCHEDULE_NEXT_REPAYMENTS(1), REDUCE_NUMBER_OF_INSTALLMENTS(2), REDUCE_EMI_AMOUNT(3), ADJUST_LAST_UNPAID_PERIOD(4);
RESCHEDULE_NEXT_REPAYMENTS(1), //
REDUCE_NUMBER_OF_INSTALLMENTS(2), //
REDUCE_EMI_AMOUNT(3), //
ADJUST_LAST_UNPAID_PERIOD(4);//
public final Integer value;
@@ -0,0 +1,43 @@
/**
* 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.data;
public enum CobBusinessStep {
APPLY_CHARGE_TO_OVERDUE_LOANS("APPLY_CHARGE_TO_OVERDUE_LOANS"), //
LOAN_DELINQUENCY_CLASSIFICATION("LOAN_DELINQUENCY_CLASSIFICATION"), //
CHECK_LOAN_REPAYMENT_DUE("CHECK_LOAN_REPAYMENT_DUE"), //
CHECK_LOAN_REPAYMENT_OVERDUE("CHECK_LOAN_REPAYMENT_OVERDUE"), //
UPDATE_LOAN_ARREARS_AGING("UPDATE_LOAN_ARREARS_AGING"), //
ADD_PERIODIC_ACCRUAL_ENTRIES("ADD_PERIODIC_ACCRUAL_ENTRIES"), //
EXTERNAL_ASSET_OWNER_TRANSFER("EXTERNAL_ASSET_OWNER_TRANSFER"), //
CHECK_DUE_INSTALLMENTS("CHECK_DUE_INSTALLMENTS"), //
ACCRUAL_ACTIVITY_POSTING("ACCRUAL_ACTIVITY_POSTING"), //
LOAN_INTEREST_RECALCULATION("LOAN_INTEREST_RECALCULATION");//
public final String value;
CobBusinessStep(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
@@ -20,16 +20,29 @@ package org.apache.fineract.test.data;
public enum LoanRescheduleErrorMessage {
LOAN_CHARGED_OFF("Loan: %s reschedule installment is not allowed. Loan Account is Charged-off"), LOAN_LOCKED_BY_COB(
"Loan is locked by the COB job. Loan ID: %s");
LOAN_CHARGED_OFF("Loan: %s reschedule installment is not allowed. Loan Account is Charged-off"), //
LOAN_RESCHEDULE_DATE_NOT_IN_FUTURE("Loan Reschedule From date (%s) for Loan: %s should be in the future."), //
LOAN_LOCKED_BY_COB("Loan is locked by the COB job. Loan ID: %s");//
public final String value;
private final String messageTemplate;
LoanRescheduleErrorMessage(String value) {
this.value = value;
LoanRescheduleErrorMessage(String messageTemplate) {
this.messageTemplate = messageTemplate;
}
public String getValue(Object... params) {
return String.format(this.value, params);
if (params.length != getExpectedParameterCount()) {
throw new IllegalArgumentException("Expected " + getExpectedParameterCount() + " parameters, but got " + params.length);
}
return String.format(this.messageTemplate, params);
}
public int getExpectedParameterCount() {
// Count the number of placeholders (%s) in the message template
return (int) messageTemplate.chars().filter(ch -> ch == '%').count();
}
public String getMessageTemplate() {
return messageTemplate;
}
}
@@ -23,8 +23,15 @@ import java.util.Map;
public enum LoanStatus {
NONE(0), SUBMITTED_AND_PENDING_APPROVAL(100), APPROVED(200), ACTIVE(300), CLOSED_OBLIGATIONS_MET(600), OVERPAID(
700), CLOSED_WRITTEN_OFF(601);
NONE(0), //
SUBMITTED_AND_PENDING_APPROVAL(100), //
APPROVED(200), //
ACTIVE(300), //
WITHDRAWN(400), //
REJECTED(500), //
CLOSED_OBLIGATIONS_MET(600), //
OVERPAID(700), //
CLOSED_WRITTEN_OFF(601);//
public final Integer value;
@@ -20,9 +20,19 @@ package org.apache.fineract.test.data;
public enum TransactionType {
DISBURSEMENT("disbursement"), REPAYMENT("repayment"), DOWN_PAYMENT("downPayment"), GOODWILL_CREDIT("goodwillCredit"), PAYOUT_REFUND(
"payoutRefund"), REFUND_BY_CASH("refundByCash"), MERCHANT_ISSUED_REFUND("merchantIssuedRefund"), CREDIT_BALANCE_REFUND(
"creditBalanceRefund"), CHARGEBACK("chargeback"), ACCRUAL("accrual"), CHARGE_OFF("chargeOff");
DISBURSEMENT("disbursement"), //
REPAYMENT("repayment"), //
DOWN_PAYMENT("downPayment"), //
GOODWILL_CREDIT("goodwillCredit"), //
PAYOUT_REFUND("payoutRefund"), //
REFUND_BY_CASH("refundByCash"), //
MERCHANT_ISSUED_REFUND("merchantIssuedRefund"), //
CREDIT_BALANCE_REFUND("creditBalanceRefund"), //
CHARGEBACK("chargeback"), //
ACCRUAL("accrual"), //
CHARGE_OFF("chargeOff"), //
CHARGE_ADJUSTMENT("chargeAdjustment"), //
INTEREST_PAYMENT_WAIVER("interestPaymentWaiver");//
public final String value;
@@ -25,7 +25,7 @@ public enum DefaultJob implements Job {
INCREASE_BUSINESS_DAY("Increase Business Date by 1 day", "BDT_INC1"), //
LOAN_DELINQUENCY_CLASSIFICATION("Loan Delinquency Classification", "LA_DECL"), //
LOAN_COB("Loan COB", "LA_ECOB"), //
;
ACCRUAL_ACTIVITY_POSTING("Accrual Activity Posting", "ACC_ACPO");
private final String customName;
private final String shortName;
@@ -25,6 +25,8 @@ public enum DefaultLoanProduct implements LoanProduct {
LP1_INTEREST_FLAT, //
LP1_INTEREST_DECLINING_BALANCE_PERIOD_SAME_AS_PAYMENT, //
LP1_INTEREST_DECLINING_BALANCE_PERIOD_DAILY, //
LP1_INTEREST_DECLINING_BALANCE_PERIOD_DAILY_ACCRUAL_ACTIVITY, //
LP1_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_NONE_ACCRUAL_ACTIVITY, //
LP1_1MONTH_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_MONTHLY, //
LP1_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_NONE, //
LP1_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_NONE_RESCHEDULE_REDUCE_NR_INST, //
@@ -37,21 +39,37 @@ public enum DefaultLoanProduct implements LoanProduct {
LP1_PAYMENT_STRATEGY_DUE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_INTEREST_FLAT, //
LP1_INTEREST_FLAT_OVERDUE_FROM_AMOUNT, //
LP1_INTEREST_FLAT_OVERDUE_FROM_AMOUNT_INTEREST, //
LP2_DOWNPAYMENT, LP2_DOWNPAYMENT_AUTO, //
LP2_DOWNPAYMENT, //
LP2_DOWNPAYMENT_AUTO, //
LP2_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION, //
LP2_DOWNPAYMENT_ADVANCED_PAYMENT_ALLOCATION, //
LP2_DOWNPAYMENT_INTEREST, LP2_DOWNPAYMENT_INTEREST_AUTO, //
LP2_ADV_CUST_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL, //
LP2_DOWNPAYMENT_INTEREST, //
LP2_DOWNPAYMENT_INTEREST_AUTO, //
LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL, //
LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_VERTICAL, //
LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL_INSTALLMENT_LEVEL_DELINQUENCY, //
LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROG_SCHEDULE_HOR_INST_LVL_DELINQUENCY_CREDIT_ALLOCATION, //
LP2_DOWNPAYMENT_ADV_PMT_ALLOC_FIXED_LENGTH, //
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_TILL_PRECLOSE, //
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_TILL_REST_FREQUENCY, //
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_IR_DAILY_TILL_PRECLOSE_LAST_INSTALLMENT_STRATEGY, //
LP2_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL, //
;
LP2_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION_REPAYMENT_START_SUBMITTED, //
LP2_DOWNPAYMENT_INTEREST_FLAT_ADV_PMT_ALLOC, //
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL, //
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_ACCRUAL_ACTIVITY, //
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30, //
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE, //
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE_PMT_ALLOC_1, //
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_SARP_TILL_PRECLOSE, //
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_REST_FREQUENCY_DATE, //
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_SARP_TILL_REST_FREQUENCY_DATE, //
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_INTEREST_REFUND_FULL, //
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_MULTIDISBURSE, //
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_MULTIDISBURSE_DOWNPAYMENT, //
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_DOWNPAYMENT, //
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_365_ACTUAL, //
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_INTEREST_REFUND, //
LP1_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL, //
LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL, //
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE_WHOLE_TERM, //
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_IR_DAILY_TILL_PRECLOSE_LAST_INSTALLMENT_STRATEGY;//
@Override
public String getName() {
@@ -62,29 +62,30 @@ public class LoanProductsRequestFactory {
public static final String NAME_PREFIX = "LP1-";
public static final String NAME_PREFIX_LP2 = "LP2-";
public static final String NAME_PREFIX_LP2_EMI = "LP2Emi-";
public static final String NAME_PREFIX_INTEREST_FLAT = "LP1InterestFlat-";
public static final String NAME_PREFIX_INTEREST_FLAT_LP2 = "LP2InterestFlat-";
public static final String NAME_PREFIX_INTEREST_DECLINING = "LP1InterestDeclining-";
public static final String NAME_PREFIX_INTEREST_DECLINING_RECALCULATION = "LP1InterestDecliningRecalculation-";
public static final String NAME_PREFIX_INTEREST_RECALCULATION_WITH_DOWN_PAYMENT_LP3 = "LP3InterestRecalculationWithDownPayment-";
public static final String NAME_PREFIX_LP2_EMI = "LP2Emi-";
public static final String NAME_PREFIX_INTEREST_RECALCULATION = "LP2InterestRecalculation-";
public static final String SHORT_NAME_PREFIX = "p";
public static final String SHORT_NAME_PREFIX_INTEREST = "i";
public static final String SHORT_NAME_PREFIX_EMI = "e";
public static final String DATE_FORMAT = "dd MMMM yyyy";
public static final String LOCALE_EN = "en";
public static final String DESCRIPTION = "LP1 product";
public static final String DESCRIPTION_LP2 = "LP2 product";
public static final String DESCRIPTION_INTEREST_FLAT = "LP1 product with 12% interest - FLAT";
public static final String DESCRIPTION_INTEREST_FLAT_LP2 = "LP2 product with 12% interest - FLAT";
public static final String DESCRIPTION_INTEREST_DECLINING = "LP1 product with 12% interest - DECLINING BALANCE";
public static final String DESCRIPTION = "30 days repayment";
public static final String DESCRIPTION_LP2 = "4 installments repayment";
public static final String DESCRIPTION_LP2_EMI = "4 installments repayment with EMI";
public static final String DESCRIPTION_INTEREST_FLAT = "30 days repayment with 12% interest - FLAT";
public static final String DESCRIPTION_INTEREST_FLAT_LP2 = "4 installments repayment with 12% interest - FLAT";
public static final String DESCRIPTION_INTEREST_DECLINING = "30 days repayment with 12% interest - DECLINING BALANCE";
public static final String DESCRIPTION_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_MONTHLY = "LP1-1MONTH with 12% DECLINING BALANCE interest, interest period: Daily, Interest recalculation-Monthly, Compounding:Interest";
public static final String DESCRIPTION_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_NONE = "LP1 with 12% DECLINING BALANCE interest, interest period: Daily, Interest recalculation-Daily, Compounding:none";
public static final String DESCRIPTION_LP2_EMI = "LP2 product with EMI";
public static final Long FUND_ID = FundId.LENDER_A.value;
public static final String CURRENCY_CODE = "EUR";
public static final Integer INTEREST_RATE_FREQUENCY_TYPE_MONTH = InterestRateFrequencyType.MONTH.value;
public static final Integer INTEREST_RATE_FREQUENCY_TYPE_YEAR = InterestRateFrequencyType.YEAR.value;
public static final Integer INTEREST_RATE_FREQUENCY_TYPE_WHOLE_TERM = InterestRateFrequencyType.WHOLE_TERM.value;
public static final Long REPAYMENT_FREQUENCY_TYPE_DAYS = RepaymentFrequencyType.DAYS.value.longValue();
public static final Long REPAYMENT_FREQUENCY_TYPE_MONTHS = RepaymentFrequencyType.MONTHS.value.longValue();
public static final Integer AMORTIZATION_TYPE = AmortizationType.EQUAL_INSTALLMENTS.value;
@@ -94,10 +95,10 @@ public class LoanProductsRequestFactory {
public static final Integer INTEREST_CALCULATION_PERIOD_TYPE_DAILY = InterestCalculationPeriodTime.DAILY.value;
public static final String TRANSACTION_PROCESSING_STRATEGY_CODE = TransactionProcessingStrategyCode.PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER.value;
public static final String TRANSACTION_PROCESSING_STRATEGY_CODE_ADVANCED = TransactionProcessingStrategyCode.ADVANCED_PAYMENT_ALLOCATION.value;
public static final Integer DAYS_IN_YEAR_TYPE = DaysInYearType.ACTUAL.value;
public static final Integer DAYS_IN_YEAR_TYPE_360 = DaysInYearType.DAYS360.value;
public static final Integer DAYS_IN_MONTH_TYPE = DaysInMonthType.ACTUAL.value;
public static final Integer DAYS_IN_YEAR_TYPE = DaysInYearType.ACTUAL.value;
public static final Integer DAYS_IN_MONTH_TYPE_30 = DaysInMonthType.DAYS30.value;
public static final Integer DAYS_IN_MONTH_TYPE = DaysInMonthType.ACTUAL.value;
public static final Integer LOAN_ACCOUNTING_RULE = AccountingRule.ACCRUAL_PERIODIC.value;
public static final Integer LOAN_ACCOUNTING_RULE_NONE = AccountingRule.NONE.value;
public static final String OVER_APPLIED_CALCULATION_TYPE = "percentage";
@@ -661,36 +662,119 @@ public class LoanProductsRequestFactory {
.recalculationRestFrequencyInterval(1);//
}
public PostLoanProductsRequest defaultLoanProductsRequestLP2InterestDailyRecalculationWithDownPayment() {
final String name = Utils.randomNameGenerator(NAME_PREFIX_INTEREST_RECALCULATION_WITH_DOWN_PAYMENT_LP3, 4);
public PostLoanProductsRequest defaultLoanProductsRequestLP2InterestDailyRecalculation() {
final String name = Utils.randomNameGenerator(NAME_PREFIX_INTEREST_RECALCULATION, 4);
final String shortName = Utils.randomNameGenerator(SHORT_NAME_PREFIX_INTEREST, 3);
return new PostLoanProductsRequest().name(name).shortName(shortName)
.description(DESCRIPTION_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_NONE).startDate(null).closeDate(null)
.accountMovesOutOfNPAOnlyOnArrearsCompletion(false).accountingRule(LOAN_ACCOUNTING_RULE_NONE)
.allowApprovedDisbursedAmountsOverApplied(true)
.allowAttributeOverrides(new AllowAttributeOverrides().amortizationType(true).interestType(true)
.transactionProcessingStrategyCode(true).interestCalculationPeriodType(true).inArrearsTolerance(true)
.repaymentEvery(true).graceOnPrincipalAndInterestPayment(true).graceOnArrearsAgeing(true))
.allowPartialPeriodInterestCalcualtion(false).allowVariableInstallments(false).amortizationType(AMORTIZATION_TYPE)
.canDefineInstallmentAmount(true).canUseForTopup(false).charges(new ArrayList<>()).creditAllocation(new ArrayList<>())
.currencyCode(CURRENCY_CODE).dateFormat(DATE_FORMAT).daysInMonthType(DAYS_IN_MONTH_TYPE_30)
.daysInYearType(DAYS_IN_YEAR_TYPE_360).delinquencyBucketId(DELINQUENCY_BUCKET_ID.longValue()).digitsAfterDecimal(2)
.disallowExpectedDisbursements(true).dueDaysForRepaymentEvent(1).enableDownPayment(false)
.enableInstallmentLevelDelinquency(true).fixedLength(null).holdGuaranteeFunds(false).inMultiplesOf(0)
.includeInBorrowerCycle(false).interestCalculationPeriodType(0).interestRateFrequencyType(3).interestRatePerPeriod(9.99)
.interestRateVariationsForBorrowerCycle(new ArrayList<>()).interestRecalculationCompoundingMethod(0).interestType(0)
.isArrearsBasedOnOriginalSchedule(false).isEqualAmortization(false).isInterestRecalculationEnabled(true)
.isLinkedToFloatingInterestRates(false).loanScheduleProcessingType("HORIZONTAL").loanScheduleType("PROGRESSIVE")
.locale(LOCALE_EN).maxInterestRatePerPeriod((double) 50).maxNumberOfRepayments(48).maxPrincipal((double) 10000)
.maxTrancheCount(10).minInterestRatePerPeriod((double) 0).minNumberOfRepayments(1).minPrincipal((double) 1)
.multiDisburseLoan(true).numberOfRepaymentVariationsForBorrowerCycle(new ArrayList<>()).numberOfRepayments(3)
.outstandingLoanBalance((double) 10000).overAppliedCalculationType("flat").overAppliedNumber(10000)
.overDueDaysForRepaymentEvent(2).preClosureInterestCalculationStrategy(1).principal((double) 40)
.principalThresholdForLastInstallment(50).principalVariationsForBorrowerCycle(new ArrayList<>())
.recalculationRestFrequencyInterval(1).recalculationRestFrequencyType(2).repaymentEvery(1).repaymentFrequencyType(2L)
.repaymentStartDateType(2).rescheduleStrategyMethod(4).supportedInterestRefundTypes(new ArrayList<>())
.transactionProcessingStrategyCode(TRANSACTION_PROCESSING_STRATEGY_CODE_ADVANCED).useBorrowerCycle(false);
List<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings = new ArrayList<>();
List<GetLoanFeeToIncomeAccountMappings> feeToIncomeAccountMappings = new ArrayList<>();
List<GetLoanPaymentChannelToFundSourceMappings> paymentChannelToFundSourceMappings = new ArrayList<>();
GetLoanPaymentChannelToFundSourceMappings loanPaymentChannelToFundSourceMappings = new GetLoanPaymentChannelToFundSourceMappings();
loanPaymentChannelToFundSourceMappings.fundSourceAccountId(accountTypeResolver.resolve(DefaultAccountType.FUND_RECEIVABLES));
loanPaymentChannelToFundSourceMappings.paymentTypeId(paymentTypeResolver.resolve(DefaultPaymentType.MONEY_TRANSFER));
paymentChannelToFundSourceMappings.add(loanPaymentChannelToFundSourceMappings);
return new PostLoanProductsRequest().name(name)//
.shortName(shortName)//
.description(DESCRIPTION_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_NONE)//
.startDate(null)//
.closeDate(null)//
.accountMovesOutOfNPAOnlyOnArrearsCompletion(false)//
.allowApprovedDisbursedAmountsOverApplied(true)//
.allowAttributeOverrides(new AllowAttributeOverrides()//
.amortizationType(true)//
.interestType(true)//
.transactionProcessingStrategyCode(true)//
.interestCalculationPeriodType(true)//
.inArrearsTolerance(true)//
.repaymentEvery(true)//
.graceOnPrincipalAndInterestPayment(true)//
.graceOnArrearsAgeing(true))//
.allowPartialPeriodInterestCalcualtion(false)//
.allowVariableInstallments(false)//
.amortizationType(AMORTIZATION_TYPE)//
.canDefineInstallmentAmount(true)//
.canUseForTopup(false)//
.charges(new ArrayList<>()).creditAllocation(new ArrayList<>())//
.currencyCode(CURRENCY_CODE)//
.dateFormat(DATE_FORMAT)//
.daysInMonthType(DAYS_IN_MONTH_TYPE_30)//
.daysInYearType(DAYS_IN_YEAR_TYPE_360)//
.delinquencyBucketId(DELINQUENCY_BUCKET_ID.longValue())//
.digitsAfterDecimal(2)//
.disallowExpectedDisbursements(true)//
.dueDaysForRepaymentEvent(1)//
.enableDownPayment(false)//
.enableInstallmentLevelDelinquency(true)//
.fixedLength(null).holdGuaranteeFunds(false)//
.inMultiplesOf(0)//
.includeInBorrowerCycle(false)//
.interestCalculationPeriodType(0)//
.interestRateFrequencyType(3)//
.interestRatePerPeriod(9.99)//
.interestRateVariationsForBorrowerCycle(new ArrayList<>())//
.interestRecalculationCompoundingMethod(0)//
.interestType(0)//
.isArrearsBasedOnOriginalSchedule(false)//
.isEqualAmortization(false)//
.isInterestRecalculationEnabled(true)//
.isLinkedToFloatingInterestRates(false)//
.loanScheduleProcessingType("HORIZONTAL")//
.loanScheduleType("PROGRESSIVE")//
.locale(LOCALE_EN)//
.maxInterestRatePerPeriod((double) 50)//
.maxNumberOfRepayments(48)//
.maxPrincipal((double) 10000)//
.maxTrancheCount(10)//
.minInterestRatePerPeriod((double) 0)//
.minNumberOfRepayments(1)//
.minPrincipal((double) 1)//
.multiDisburseLoan(true)//
.numberOfRepaymentVariationsForBorrowerCycle(new ArrayList<>())//
.numberOfRepayments(3)//
.outstandingLoanBalance((double) 10000)//
.overAppliedCalculationType("flat")//
.overAppliedNumber(10000)//
.overDueDaysForRepaymentEvent(2)//
.preClosureInterestCalculationStrategy(1)//
.principal((double) 40)//
.principalThresholdForLastInstallment(50)//
.principalVariationsForBorrowerCycle(new ArrayList<>())//
.recalculationRestFrequencyInterval(1)//
.recalculationRestFrequencyType(2)//
.repaymentEvery(1)//
.repaymentFrequencyType(2L)//
.repaymentStartDateType(2)//
.rescheduleStrategyMethod(4)//
.supportedInterestRefundTypes(new ArrayList<>())//
.transactionProcessingStrategyCode(TRANSACTION_PROCESSING_STRATEGY_CODE_ADVANCED)//
.useBorrowerCycle(false)//
.accountingRule(LOAN_ACCOUNTING_RULE)//
.fundSourceAccountId(accountTypeResolver.resolve(DefaultAccountType.SUSPENSE_CLEARING_ACCOUNT))//
.loanPortfolioAccountId(accountTypeResolver.resolve(DefaultAccountType.LOANS_RECEIVABLE))//
.transfersInSuspenseAccountId(accountTypeResolver.resolve(DefaultAccountType.TRANSFER_IN_SUSPENSE_ACCOUNT))//
.interestOnLoanAccountId(accountTypeResolver.resolve(DefaultAccountType.INTEREST_INCOME))//
.incomeFromFeeAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_INCOME))//
.incomeFromPenaltyAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_INCOME))//
.incomeFromRecoveryAccountId(accountTypeResolver.resolve(DefaultAccountType.RECOVERIES))//
.writeOffAccountId(accountTypeResolver.resolve(DefaultAccountType.WRITTEN_OFF))//
.overpaymentLiabilityAccountId(accountTypeResolver.resolve(DefaultAccountType.OVERPAYMENT_ACCOUNT))//
.receivableInterestAccountId(accountTypeResolver.resolve(DefaultAccountType.INTEREST_FEE_RECEIVABLE))//
.receivableFeeAccountId(accountTypeResolver.resolve(DefaultAccountType.INTEREST_FEE_RECEIVABLE))//
.receivablePenaltyAccountId(accountTypeResolver.resolve(DefaultAccountType.INTEREST_FEE_RECEIVABLE))//
.goodwillCreditAccountId(accountTypeResolver.resolve(DefaultAccountType.GOODWILL_EXPENSE_ACCOUNT))//
.incomeFromGoodwillCreditInterestAccountId(accountTypeResolver.resolve(DefaultAccountType.INTEREST_INCOME_CHARGE_OFF))//
.incomeFromGoodwillCreditFeesAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_CHARGE_OFF))//
.incomeFromGoodwillCreditPenaltyAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_CHARGE_OFF))//
.paymentChannelToFundSourceMappings(paymentChannelToFundSourceMappings)//
.penaltyToIncomeAccountMappings(penaltyToIncomeAccountMappings)//
.feeToIncomeAccountMappings(feeToIncomeAccountMappings)//
.incomeFromChargeOffInterestAccountId(accountTypeResolver.resolve(DefaultAccountType.INTEREST_INCOME_CHARGE_OFF))//
.incomeFromChargeOffFeesAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_CHARGE_OFF))//
.chargeOffExpenseAccountId(accountTypeResolver.resolve(DefaultAccountType.CREDIT_LOSS_BAD_DEBT))//
.chargeOffFraudExpenseAccountId(accountTypeResolver.resolve(DefaultAccountType.CREDIT_LOSS_BAD_DEBT_FRAUD))//
.incomeFromChargeOffPenaltyAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_CHARGE_OFF));//
}
public PostLoanProductsRequest defaultLoanProductsRequestLP2() {
@@ -806,116 +890,6 @@ public class LoanProductsRequestFactory {
.incomeFromChargeOffPenaltyAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_CHARGE_OFF));//
}
public PostLoanProductsRequest defaultLoanProductsRequestLP2NoDown() {
String name = Utils.randomNameGenerator(NAME_PREFIX, 4);
String shortName = Utils.randomNameGenerator(SHORT_NAME_PREFIX, 3);
List<Integer> principalVariationsForBorrowerCycle = new ArrayList<>();
List<Integer> numberOfRepaymentVariationsForBorrowerCycle = new ArrayList<>();
List<Integer> interestRateVariationsForBorrowerCycle = new ArrayList<>();
List<ChargeData> charges = new ArrayList<>();
List<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings = new ArrayList<>();
List<GetLoanFeeToIncomeAccountMappings> feeToIncomeAccountMappings = new ArrayList<>();
List<GetLoanPaymentChannelToFundSourceMappings> paymentChannelToFundSourceMappings = new ArrayList<>();
GetLoanPaymentChannelToFundSourceMappings loanPaymentChannelToFundSourceMappings = new GetLoanPaymentChannelToFundSourceMappings();
loanPaymentChannelToFundSourceMappings.fundSourceAccountId(accountTypeResolver.resolve(DefaultAccountType.FUND_RECEIVABLES));
loanPaymentChannelToFundSourceMappings.paymentTypeId(paymentTypeResolver.resolve(DefaultPaymentType.MONEY_TRANSFER));
paymentChannelToFundSourceMappings.add(loanPaymentChannelToFundSourceMappings);
return new PostLoanProductsRequest()//
.name(name)//
.shortName(shortName)//
.description(DESCRIPTION_LP2)//
.fundId(FUND_ID)//
.startDate(null)//
.closeDate(null)//
.includeInBorrowerCycle(false)//
.currencyCode(CURRENCY_CODE)//
.digitsAfterDecimal(2)//
.inMultiplesOf(0)//
.installmentAmountInMultiplesOf(1)//
.useBorrowerCycle(false)//
.minPrincipal(100.0)//
.principal(1000.0)//
.maxPrincipal(10000.0)//
.minNumberOfRepayments(1)//
.numberOfRepayments(1)//
.maxNumberOfRepayments(30)//
.isLinkedToFloatingInterestRates(false)//
.minInterestRatePerPeriod((double) 0)//
.interestRatePerPeriod((double) 0)//
.maxInterestRatePerPeriod((double) 0)//
.interestRateFrequencyType(INTEREST_RATE_FREQUENCY_TYPE_MONTH)//
.repaymentEvery(30)//
.repaymentFrequencyType(REPAYMENT_FREQUENCY_TYPE_DAYS)//
.principalVariationsForBorrowerCycle(principalVariationsForBorrowerCycle)//
.numberOfRepaymentVariationsForBorrowerCycle(numberOfRepaymentVariationsForBorrowerCycle)//
.interestRateVariationsForBorrowerCycle(interestRateVariationsForBorrowerCycle)//
.amortizationType(AMORTIZATION_TYPE)//
.interestType(INTEREST_TYPE_DECLINING_BALANCE)//
.isEqualAmortization(false)//
.interestCalculationPeriodType(INTEREST_CALCULATION_PERIOD_TYPE_SAME_AS_REPAYMENT)//
.transactionProcessingStrategyCode(TRANSACTION_PROCESSING_STRATEGY_CODE)//
.daysInYearType(DAYS_IN_YEAR_TYPE)//
.daysInMonthType(DAYS_IN_MONTH_TYPE)//
.canDefineInstallmentAmount(true)//
.graceOnArrearsAgeing(3)//
.overdueDaysForNPA(179)//
.accountMovesOutOfNPAOnlyOnArrearsCompletion(false)//
.principalThresholdForLastInstallment(50)//
.allowVariableInstallments(false)//
.canUseForTopup(false)//
.isInterestRecalculationEnabled(false)//
.holdGuaranteeFunds(false)//
.multiDisburseLoan(true)//
.allowAttributeOverrides(new AllowAttributeOverrides()//
.amortizationType(true)//
.interestType(true)//
.transactionProcessingStrategyCode(true)//
.interestCalculationPeriodType(true)//
.inArrearsTolerance(true)//
.repaymentEvery(true)//
.graceOnPrincipalAndInterestPayment(true)//
.graceOnArrearsAgeing(true))//
.allowPartialPeriodInterestCalcualtion(true)//
.maxTrancheCount(10)//
.outstandingLoanBalance(10000.0)//
.charges(charges)//
.accountingRule(LOAN_ACCOUNTING_RULE)//
.fundSourceAccountId(accountTypeResolver.resolve(DefaultAccountType.SUSPENSE_CLEARING_ACCOUNT))//
.loanPortfolioAccountId(accountTypeResolver.resolve(DefaultAccountType.LOANS_RECEIVABLE))//
.transfersInSuspenseAccountId(accountTypeResolver.resolve(DefaultAccountType.TRANSFER_IN_SUSPENSE_ACCOUNT))//
.interestOnLoanAccountId(accountTypeResolver.resolve(DefaultAccountType.INTEREST_INCOME))//
.incomeFromFeeAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_INCOME))//
.incomeFromPenaltyAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_INCOME))//
.incomeFromRecoveryAccountId(accountTypeResolver.resolve(DefaultAccountType.RECOVERIES))//
.writeOffAccountId(accountTypeResolver.resolve(DefaultAccountType.WRITTEN_OFF))//
.overpaymentLiabilityAccountId(accountTypeResolver.resolve(DefaultAccountType.OVERPAYMENT_ACCOUNT))//
.receivableInterestAccountId(accountTypeResolver.resolve(DefaultAccountType.INTEREST_FEE_RECEIVABLE))//
.receivableFeeAccountId(accountTypeResolver.resolve(DefaultAccountType.INTEREST_FEE_RECEIVABLE))//
.receivablePenaltyAccountId(accountTypeResolver.resolve(DefaultAccountType.INTEREST_FEE_RECEIVABLE))//
.dateFormat(DATE_FORMAT)//
.locale(LOCALE_EN)//
.disallowExpectedDisbursements(true)//
.allowApprovedDisbursedAmountsOverApplied(true)//
.overAppliedCalculationType(OVER_APPLIED_CALCULATION_TYPE)//
.overAppliedNumber(OVER_APPLIED_NUMBER)//
.delinquencyBucketId(DELINQUENCY_BUCKET_ID.longValue())//
.goodwillCreditAccountId(accountTypeResolver.resolve(DefaultAccountType.GOODWILL_EXPENSE_ACCOUNT))//
.incomeFromGoodwillCreditInterestAccountId(accountTypeResolver.resolve(DefaultAccountType.INTEREST_INCOME_CHARGE_OFF))//
.incomeFromGoodwillCreditFeesAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_CHARGE_OFF))//
.incomeFromGoodwillCreditPenaltyAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_CHARGE_OFF))//
.paymentChannelToFundSourceMappings(paymentChannelToFundSourceMappings)//
.penaltyToIncomeAccountMappings(penaltyToIncomeAccountMappings)//
.feeToIncomeAccountMappings(feeToIncomeAccountMappings)//
.incomeFromChargeOffInterestAccountId(accountTypeResolver.resolve(DefaultAccountType.INTEREST_INCOME_CHARGE_OFF))//
.incomeFromChargeOffFeesAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_CHARGE_OFF))//
.chargeOffExpenseAccountId(accountTypeResolver.resolve(DefaultAccountType.CREDIT_LOSS_BAD_DEBT))//
.chargeOffFraudExpenseAccountId(accountTypeResolver.resolve(DefaultAccountType.CREDIT_LOSS_BAD_DEBT_FRAUD))//
.incomeFromChargeOffPenaltyAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_CHARGE_OFF));//
}
public PostLoanProductsRequest defaultLoanProductsRequestLP2InterestFlat() {
String name = Utils.randomNameGenerator(NAME_PREFIX_INTEREST_FLAT_LP2, 4);
String shortName = Utils.randomNameGenerator(SHORT_NAME_PREFIX_INTEREST, 3);
@@ -1136,5 +1110,4 @@ public class LoanProductsRequestFactory {
.chargeOffFraudExpenseAccountId(accountTypeResolver.resolve(DefaultAccountType.CREDIT_LOSS_BAD_DEBT_FRAUD))//
.incomeFromChargeOffPenaltyAccountId(accountTypeResolver.resolve(DefaultAccountType.FEE_CHARGE_OFF));//
}
}
@@ -19,8 +19,6 @@
package org.apache.fineract.test.factory;
import java.math.BigDecimal;
import java.time.Clock;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import lombok.RequiredArgsConstructor;
@@ -39,6 +37,7 @@ import org.apache.fineract.test.data.RepaymentFrequencyType;
import org.apache.fineract.test.data.TransactionProcessingStrategyCode;
import org.apache.fineract.test.data.loanproduct.DefaultLoanProduct;
import org.apache.fineract.test.data.loanproduct.LoanProductResolver;
import org.apache.fineract.test.helper.Utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -79,8 +78,10 @@ public class LoanRequestFactory {
public static final String DEFAULT_TRANSACTION_PROCESSING_STRATEGY_CODE = TransactionProcessingStrategyCode.PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER.value;
public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT);
public static final String DATE_SUBMIT_STRING = FORMATTER.format(LocalDate.now(Clock.systemUTC()).minusMonths(1L));
public static final String DEFAULT_TRANSACTION_DATE = FORMATTER.format(LocalDate.now(Clock.systemUTC()).minusMonths(1L));
public static final String DATE_SUBMIT_STRING = FORMATTER.format(Utils.now().minusMonths(1L));
public static final String DATE_REJECT_STRING = FORMATTER.format(Utils.now().minusMonths(1L));
public static final String DATE_WITHDRAWN_STRING = FORMATTER.format(Utils.now().minusMonths(1L));
public static final String DEFAULT_TRANSACTION_DATE = FORMATTER.format(Utils.now().minusMonths(1L));
public PostLoansRequest defaultLoansRequest(Long clientId) {
return new PostLoansRequest()//
@@ -108,7 +109,7 @@ public class LoanRequestFactory {
public PutLoansLoanIdRequest modifySubmittedOnDateOnLoan(Long clientId, String newSubmittedOnDate) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
String dateDisburseStr = formatter.format(LocalDate.now(Clock.systemUTC()));
String dateDisburseStr = formatter.format(Utils.now());
return new PutLoansLoanIdRequest()//
.productId(loanProductResolver.resolve(DEFAULT_LOAN_PRODUCT))//
@@ -162,6 +163,20 @@ public class LoanRequestFactory {
.locale(DEFAULT_LOCALE);//
}
public static PostLoansLoanIdRequest defaultLoanRejectRequest() {
return new PostLoansLoanIdRequest()//
.rejectedOnDate(DATE_REJECT_STRING)//
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
}
public static PostLoansLoanIdRequest defaultLoanWithdrawnRequest() {
return new PostLoansLoanIdRequest()//
.withdrawnOnDate(DATE_WITHDRAWN_STRING)//
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
}
public static PostLoansLoanIdRequest defaultLoanDisburseRequest() {
return new PostLoansLoanIdRequest().actualDisbursementDate(DATE_SUBMIT_STRING).transactionAmount(DEFAULT_DISBURSED_AMOUNT)
.paymentTypeId(Math.toIntExact(DEFAULT_PAYMENT_TYPE_ID)).dateFormat(DATE_FORMAT).locale(DEFAULT_LOCALE);
@@ -0,0 +1,91 @@
/**
* 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.factory;
import java.math.BigDecimal;
import org.apache.fineract.client.models.PostSavingsAccountTransactionsRequest;
import org.apache.fineract.client.models.PostSavingsAccountsAccountIdRequest;
import org.apache.fineract.client.models.PostSavingsAccountsRequest;
public final class SavingsAccountRequestFactory {
public static final String DATE_FORMAT = "dd MMMM yyyy";
public static final String DEFAULT_LOCALE = "en";
public static final String DEFAULT_TRANSACTION_DATE = "";
public static final Integer DEFAULT_CLIENT_ID = 1;
public static final String DEFAULT_SUBMITTED_ON_DATE = "";
public static final String DEFAULT_APPROVED_ON_DATE = "";
public static final String DEFAULT_ACTIVATED_ON_DATE = "";
public static final BigDecimal DEFAULT_REPAYMENT_TRANSACTION_AMOUNT = new BigDecimal(1);
public static final Integer DEFAULT_PAYMENT_TYPE_ID = 2;
public static final Integer EUR_SAVING_PRODUCT_ID = 1;
public static final Integer USD_SAVING_PRODUCT_ID = 2;
private SavingsAccountRequestFactory() {}
public static PostSavingsAccountsRequest defaultEURSavingsAccountRequest() {
return new PostSavingsAccountsRequest()//
.clientId(DEFAULT_CLIENT_ID)//
.dateFormat(DATE_FORMAT)//
.productId(EUR_SAVING_PRODUCT_ID)//
.submittedOnDate(DEFAULT_SUBMITTED_ON_DATE)//
.locale(DEFAULT_LOCALE);//
}
public static PostSavingsAccountsRequest defaultUSDSavingsAccountRequest() {
return new PostSavingsAccountsRequest()//
.clientId(DEFAULT_CLIENT_ID)//
.dateFormat(DATE_FORMAT)//
.productId(USD_SAVING_PRODUCT_ID)//
.submittedOnDate(DEFAULT_SUBMITTED_ON_DATE)//
.locale(DEFAULT_LOCALE);//
}
public static PostSavingsAccountsAccountIdRequest defaultApproveRequest() {
return new PostSavingsAccountsAccountIdRequest()//
.approvedOnDate(DEFAULT_APPROVED_ON_DATE)//
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
}
public static PostSavingsAccountsAccountIdRequest defaultActivateRequest() {
return new PostSavingsAccountsAccountIdRequest()//
.activatedOnDate(DEFAULT_ACTIVATED_ON_DATE)//
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
}
public static PostSavingsAccountTransactionsRequest defaultDepositRequest() {
return new PostSavingsAccountTransactionsRequest()//
.transactionDate(DEFAULT_TRANSACTION_DATE)//
.transactionAmount(DEFAULT_REPAYMENT_TRANSACTION_AMOUNT)//
.paymentTypeId(DEFAULT_PAYMENT_TYPE_ID)//
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
}
public static PostSavingsAccountTransactionsRequest defaultWithdrawRequest() {
return new PostSavingsAccountTransactionsRequest()//
.transactionDate(DEFAULT_TRANSACTION_DATE)//
.transactionAmount(DEFAULT_REPAYMENT_TRANSACTION_AMOUNT)//
.paymentTypeId(DEFAULT_PAYMENT_TYPE_ID)//
.dateFormat(DATE_FORMAT)//
.locale(DEFAULT_LOCALE);//
}
}
@@ -0,0 +1,63 @@
/**
* 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.factory;
import java.util.HashSet;
import java.util.Set;
import org.apache.fineract.client.models.PostSavingsCharges;
import org.apache.fineract.client.models.PostSavingsProductsRequest;
public final class SavingsProductRequestFactory {
public static final String DEFAULT_SAVINGS_PRODUCT_NAME = "CEUR";
public static final String DEFAULT_SAVINGS_PRODUCT_SHORT_NAME = "CEU";
public static final String DEFAULT_SAVINGS_PRODUCT_DESCRIPTION = "";
public static final String DEFAULT_SAVINGS_PRODUCT_CURRENCY_CODE = "EUR";
public static final Integer DEFAULT_SAVINGS_PRODUCT_DIGITS_AFTER_DECIMAL = 2;
public static final Integer DEFAULT_SAVINGS_PRODUCT_IN_MULTIPLES_OF = 0;
public static final Double DEFAULT_SAVINGS_PRODUCT_NOMINAL_ANNUAL_INTEREST_RATE = 0.0;
public static final Integer DEFAULT_SAVINGS_PRODUCT_INTEREST_COMPOUNDING_PERIOD_TIME = 1;
public static final Integer DEFAULT_SAVINGS_PRODUCT_INTEREST_POSTING_PERIOD_TIME = 4;
public static final Integer DEFAULT_SAVINGS_PRODUCT_INTEREST_CALCULATION_TYPE = 1;
public static final Integer DEFAULT_SAVINGS_PRODUCT_INTEREST_CALCULATION_DAYS_IN_YEAR_TYPE = 365;
public static final Integer DEFAULT_SAVINGS_PRODUCT_ACCOUNTING_RULE = 1;
public static final String LOCALE_EN = "en";
private SavingsProductRequestFactory() {}
public static PostSavingsProductsRequest defaultSavingsProductRequest() {
Set<PostSavingsCharges> charges = new HashSet<>();
return new PostSavingsProductsRequest().name(DEFAULT_SAVINGS_PRODUCT_NAME)//
.shortName(DEFAULT_SAVINGS_PRODUCT_SHORT_NAME)//
.description(DEFAULT_SAVINGS_PRODUCT_DESCRIPTION)//
.currencyCode(DEFAULT_SAVINGS_PRODUCT_CURRENCY_CODE)//
.digitsAfterDecimal(DEFAULT_SAVINGS_PRODUCT_DIGITS_AFTER_DECIMAL)//
.inMultiplesOf(DEFAULT_SAVINGS_PRODUCT_IN_MULTIPLES_OF)//
.nominalAnnualInterestRate(DEFAULT_SAVINGS_PRODUCT_NOMINAL_ANNUAL_INTEREST_RATE)//
.interestCompoundingPeriodType(DEFAULT_SAVINGS_PRODUCT_INTEREST_COMPOUNDING_PERIOD_TIME)//
.interestPostingPeriodType(DEFAULT_SAVINGS_PRODUCT_INTEREST_POSTING_PERIOD_TIME)//
.interestCalculationType(DEFAULT_SAVINGS_PRODUCT_INTEREST_CALCULATION_TYPE)//
.interestCalculationDaysInYearType(DEFAULT_SAVINGS_PRODUCT_INTEREST_CALCULATION_DAYS_IN_YEAR_TYPE)//
.charges(charges)//
.accountingRule(DEFAULT_SAVINGS_PRODUCT_ACCOUNTING_RULE)//
.locale(LOCALE_EN);//
}
}
@@ -19,8 +19,6 @@
package org.apache.fineract.test.helper;
import java.io.IOException;
import java.time.Clock;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.client.models.BusinessDateRequest;
@@ -53,7 +51,7 @@ public class BusinessDateHelper {
public void setBusinessDateToday() throws IOException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
String today = formatter.format(LocalDate.now(Clock.systemUTC()));
String today = formatter.format(Utils.now());
setBusinessDate(today);
}
@@ -24,7 +24,6 @@ import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.client.models.BatchResponse;
import org.apache.fineract.client.models.GetJournalEntriesTransactionIdResponse;
@@ -63,14 +62,14 @@ public final class ErrorMessageHelper {
return String.format("The date on which a loan with identifier : %s is disbursed cannot be in the future.", loanIdStr);
}
public static String disburseMaxAmountFailure() {
return "Loan disbursal amount can't be greater than maximum applied loan amount calculation. Total disbursed amount: [0-9]* Maximum disbursal amount: [0-9]*";
}
public static String disbursePastDateFailure(Integer loanId, String actualDisbursementDate) {
return String.format("The date on which a loan is disbursed cannot be before its approval date: %s", actualDisbursementDate);
}
public static String disburseMaxAmountFailure() {
return "Loan disbursal amount can't be greater than maximum applied loan amount calculation. Total disbursed amount: [0-9]* Maximum disbursal amount: [0-9]*";
}
public static String loanSubmitDateInFutureFailureMsg() {
return "The date on which a loan is submitted cannot be in the future.";
}
@@ -787,6 +786,26 @@ public final class ErrorMessageHelper {
sb.toString(), expectedStr);
}
public static String wrongValueInLineInLoanTermVariations(int line, List<List<String>> actual, List<String> expected) {
String lineStr = String.valueOf(line);
String expectedStr = expected.toString();
StringBuilder sb = new StringBuilder();
for (List<String> innerList : actual) {
sb.append(innerList.toString());
sb.append(System.lineSeparator());
}
return String.format("%nWrong value in Loan Term Variations line %s. %nActual values in line: %s %nExpected values in line: %s",
lineStr, sb.toString(), expectedStr);
}
public static String wrongNumberOfLinesInLoanTermVariations(int actual, int expected) {
String actualStr = String.valueOf(actual);
String expectedStr = String.valueOf(expected);
return String.format("Number of items (lines) in Loan Term Variations is not correct. Actual value is: %s - Expected value is: %s",
actualStr, expectedStr);
}
public static String nrOfLinesWrongInLoanDelinquencyPauseData(int actual, int expected) {
String actualStr = String.valueOf(actual);
String expectedStr = String.valueOf(expected);
@@ -866,17 +885,7 @@ public final class ErrorMessageHelper {
expectedToStr);
}
public static String wrongValueInLineInLoanTermVariations(final int line, final List<List<String>> actual,
final List<String> expected) {
final String actualValues = actual.stream().map(List::toString).collect(Collectors.joining(System.lineSeparator()));
return String.format(
"%nWrong value in loan term variations tab line %d.%nActual values in line (with the same term variation applicable from) are:%n%s%nExpected values in line:%n%s",
line, actualValues, expected);
}
public static String wrongNumberOfLinesInLoanTermVariations(final int actual, final int expected) {
return String.format("Number of lines in loan term variations is not correct. Actual value is: %d - Expected value is: %d", actual,
expected);
public static String downpaymentDisabledOnProductErrorCodeMsg() {
return "The Loan can not override the downpayment properties because in the Loan Product the downpayment is disabled";
}
}
@@ -19,6 +19,8 @@
package org.apache.fineract.test.helper;
import java.security.SecureRandom;
import java.time.Clock;
import java.time.LocalDate;
public final class Utils {
@@ -42,4 +44,8 @@ public final class Utils {
public static String randomNameGenerator(final String prefix, final int lenOfRandomSuffix) {
return randomStringGenerator(prefix, lenOfRandomSuffix);
}
public static LocalDate now() {
return LocalDate.now(Clock.systemUTC());
}
}
@@ -0,0 +1,49 @@
/**
* 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.helper;
import java.io.IOException;
import java.util.Comparator;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.client.models.BusinessStep;
import org.apache.fineract.client.models.GetBusinessStepConfigResponse;
import org.apache.fineract.client.services.BusinessStepConfigurationApi;
import org.apache.fineract.test.support.TestContext;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.stereotype.Component;
import retrofit2.Response;
@RequiredArgsConstructor
@Component
public class WorkFlowJobHelper {
private static final String WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS = "LOAN_CLOSE_OF_BUSINESS";
private final BusinessStepConfigurationApi businessStepConfigurationApi;
public void saveOriginalCOBWorkflowJobBusinessStepList() throws IOException {
Response<GetBusinessStepConfigResponse> businessStepConfigResponse = businessStepConfigurationApi
.retrieveAllConfiguredBusinessStep(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS).execute();
ErrorHelper.checkSuccessfulApiCall(businessStepConfigResponse);
List<BusinessStep> businessSteps = businessStepConfigResponse.body().getBusinessSteps();
businessSteps.sort(Comparator.comparingLong(BusinessStep::getOrder));
TestContext.GLOBAL.set(TestContextKey.ORIGINAL_COB_WORKFLOW_JOB_BUSINESS_STEP_LIST, businessSteps);
}
}
@@ -21,6 +21,7 @@ package org.apache.fineract.test.initializer.base;
import java.util.List;
import org.apache.fineract.test.config.CacheConfiguration;
import org.apache.fineract.test.helper.BusinessDateHelper;
import org.apache.fineract.test.helper.WorkFlowJobHelper;
import org.apache.fineract.test.initializer.global.FineractGlobalInitializerStep;
import org.apache.fineract.test.initializer.scenario.FineractScenarioInitializerStep;
import org.apache.fineract.test.initializer.suite.FineractSuiteInitializerStep;
@@ -39,7 +40,8 @@ public class BaseFineractInitializerConfiguration {
@Bean
public FineractInitializer fineractInitializer(List<FineractGlobalInitializerStep> globalInitializerSteps,
List<FineractSuiteInitializerStep> suiteInitializerSteps, List<FineractScenarioInitializerStep> scenarioInitializerSteps,
BusinessDateHelper businessDateHelper) {
return new FineractInitializer(globalInitializerSteps, suiteInitializerSteps, scenarioInitializerSteps, businessDateHelper);
BusinessDateHelper businessDateHelper, WorkFlowJobHelper workFlowJobHelper) {
return new FineractInitializer(globalInitializerSteps, suiteInitializerSteps, scenarioInitializerSteps, businessDateHelper,
workFlowJobHelper);
}
}
@@ -23,6 +23,7 @@ import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.test.helper.BusinessDateHelper;
import org.apache.fineract.test.helper.WorkFlowJobHelper;
import org.apache.fineract.test.initializer.global.FineractGlobalInitializerStep;
import org.apache.fineract.test.initializer.scenario.FineractScenarioInitializerStep;
import org.apache.fineract.test.initializer.suite.FineractSuiteInitializerStep;
@@ -38,6 +39,7 @@ public class FineractInitializer implements InitializingBean {
private final List<FineractSuiteInitializerStep> suiteInitializerSteps;
private final List<FineractScenarioInitializerStep> scenarioInitializerSteps;
private final BusinessDateHelper businessDateHelper;
private final WorkFlowJobHelper workFlowJobHelper;
@Override
public void afterPropertiesSet() throws Exception {
@@ -71,6 +73,7 @@ public class FineractInitializer implements InitializingBean {
}
businessDateHelper.setBusinessDateToday();
workFlowJobHelper.saveOriginalCOBWorkflowJobBusinessStepList();
}
public void setupDefaultsForScenario() throws Exception {
@@ -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.initializer.global;
import java.util.Comparator;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.client.models.BusinessStep;
import org.apache.fineract.client.models.GetBusinessStepConfigResponse;
import org.apache.fineract.client.models.UpdateBusinessStepConfigRequest;
import org.apache.fineract.client.services.BusinessStepConfigurationApi;
import org.apache.fineract.test.helper.ErrorHelper;
import org.springframework.stereotype.Component;
import retrofit2.Response;
@RequiredArgsConstructor
@Component
public class CobBusinessStepInitializerStep implements FineractGlobalInitializerStep {
private static final String WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS = "LOAN_CLOSE_OF_BUSINESS";
private static final String BUSINESS_STEP_NAME_ACCRUAL_ACTIVITY_POSTING = "ACCRUAL_ACTIVITY_POSTING";
private final BusinessStepConfigurationApi businessStepConfigurationApi;
@Override
public void initialize() throws Exception {
// --- Adding ACCRUAL_ACTIVITY_POSTING to default COB steps ---
Response<GetBusinessStepConfigResponse> businessStepConfigResponse = businessStepConfigurationApi
.retrieveAllConfiguredBusinessStep(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS).execute();
ErrorHelper.checkSuccessfulApiCall(businessStepConfigResponse);
List<BusinessStep> businessSteps = businessStepConfigResponse.body().getBusinessSteps();
businessSteps.sort(Comparator.comparingLong(BusinessStep::getOrder));
Long lastOrder = businessSteps.get(businessSteps.size() - 1).getOrder();
BusinessStep accrualActivityPosting = new BusinessStep().stepName(BUSINESS_STEP_NAME_ACCRUAL_ACTIVITY_POSTING).order(lastOrder + 1);
businessSteps.add(accrualActivityPosting);
UpdateBusinessStepConfigRequest request = new UpdateBusinessStepConfigRequest().businessSteps(businessSteps);
Response<Void> response = businessStepConfigurationApi.updateJobBusinessStepConfig(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS, request)
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
}
}
@@ -19,7 +19,9 @@
package org.apache.fineract.test.initializer.global;
import static org.apache.fineract.test.data.TransactionProcessingStrategyCode.ADVANCED_PAYMENT_ALLOCATION;
import static org.apache.fineract.test.factory.LoanProductsRequestFactory.INTEREST_RATE_FREQUENCY_TYPE_WHOLE_TERM;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -479,13 +481,243 @@ public class LoanProductGlobalInitializerStep implements FineractGlobalInitializ
TestContext.INSTANCE.set(TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_ADV_PMT_ALLOC_FIXED_LENGTH,
responseLoanProductsRequestDownPaymentAdvPmtAllocFixedLength);
// LP2 with Down-payment+autopayment + advanced payment allocation + repayment start date SUBMITTED ON DATE
// (LP2_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION_REPAYMENT_START_SUBMITTED)
String name26 = DefaultLoanProduct.LP2_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION_REPAYMENT_START_SUBMITTED.getName();
PostLoanProductsRequest loanProductsRequestDownPaymentAutoAdvPaymentAllocationRepaymentStartSubmitted = loanProductsRequestFactory
.defaultLoanProductsRequestLP2()//
.name(name26)//
.transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION.getValue())//
.loanScheduleType("PROGRESSIVE") //
.repaymentStartDateType(2)//
.paymentAllocation(List.of(//
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT"), //
createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT"), //
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "REAMORTIZATION"), //
createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT")));//
Response<PostLoanProductsResponse> responseLoanProductsRequestDownPaymentAutoAdvPaymentAllocationRepaymentStartSubmitted = loanProductsApi
.createLoanProduct(loanProductsRequestDownPaymentAutoAdvPaymentAllocationRepaymentStartSubmitted).execute();
TestContext.INSTANCE.set(
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_AUTO_ADVANCED_REPAYMENT_ALLOCATION_PAYMENT_START_SUBMITTED,
responseLoanProductsRequestDownPaymentAutoAdvPaymentAllocationRepaymentStartSubmitted);
// LP2 with Down-payment + advanced payment allocation + progressive loan schedule + horizontal + interest Flat
// (LP2_DOWNPAYMENT_INTEREST_FLAT_ADV_PMT_ALLOC)
String name27 = DefaultLoanProduct.LP2_DOWNPAYMENT_INTEREST_FLAT_ADV_PMT_ALLOC.getName();
PostLoanProductsRequest loanProductsRequestDownPaymentAdvPaymentAllocationInterestFlat = loanProductsRequestFactory
.defaultLoanProductsRequestLP2InterestFlat()//
.name(name27)//
.transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION.getValue())//
.loanScheduleType("PROGRESSIVE") //
.loanScheduleProcessingType("HORIZONTAL")//
.enableAutoRepaymentForDownPayment(false)//
.installmentAmountInMultiplesOf(null)//
.paymentAllocation(List.of(//
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT"), //
createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT"), //
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "REAMORTIZATION"), //
createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT")));//
Response<PostLoanProductsResponse> responseLoanProductsRequestDownPaymentAdvPaymentAllocationInterestFlat = loanProductsApi
.createLoanProduct(loanProductsRequestDownPaymentAdvPaymentAllocationInterestFlat).execute();
TestContext.INSTANCE.set(TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_INTEREST_FLAT_ADV_PMT_ALLOC,
responseLoanProductsRequestDownPaymentAdvPaymentAllocationInterestFlat);
// LP2 with progressive loan schedule + horizontal + interest EMI + actual/actual
// (LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL)
String name28 = DefaultLoanProduct.LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL.getName();
PostLoanProductsRequest loanProductsRequestLP2AdvancedpaymentInterestEmiActualActual = loanProductsRequestFactory
.defaultLoanProductsRequestLP2Emi()//
.name(name28)//
.paymentAllocation(List.of(//
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT"), //
createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT"), //
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "REAMORTIZATION"), //
createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT")));//
Response<PostLoanProductsResponse> responseLoanProductsRequestLP2AdvancedpaymentInterestEmiActualActual = loanProductsApi
.createLoanProduct(loanProductsRequestLP2AdvancedpaymentInterestEmiActualActual).execute();
TestContext.INSTANCE.set(TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL,
responseLoanProductsRequestLP2AdvancedpaymentInterestEmiActualActual);
// LP2 with progressive loan schedule + horizontal + interest EMI + 360/30
// (LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30)
String name29 = DefaultLoanProduct.LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30.getName();
PostLoanProductsRequest loanProductsRequestLP2AdvancedpaymentInterestEmi36030 = loanProductsRequestFactory
.defaultLoanProductsRequestLP2Emi()//
.name(name29)//
.daysInYearType(DaysInYearType.DAYS360.value)//
.daysInMonthType(DaysInMonthType.DAYS30.value)//
.paymentAllocation(List.of(//
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT"), //
createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT"), //
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "REAMORTIZATION"), //
createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT")));//
Response<PostLoanProductsResponse> responseLoanProductsRequestLP2AdvancedpaymentInterest36030 = loanProductsApi
.createLoanProduct(loanProductsRequestLP2AdvancedpaymentInterestEmi36030).execute();
TestContext.INSTANCE.set(TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30,
responseLoanProductsRequestLP2AdvancedpaymentInterest36030);
// LP2 with progressive loan schedule + horizontal + interest EMI + 360/30 + multidisbursement
// (LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_MULTIDISBURSE)
String name36 = DefaultLoanProduct.LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_MULTIDISBURSE.getName();
PostLoanProductsRequest loanProductsRequestLP2AdvancedpaymentInterestEmi36030MultiDisburse = loanProductsRequestFactory
.defaultLoanProductsRequestLP2Emi()//
.name(name36)//
.daysInYearType(DaysInYearType.DAYS360.value)//
.daysInMonthType(DaysInMonthType.DAYS30.value)//
.paymentAllocation(List.of(//
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT"), //
createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT"), //
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "REAMORTIZATION"), //
createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT")))//
.multiDisburseLoan(true)//
.disallowExpectedDisbursements(true)//
.maxTrancheCount(10)//
.outstandingLoanBalance(10000.0);//
Response<PostLoanProductsResponse> responseLoanProductsRequestLP2AdvancedpaymentInterest36030MultiDisburse = loanProductsApi
.createLoanProduct(loanProductsRequestLP2AdvancedpaymentInterestEmi36030MultiDisburse).execute();
TestContext.INSTANCE.set(TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_MULTIDISBURSE,
responseLoanProductsRequestLP2AdvancedpaymentInterest36030MultiDisburse);
// LP2 with progressive loan schedule + horizontal + interest EMI + 360/30 + multidisbursement + downpayment
// 25%, auto disabled
// (LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_MULTIDISBURSE_DOWNPAYMENT)
String name37 = DefaultLoanProduct.LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_MULTIDISBURSE_DOWNPAYMENT.getName();
PostLoanProductsRequest loanProductsRequestLP2AdvancedpaymentInterestEmi36030MultiDisburseDownPayment = loanProductsRequestFactory
.defaultLoanProductsRequestLP2Emi()//
.name(name37)//
.enableDownPayment(true)//
.disbursedAmountPercentageForDownPayment(new BigDecimal(25))//
.daysInYearType(DaysInYearType.DAYS360.value)//
.daysInMonthType(DaysInMonthType.DAYS30.value)//
.paymentAllocation(List.of(//
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT"), //
createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT"), //
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "REAMORTIZATION"), //
createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT")))//
.multiDisburseLoan(true)//
.disallowExpectedDisbursements(true)//
.maxTrancheCount(10)//
.outstandingLoanBalance(10000.0);//
Response<PostLoanProductsResponse> responseLoanProductsRequestLP2AdvancedpaymentInterest36030MultiDisburseDownPayment = loanProductsApi
.createLoanProduct(loanProductsRequestLP2AdvancedpaymentInterestEmi36030MultiDisburseDownPayment).execute();
TestContext.INSTANCE.set(
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_MULTIDISBURSE_DOWNPAYMENT,
responseLoanProductsRequestLP2AdvancedpaymentInterest36030MultiDisburseDownPayment);
// LP2 with progressive loan schedule + horizontal + interest EMI + 365/Actual
// (LP2_ADV_PYMNT_INTEREST_DAILY_EMI_365_ACTUAL)
String name30 = DefaultLoanProduct.LP2_ADV_PYMNT_INTEREST_DAILY_EMI_365_ACTUAL.getName();
PostLoanProductsRequest loanProductsRequestLP2AdvancedpaymentInterest365Actual = loanProductsRequestFactory
.defaultLoanProductsRequestLP2Emi()//
.name(name30)//
.daysInYearType(DaysInYearType.DAYS365.value)//
.daysInMonthType(DaysInMonthType.ACTUAL.value)//
.paymentAllocation(List.of(//
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT"), //
createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT"), //
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "REAMORTIZATION"), //
createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT")));//
Response<PostLoanProductsResponse> responseLoanProductsRequestLP2AdvancedpaymentInterestEmi365Actual = loanProductsApi
.createLoanProduct(loanProductsRequestLP2AdvancedpaymentInterest365Actual).execute();
TestContext.INSTANCE.set(TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_365_ACTUAL,
responseLoanProductsRequestLP2AdvancedpaymentInterestEmi365Actual);
// LP2 with progressive loan schedule + horizontal + interest EMI + 360/30 + downpayment 25%
// (LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_DOWNPAYMENT)
String name31 = DefaultLoanProduct.LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_DOWNPAYMENT.getName();
PostLoanProductsRequest loanProductsRequestLP2AdvancedpaymentInterest36030Downpayment = loanProductsRequestFactory
.defaultLoanProductsRequestLP2Emi()//
.name(name31)//
.enableDownPayment(true)//
.disbursedAmountPercentageForDownPayment(new BigDecimal(25))//
.daysInYearType(DaysInYearType.DAYS360.value)//
.daysInMonthType(DaysInMonthType.DAYS30.value)//
.paymentAllocation(List.of(//
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT"), //
createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT"), //
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "REAMORTIZATION"), //
createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT")));//
Response<PostLoanProductsResponse> responseLoanProductsRequestLP2AdvancedpaymentInterestEmi36030Downpayment = loanProductsApi
.createLoanProduct(loanProductsRequestLP2AdvancedpaymentInterest36030Downpayment).execute();
TestContext.INSTANCE.set(TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_DOWNPAYMENT,
responseLoanProductsRequestLP2AdvancedpaymentInterestEmi36030Downpayment);
// LP2 with progressive loan schedule + horizontal + interest EMI + actual/actual +
// enableAccrualActivityPosting
// (LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_ACCRUAL_ACTIVITY)
String name32 = DefaultLoanProduct.LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_ACCRUAL_ACTIVITY.getName();
PostLoanProductsRequest loanProductsRequestLP2AdvancedpaymentInterestEmiActualActualAccrualActivity = loanProductsRequestFactory
.defaultLoanProductsRequestLP2Emi()//
.name(name32)//
.enableAccrualActivityPosting(true)//
.paymentAllocation(List.of(//
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT"), //
createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT"), //
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "REAMORTIZATION"), //
createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT")));//
Response<PostLoanProductsResponse> responseLoanProductsRequestLP2AdvancedpaymentInterestEmiActualActualAccrualActivity = loanProductsApi
.createLoanProduct(loanProductsRequestLP2AdvancedpaymentInterestEmiActualActualAccrualActivity).execute();
TestContext.INSTANCE.set(TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL,
responseLoanProductsRequestLP2AdvancedpaymentInterestEmiActualActualAccrualActivity);
// LP1 with 12% DECLINING BALANCE interest, interest period: Daily + enableAccrualActivityPosting
// (LP1_INTEREST_DECLINING_BALANCE_PERIOD_DAILY_ACCRUAL_ACTIVITY)
String name33 = DefaultLoanProduct.LP1_INTEREST_DECLINING_BALANCE_PERIOD_DAILY_ACCRUAL_ACTIVITY.getName();
PostLoanProductsRequest loanProductsRequestInterestDecliningPeriodDailyAccrualActivity = loanProductsRequestFactory
.defaultLoanProductsRequestLP1InterestDeclining()//
.name(name33)//
.enableAccrualActivityPosting(true)//
.interestCalculationPeriodType(InterestCalculationPeriodTime.DAILY.value)//
.allowPartialPeriodInterestCalcualtion(false);//
Response<PostLoanProductsResponse> responseInterestDecliningPeriodDailyAccrualActivity = loanProductsApi
.createLoanProduct(loanProductsRequestInterestDecliningPeriodDailyAccrualActivity).execute();
TestContext.INSTANCE.set(TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_INTEREST_DECLINING_PERIOD_DAILY_ACCRUAL_ACTIVITY,
responseInterestDecliningPeriodDailyAccrualActivity);
// LP1 with 12% DECLINING BALANCE interest, interest period: Daily, Interest
// recalculation-Daily, Compounding:none + enableAccrualActivityPosting
// (LP1_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_NONE_ACCRUAL_ACTIVITY)
String name34 = DefaultLoanProduct.LP1_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_NONE_ACCRUAL_ACTIVITY.getName();
PostLoanProductsRequest loanProductsRequestLP1InterestDecliningBalanceDailyRecalculationCompoundingNoneAccrualActivity = loanProductsRequestFactory
.defaultLoanProductsRequestLP1InterestDecliningBalanceDailyRecalculationCompoundingNone()//
.name(name34)//
.enableAccrualActivityPosting(true)//
.interestCalculationPeriodType(InterestCalculationPeriodTime.DAILY.value)//
.allowPartialPeriodInterestCalcualtion(false);//
Response<PostLoanProductsResponse> responseLP1InterestDecliningBalanceDailyRecalculationCompoundingNoneAccrualActivity = loanProductsApi
.createLoanProduct(loanProductsRequestLP1InterestDecliningBalanceDailyRecalculationCompoundingNoneAccrualActivity)
.execute();
TestContext.INSTANCE.set(
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_NONE_ACCRUAL_ACTIVITY,
responseLP1InterestDecliningBalanceDailyRecalculationCompoundingNoneAccrualActivity);
// LP2 with progressive loan schedule + horizontal + interest EMI + actual/actual + interest refund with
// Merchant issued and Payment refund
// (LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_INTEREST_REFUND)
String name35 = DefaultLoanProduct.LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_INTEREST_REFUND.getName();
List<String> supportedInterestRefundTypes = Arrays.asList("MERCHANT_ISSUED_REFUND", "PAYOUT_REFUND");
PostLoanProductsRequest loanProductsRequestLP2AdvancedpaymentInterestEmiActualActualInterestRefund = loanProductsRequestFactory
.defaultLoanProductsRequestLP2Emi()//
.name(name35)//
.supportedInterestRefundTypes(supportedInterestRefundTypes).paymentAllocation(List.of(//
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT"), //
createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT"), //
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "REAMORTIZATION"), //
createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT"), //
createPaymentAllocation("INTEREST_REFUND", "NEXT_INSTALLMENT")));//
Response<PostLoanProductsResponse> responseLoanProductsRequestLP2AdvancedpaymentInterestEmiActualActualInterestRefund = loanProductsApi
.createLoanProduct(loanProductsRequestLP2AdvancedpaymentInterestEmiActualActualInterestRefund).execute();
TestContext.INSTANCE.set(
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_INTEREST_REFUND,
responseLoanProductsRequestLP2AdvancedpaymentInterestEmiActualActualInterestRefund);
// LP2 with progressive loan schedule + horizontal + interest EMI + 360/30
// + interest recalculation, preClosureInterestCalculationStrategy= till preclose,
// interestRecalculationCompoundingMethod = none
// Frequency for recalculate Outstanding Principal: Daily, Frequency Interval for recalculation: 1
// (LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_TILL_PRECLOSE)
String name38 = DefaultLoanProduct.LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_TILL_PRECLOSE.getName();
PostLoanProductsRequest loanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcTillPreclose = loanProductsRequestFactory
// (LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE)
String name38 = DefaultLoanProduct.LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE.getName();
PostLoanProductsRequest loanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcDailyTillPreclose = loanProductsRequestFactory
.defaultLoanProductsRequestLP2Emi()//
.name(name38)//
.daysInYearType(DaysInYearType.DAYS360.value)//
@@ -497,20 +729,24 @@ public class LoanProductGlobalInitializerStep implements FineractGlobalInitializ
.recalculationRestFrequencyType(2)//
.recalculationRestFrequencyInterval(1)//
.paymentAllocation(List.of(//
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT")));//
Response<PostLoanProductsResponse> responseLoanProductsRequestLP2AdvancedpaymentInterest36030InterestRecalcTillPreClose = loanProductsApi
.createLoanProduct(loanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcTillPreclose).execute();
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT"), //
createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT"), //
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "REAMORTIZATION"), //
createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT")));//
Response<PostLoanProductsResponse> responseLoanProductsRequestLP2AdvancedpaymentInterest36030InterestRecalcDailyTillPreCloese = loanProductsApi
.createLoanProduct(loanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcDailyTillPreclose).execute();
TestContext.INSTANCE.set(
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_TILL_PRECLOSE,
responseLoanProductsRequestLP2AdvancedpaymentInterest36030InterestRecalcTillPreClose);
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE,
responseLoanProductsRequestLP2AdvancedpaymentInterest36030InterestRecalcDailyTillPreCloese);
// LP2 with progressive loan schedule + horizontal + interest EMI + 360/30
// + interest recalculation, preClosureInterestCalculationStrategy= till rest frequency,
// + interest recalculation, preClosureInterestCalculationStrategy= till rest frequency date,
// interestRecalculationCompoundingMethod = none
// Frequency for recalculate Outstanding Principal: Daily, Frequency Interval for recalculation: 1
// (LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_TILL_REST_FREQUENCY)
String name39 = DefaultLoanProduct.LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_TILL_REST_FREQUENCY.getName();
PostLoanProductsRequest loanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcTillRestFrequency = loanProductsRequestFactory
// (LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_REST_FREQUENCY_DATE)
String name39 = DefaultLoanProduct.LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_REST_FREQUENCY_DATE
.getName();
PostLoanProductsRequest loanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcDailyTillRestFrequencyDate = loanProductsRequestFactory
.defaultLoanProductsRequestLP2Emi()//
.name(name39)//
.daysInYearType(DaysInYearType.DAYS360.value)//
@@ -526,16 +762,78 @@ public class LoanProductGlobalInitializerStep implements FineractGlobalInitializ
createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT"), //
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "REAMORTIZATION"), //
createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT")));//
Response<PostLoanProductsResponse> responseLoanProductsRequestLP2AdvancedpaymentInterest36030InterestRecalcTillRestFrequency = loanProductsApi
.createLoanProduct(loanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcTillRestFrequency).execute();
Response<PostLoanProductsResponse> responseLoanProductsRequestLP2AdvancedpaymentInterest36030InterestRecalcDailyTillRestFrequencyDate = loanProductsApi
.createLoanProduct(loanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcDailyTillRestFrequencyDate).execute();
TestContext.INSTANCE.set(
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_TILL_REST_FREQUENCY,
responseLoanProductsRequestLP2AdvancedpaymentInterest36030InterestRecalcTillRestFrequency);
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_REST_FREQUENCY_DATE,
responseLoanProductsRequestLP2AdvancedpaymentInterest36030InterestRecalcDailyTillRestFrequencyDate);
String name40 = DefaultLoanProduct.LP2_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL.getName();
PostLoanProductsRequest loanProductsRequestLP2AdvPmtAllocProgressiveLoanScheduleHorizontal = loanProductsRequestFactory
.defaultLoanProductsRequestLP2NoDown()//
// LP2 with progressive loan schedule + horizontal + interest EMI + 360/30
// + interest recalculation, preClosureInterestCalculationStrategy= till preclose,
// interestRecalculationCompoundingMethod = none
// Frequency for recalculate Outstanding Principal: Same as repayment period, Frequency Interval for
// recalculation: 1
// (LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_SARP_TILL_PRECLOSE)
String name40 = DefaultLoanProduct.LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_SARP_TILL_PRECLOSE.getName();
PostLoanProductsRequest loanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcSameAsRepTillPreclose = loanProductsRequestFactory
.defaultLoanProductsRequestLP2Emi()//
.name(name40)//
.daysInYearType(DaysInYearType.DAYS360.value)//
.daysInMonthType(DaysInMonthType.DAYS30.value)//
.isInterestRecalculationEnabled(true)//
.preClosureInterestCalculationStrategy(1)//
.rescheduleStrategyMethod(4)//
.interestRecalculationCompoundingMethod(0)//
.recalculationRestFrequencyType(1)//
.recalculationRestFrequencyInterval(1)//
.paymentAllocation(List.of(//
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT"), //
createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT"), //
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "REAMORTIZATION"), //
createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT")));//
Response<PostLoanProductsResponse> responseLoanProductsRequestLP2AdvancedpaymentInterest36030InterestRecalcSameAsRepTillPreCloese = loanProductsApi
.createLoanProduct(loanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcSameAsRepTillPreclose).execute();
TestContext.INSTANCE.set(
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_SAME_AS_REP_TILL_PRECLOSE,
responseLoanProductsRequestLP2AdvancedpaymentInterest36030InterestRecalcSameAsRepTillPreCloese);
// LP2 with progressive loan schedule + horizontal + interest EMI + 360/30
// + interest recalculation, preClosureInterestCalculationStrategy= till rest frequency date,
// interestRecalculationCompoundingMethod = none
// Frequency for recalculate Outstanding Principal: Same as repayment period, Frequency Interval for
// recalculation: 1
// (LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_SARP_TILL_REST_FREQUENCY_DATE)
String name41 = DefaultLoanProduct.LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_SARP_TILL_REST_FREQUENCY_DATE
.getName();
PostLoanProductsRequest loanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcSameAsRepTillRestFrequencyDate = loanProductsRequestFactory
.defaultLoanProductsRequestLP2Emi()//
.name(name41)//
.daysInYearType(DaysInYearType.DAYS360.value)//
.daysInMonthType(DaysInMonthType.DAYS30.value)//
.isInterestRecalculationEnabled(true)//
.preClosureInterestCalculationStrategy(2)//
.rescheduleStrategyMethod(4)//
.interestRecalculationCompoundingMethod(0)//
.recalculationRestFrequencyType(1)//
.recalculationRestFrequencyInterval(1)//
.paymentAllocation(List.of(//
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT"), //
createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT"), //
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "REAMORTIZATION"), //
createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT")));//
Response<PostLoanProductsResponse> responseLoanProductsRequestLP2AdvancedpaymentInterest36030InterestRecalcSameAsRepTillRestFrequencyDate = loanProductsApi
.createLoanProduct(loanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcSameAsRepTillRestFrequencyDate)
.execute();
TestContext.INSTANCE.set(
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_SAME_AS_REP_TILL_REST_FREQUENCY_DATE,
responseLoanProductsRequestLP2AdvancedpaymentInterest36030InterestRecalcSameAsRepTillRestFrequencyDate);
// LP1 advanced payment allocation + progressive loan schedule + horizontal
// (LP1_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL)
String name42 = DefaultLoanProduct.LP1_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL.getName();
PostLoanProductsRequest loanProductsRequestLP1AdvPmtAllocProgressiveLoanScheduleHorizontal = loanProductsRequestFactory//
.defaultLoanProductsRequestLP1()//
.name(name42)//
.transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION.getValue())//
.loanScheduleType("PROGRESSIVE") //
.loanScheduleProcessingType("HORIZONTAL")//
@@ -544,20 +842,22 @@ public class LoanProductGlobalInitializerStep implements FineractGlobalInitializ
createPaymentAllocation("GOODWILL_CREDIT", "NEXT_INSTALLMENT"), //
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "NEXT_INSTALLMENT"), //
createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT")));//
Response<PostLoanProductsResponse> responseLP2AdvPmtAllocProgressiveLoanScheduleHorizontal = loanProductsApi
.createLoanProduct(loanProductsRequestLP2AdvPmtAllocProgressiveLoanScheduleHorizontal).execute();
TestContext.INSTANCE.set(TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL,
responseLP2AdvPmtAllocProgressiveLoanScheduleHorizontal);
Response<PostLoanProductsResponse> responseLP1AdvPmtAllocProgressiveLoanScheduleHorizontal = loanProductsApi
.createLoanProduct(loanProductsRequestLP1AdvPmtAllocProgressiveLoanScheduleHorizontal).execute();
TestContext.INSTANCE.set(
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_ADVANCED_PAYMENT_ALLOCATION_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL,
responseLP1AdvPmtAllocProgressiveLoanScheduleHorizontal);
// LP2 with progressive loan schedule + horizontal + interest EMI + 360/30
// + interest recalculation, preClosureInterestCalculationStrategy= till preclose,
// interestRecalculationCompoundingMethod = none
// Frequency for recalculate Outstanding Principal: Daily, Frequency Interval for recalculation: 1
// (LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_TILL_PRECLOSE)
String name41 = DefaultLoanProduct.LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_IR_DAILY_TILL_PRECLOSE_LAST_INSTALLMENT_STRATEGY
// Frequency for Interest rate - Whole Year
// (LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE_WHOLE_TERM)
String name43 = DefaultLoanProduct.LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE_WHOLE_TERM
.getName();
PostLoanProductsRequest product41 = loanProductsRequestFactory.defaultLoanProductsRequestLP2Emi()//
.name(name41)//
PostLoanProductsRequest loanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcDailyTillPrecloseWholeTerm = loanProductsRequestFactory//
.defaultLoanProductsRequestLP2Emi()//
.name(name43)//
.daysInYearType(DaysInYearType.DAYS360.value)//
.daysInMonthType(DaysInMonthType.DAYS30.value)//
.isInterestRecalculationEnabled(true)//
@@ -566,19 +866,25 @@ public class LoanProductGlobalInitializerStep implements FineractGlobalInitializ
.interestRecalculationCompoundingMethod(0)//
.recalculationRestFrequencyType(2)//
.recalculationRestFrequencyInterval(1)//
.interestRatePerPeriod((double) 4)//
.interestRateFrequencyType(INTEREST_RATE_FREQUENCY_TYPE_WHOLE_TERM)//
.paymentAllocation(List.of(//
createPaymentAllocation("DEFAULT", "LAST_INSTALLMENT")));//
Response<PostLoanProductsResponse> response41 = loanProductsApi.createLoanProduct(product41).execute();
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT"), //
createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT"), //
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "REAMORTIZATION"), //
createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT")));//
Response<PostLoanProductsResponse> responseLP2AdvancedpaymentInterestEmi36030InterestRecalcDailyTillPrecloseWholeTerm = loanProductsApi
.createLoanProduct(loanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcDailyTillPrecloseWholeTerm).execute();
TestContext.INSTANCE.set(
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL_LAST,
response41);
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE_WHOLE_TERM,
responseLP2AdvancedpaymentInterestEmi36030InterestRecalcDailyTillPrecloseWholeTerm);
// LP2 with Down-payment + advanced custom payment allocation + progressive loan schedule + horizontal
// (LP2_DOWNPAYMENT_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL)
String name44 = DefaultLoanProduct.LP2_ADV_CUST_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL.getName();
// LP2 + interest recalculation + advanced custom payment allocation + progressive loan schedule + horizontal
// (LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL)
String name44 = DefaultLoanProduct.LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL.getName();
PostLoanProductsRequest loanProductsRequestAdvCustomPaymentAllocationProgressiveLoanSchedule = loanProductsRequestFactory
.defaultLoanProductsRequestLP2InterestDailyRecalculationWithDownPayment()//
.defaultLoanProductsRequestLP2InterestDailyRecalculation()//
.name(name44)//
.paymentAllocation(List.of(//
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT",
@@ -602,6 +908,88 @@ public class LoanProductGlobalInitializerStep implements FineractGlobalInitializ
TestContext.INSTANCE.set(
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADVANCED_CUSTOM_PAYMENT_ALLOCATION_PROGRESSIVE_LOAN_SCHEDULE,
responseLoanProductsRequestAdvCustomPaymentAllocationProgressiveLoanSchedule);
String name45 = DefaultLoanProduct.LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_INTEREST_REFUND_FULL.getName();
PostLoanProductsRequest loanProductsRequestLP2AdvancedpaymentInterestEmiActualActualInterestRefundFull = loanProductsRequestFactory
.defaultLoanProductsRequestLP2Emi()//
.name(name45)//
.multiDisburseLoan(true)//
.disallowExpectedDisbursements(true)//
.maxTrancheCount(10)//
.isInterestRecalculationEnabled(true)//
.preClosureInterestCalculationStrategy(1)//
.rescheduleStrategyMethod(4)//
.interestRecalculationCompoundingMethod(0)//
.recalculationRestFrequencyType(2)//
.recalculationRestFrequencyInterval(1)//
.supportedInterestRefundTypes(supportedInterestRefundTypes).paymentAllocation(List.of(//
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT"), //
createPaymentAllocation("GOODWILL_CREDIT", "NEXT_INSTALLMENT"), //
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "NEXT_INSTALLMENT"), //
createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT"), //
createPaymentAllocation("INTEREST_REFUND", "NEXT_INSTALLMENT")));//
Response<PostLoanProductsResponse> responseLoanProductsRequestLP2AdvancedpaymentInterestEmiActualActualInterestRefundFull = loanProductsApi
.createLoanProduct(loanProductsRequestLP2AdvancedpaymentInterestEmiActualActualInterestRefundFull).execute();
TestContext.INSTANCE.set(
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_INTEREST_REFUND_FULL,
responseLoanProductsRequestLP2AdvancedpaymentInterestEmiActualActualInterestRefundFull);
// LP2 with progressive loan schedule + horizontal + interest EMI + 360/30
// + interest recalculation, preClosureInterestCalculationStrategy= till preclose,
// interestRecalculationCompoundingMethod = none
// payment allocation order: penalty-fee-interest-principal
// Frequency for recalculate Outstanding Principal: Daily, Frequency Interval for recalculation: 1
// (LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE_PMT_ALLOC_1)
String name46 = DefaultLoanProduct.LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE_PMT_ALLOC_1
.getName();
PostLoanProductsRequest loanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcDailyTillPreclosePmtAlloc1 = loanProductsRequestFactory
.defaultLoanProductsRequestLP2Emi()//
.name(name46)//
.daysInYearType(DaysInYearType.DAYS360.value)//
.daysInMonthType(DaysInMonthType.DAYS30.value)//
.isInterestRecalculationEnabled(true)//
.preClosureInterestCalculationStrategy(1)//
.rescheduleStrategyMethod(4)//
.interestRecalculationCompoundingMethod(0)//
.recalculationRestFrequencyType(2)//
.recalculationRestFrequencyInterval(1)//
.paymentAllocation(List.of(//
createPaymentAllocationPenFeeIntPrincipal("DEFAULT", "NEXT_INSTALLMENT"), //
createPaymentAllocationPenFeeIntPrincipal("GOODWILL_CREDIT", "LAST_INSTALLMENT"), //
createPaymentAllocationPenFeeIntPrincipal("MERCHANT_ISSUED_REFUND", "REAMORTIZATION"), //
createPaymentAllocationPenFeeIntPrincipal("PAYOUT_REFUND", "NEXT_INSTALLMENT")));//
Response<PostLoanProductsResponse> responseLoanProductsRequestLP2AdvancedpaymentInterest36030InterestRecalcDailyTillPreCloesePmtAlloc1 = loanProductsApi
.createLoanProduct(loanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcDailyTillPreclosePmtAlloc1).execute();
TestContext.INSTANCE.set(
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE_PMT_ALLOC_1,
responseLoanProductsRequestLP2AdvancedpaymentInterest36030InterestRecalcDailyTillPreCloesePmtAlloc1);
// LP2 with progressive loan schedule + horizontal + interest EMI + 360/30, LAST INSTALLMENT strategy
// + interest recalculation, preClosureInterestCalculationStrategy= till preclose,
// interestRecalculationCompoundingMethod = none
// Frequency for recalculate Outstanding Principal: Daily, Frequency Interval for recalculation: 1
// (LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_IR_DAILY_TILL_PRECLOSE_LAST_INSTALLMENT_STRATEGY)
String name47 = DefaultLoanProduct.LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_IR_DAILY_TILL_PRECLOSE_LAST_INSTALLMENT_STRATEGY
.getName();
PostLoanProductsRequest loanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcDailyTillPrecloseLastInstallment = loanProductsRequestFactory
.defaultLoanProductsRequestLP2Emi()//
.name(name47)//
.daysInYearType(DaysInYearType.DAYS360.value)//
.daysInMonthType(DaysInMonthType.DAYS30.value)//
.isInterestRecalculationEnabled(true)//
.preClosureInterestCalculationStrategy(1)//
.rescheduleStrategyMethod(4)//
.interestRecalculationCompoundingMethod(0)//
.recalculationRestFrequencyType(2)//
.recalculationRestFrequencyInterval(1)//
.paymentAllocation(List.of(//
createPaymentAllocation("DEFAULT", "LAST_INSTALLMENT")));//
Response<PostLoanProductsResponse> loanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcDailyTillPrecloseLastInstallmentResponse = loanProductsApi
.createLoanProduct(loanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcDailyTillPrecloseLastInstallment)
.execute();
TestContext.INSTANCE.set(
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE_LAST_INSTALLMENT,
loanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcDailyTillPrecloseLastInstallmentResponse);
}
public static AdvancedPaymentData createPaymentAllocation(String transactionType, String futureInstallmentAllocationRule,
@@ -634,6 +1022,46 @@ public class LoanProductGlobalInitializerStep implements FineractGlobalInitializ
return advancedPaymentData;
}
public static AdvancedPaymentData createPaymentAllocationPenFeeIntPrincipal(String transactionType,
String futureInstallmentAllocationRule, LoanProductPaymentAllocationRule.AllocationTypesEnum... rules) {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType(transactionType);
advancedPaymentData.setFutureInstallmentAllocationRule(futureInstallmentAllocationRule);
List<PaymentAllocationOrder> paymentAllocationOrders;
if (rules.length == 0) {
paymentAllocationOrders = getPaymentAllocationOrder(//
LoanProductPaymentAllocationRule.AllocationTypesEnum.PAST_DUE_PENALTY, //
LoanProductPaymentAllocationRule.AllocationTypesEnum.PAST_DUE_FEE, //
LoanProductPaymentAllocationRule.AllocationTypesEnum.PAST_DUE_INTEREST, //
LoanProductPaymentAllocationRule.AllocationTypesEnum.PAST_DUE_PRINCIPAL, //
LoanProductPaymentAllocationRule.AllocationTypesEnum.DUE_PENALTY, //
LoanProductPaymentAllocationRule.AllocationTypesEnum.DUE_FEE, //
LoanProductPaymentAllocationRule.AllocationTypesEnum.DUE_INTEREST, //
LoanProductPaymentAllocationRule.AllocationTypesEnum.DUE_PRINCIPAL, //
LoanProductPaymentAllocationRule.AllocationTypesEnum.IN_ADVANCE_PENALTY, //
LoanProductPaymentAllocationRule.AllocationTypesEnum.IN_ADVANCE_FEE, //
LoanProductPaymentAllocationRule.AllocationTypesEnum.IN_ADVANCE_INTEREST, //
LoanProductPaymentAllocationRule.AllocationTypesEnum.IN_ADVANCE_PRINCIPAL);//
} else {
paymentAllocationOrders = getPaymentAllocationOrder(rules);
}
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
return advancedPaymentData;
}
public static AdvancedPaymentData editPaymentAllocationFutureInstallment(String transactionType, String futureInstallmentAllocationRule,
List<PaymentAllocationOrder> paymentAllocationOrder) {
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
advancedPaymentData.setTransactionType(transactionType);
advancedPaymentData.setFutureInstallmentAllocationRule(futureInstallmentAllocationRule);
advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrder);
return advancedPaymentData;
}
private static CreditAllocationData createCreditAllocation(String transactionType, List<String> creditAllocationRules) {
CreditAllocationData creditAllocationData = new CreditAllocationData();
creditAllocationData.setTransactionType(transactionType);
@@ -0,0 +1,53 @@
/**
* 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.initializer.global;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@RequiredArgsConstructor
@Component
public class SavingsProductGlobalInitializer implements FineractGlobalInitializerStep {
@Override
public void initialize() throws Exception {
/**
* TODO uncomment and check when PS-1088 is done
*/
// //EUR
// PostSavingsProductsRequest savingsProductsRequestEUR =
// SavingsProductRequestFactory.defaultSavingsProductRequest();
//
// Response<PostSavingsProductsResponse> savingsProductsResponseEUR =
// savingsProductApi.create13(savingsProductsRequestEUR).execute();
// testContext().set(TestContextKey.DEFAULT_SAVINGS_PRODUCT_CREATE_RESPONSE_EUR, savingsProductsResponseEUR);
//
// //USD
// PostSavingsProductsRequest savingsProductsRequestUSD =
// SavingsProductRequestFactory.defaultSavingsProductRequest()
// .name("CUSD")
// .shortName("CUS")
// .currencyCode("USD");
//
// Response<PostSavingsProductsResponse> savingsProductsResponseUSD =
// savingsProductApi.create13(savingsProductsRequestUSD).execute();
// testContext().set(TestContextKey.DEFAULT_SAVINGS_PRODUCT_CREATE_RESPONSE_USD, savingsProductsResponseUSD);
}
}
@@ -21,6 +21,6 @@ package org.apache.fineract.test.messaging;
public class EmptyEventMessage extends EventMessage<Object> {
public EmptyEventMessage() {
super(null, null, null);
super(null, null, null, null);
}
}
@@ -21,13 +21,13 @@ package org.apache.fineract.test.messaging;
import static java.lang.String.format;
import static org.awaitility.Awaitility.await;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDate;
import java.util.function.Function;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.test.messaging.config.EventProperties;
import org.apache.fineract.test.messaging.event.Event;
import org.apache.fineract.test.messaging.event.EventFactory;
@@ -39,7 +39,7 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor
@Component
@SuppressFBWarnings({ "VA_FORMAT_STRING_USES_NEWLINE" })
@Slf4j
public class EventAssertion {
private final EventStore eventStore;
@@ -85,11 +85,8 @@ public class EventAssertion {
String receivedEventsLogParam = eventStore.getReceivedEvents().stream().map(LoggedEvent::new).map(LoggedEvent::toString)
.reduce("", (s, e) -> format("%s%s%n", s, e));
Assertions.fail("""
%s has been received, but it was unexpected.
Events received but not verified:
%s
""".formatted(event.getEventName(), receivedEventsLogParam));
Assertions.fail("%s has been received, but it was unexpected. Events received but not verified: %s", event.getEventName(),
receivedEventsLogParam);
} catch (ConditionTimeoutException e) {
// This is the expected outcome here!
}
@@ -104,6 +101,7 @@ public class EventAssertion {
} else {
eventMessage = (EventMessage<R>) new EmptyEventMessage();
}
log.info("Assert event: {}", eventMessage.getIdempotencyKey());
return new EventAssertionBuilder<>(eventMessage);
}
@@ -29,4 +29,5 @@ public class EventMessage<T> {
private final String type;
private final LocalDate businessDate;
private final T data;
private final String idempotencyKey;
}
@@ -22,6 +22,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.MathContext;
import java.time.format.DateTimeFormatter;
import java.util.List;
import lombok.RequiredArgsConstructor;
@@ -55,15 +56,21 @@ import org.apache.fineract.test.messaging.event.assetexternalization.LoanAccount
import org.apache.fineract.test.messaging.event.assetexternalization.LoanOwnershipTransferEvent;
import org.apache.fineract.test.messaging.event.client.ClientActivatedEvent;
import org.apache.fineract.test.messaging.event.client.ClientCreatedEvent;
import org.apache.fineract.test.messaging.event.loan.AbstractLoanEvent;
import org.apache.fineract.test.messaging.event.loan.LoanApprovedEvent;
import org.apache.fineract.test.messaging.event.loan.LoanBalanceChangedEvent;
import org.apache.fineract.test.messaging.event.loan.LoanCreatedEvent;
import org.apache.fineract.test.messaging.event.loan.LoanDisbursalEvent;
import org.apache.fineract.test.messaging.event.loan.LoanRejectedEvent;
import org.apache.fineract.test.messaging.event.loan.LoanStatusChangedEvent;
import org.apache.fineract.test.messaging.event.loan.LoanUndoApprovalEvent;
import org.apache.fineract.test.messaging.event.loan.delinquency.LoanDelinquencyPauseChangedEvent;
import org.apache.fineract.test.messaging.event.loan.delinquency.LoanDelinquencyRangeChangeEvent;
import org.apache.fineract.test.messaging.event.loan.transaction.AbstractLoanTransactionEvent;
import org.apache.fineract.test.messaging.event.loan.transaction.LoanDisbursalTransactionEvent;
import org.apache.fineract.test.messaging.event.loan.transaction.LoanRefundPostBusinessEvent;
import org.apache.fineract.test.messaging.event.loan.transaction.LoanTransactionGoodwillCreditPostEvent;
import org.apache.fineract.test.messaging.event.loan.transaction.LoanTransactionInterestPaymentWaiverPostEvent;
import org.apache.fineract.test.messaging.event.loan.transaction.LoanTransactionMakeRepaymentPostEvent;
import org.apache.fineract.test.messaging.event.loan.transaction.LoanTransactionMerchantIssuedRefundPostEvent;
import org.apache.fineract.test.messaging.event.loan.transaction.LoanTransactionPayoutRefundPostEvent;
@@ -111,22 +118,21 @@ public class EventCheckHelper {
eventAssertion.assertEvent(ClientActivatedEvent.class, clientCreationResponse.body().getClientId())//
.extractingData(ClientDataV1::getActive).isEqualTo(true)//
.extractingData(clientDataV1 -> clientDataV1.getStatus().getId()).isEqualTo(status);//
}
public void createLoanEventCheck(Response<PostLoansResponse> createLoanResponse) throws IOException {
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(createLoanResponse.body().getLoanId(), false, "", "", "")
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(createLoanResponse.body().getLoanId(), false, "all", "", "")
.execute();
GetLoansLoanIdResponse body = loanDetails.body();
eventAssertion.assertEvent(LoanCreatedEvent.class, createLoanResponse.body().getLoanId()).extractingData(LoanAccountDataV1::getId)
.isEqualTo(body.getId()).extractingData(loanAccountDataV1 -> loanAccountDataV1.getStatus().getId())
.isEqualTo(body.getStatus().getId()).extractingData(LoanAccountDataV1::getClientId)
.isEqualTo(Long.valueOf(body.getClientId()))
.extractingData(loanAccountDataV11 -> loanAccountDataV11.getPrincipal().longValue())
.isEqualTo(body.getPrincipal().longValue())
eventAssertion.assertEvent(LoanCreatedEvent.class, createLoanResponse.body().getLoanId())//
.extractingData(LoanAccountDataV1::getId).isEqualTo(body.getId())//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getStatus().getId()).isEqualTo(body.getStatus().getId())//
.extractingData(LoanAccountDataV1::getClientId).isEqualTo(Long.valueOf(body.getClientId()))//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getPrincipal().longValue())
.isEqualTo(body.getPrincipal().longValue())//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getSummary().getCurrency().getCode())
.isEqualTo(body.getCurrency().getCode());
.isEqualTo(body.getCurrency().getCode());//
}
public void approveLoanEventCheck(Response<PostLoansLoanIdResponse> loanApproveResponse) throws IOException {
@@ -134,37 +140,111 @@ public class EventCheckHelper {
.execute();
GetLoansLoanIdResponse body = loanDetails.body();
eventAssertion.assertEvent(LoanApprovedEvent.class, loanApproveResponse.body().getLoanId()).extractingData(LoanAccountDataV1::getId)
.isEqualTo(body.getId()).extractingData(loanAccountDataV1 -> loanAccountDataV1.getStatus().getId())
.isEqualTo(body.getStatus().getId()).extractingData(loanAccountDataV1 -> loanAccountDataV1.getStatus().getCode())
.isEqualTo(body.getStatus().getCode()).extractingData(LoanAccountDataV1::getClientId)
.isEqualTo(Long.valueOf(body.getClientId()))
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getApprovedPrincipal().longValue())
.isEqualTo(body.getApprovedPrincipal().longValue())
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getTimeline().getApprovedOnDate())
.isEqualTo(FORMATTER_EVENTS.format(body.getTimeline().getApprovedOnDate()))
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getSummary().getCurrency().getCode())
.isEqualTo(body.getCurrency().getCode());
}
public void disburseLoanEventCheck(Response<PostLoansLoanIdResponse> loanDisburseResponse) throws IOException {
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanDisburseResponse.body().getLoanId(), false, "", "", "")
.execute();
GetLoansLoanIdResponse body = loanDetails.body();
eventAssertion.assertEvent(LoanDisbursalEvent.class, loanDisburseResponse.body().getLoanId())//
eventAssertion.assertEvent(LoanApprovedEvent.class, loanApproveResponse.body().getLoanId())//
.extractingData(LoanAccountDataV1::getId).isEqualTo(body.getId())//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getStatus().getId()).isEqualTo(body.getStatus().getId())//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getStatus().getCode()).isEqualTo(body.getStatus().getCode())//
.extractingData(LoanAccountDataV1::getClientId).isEqualTo(Long.valueOf(body.getClientId()))//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getSummary().getPrincipalDisbursed().longValue())
.isEqualTo(body.getSummary().getPrincipalDisbursed().longValue())//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getTimeline().getActualDisbursementDate())
.isEqualTo(FORMATTER_EVENTS.format(body.getTimeline().getActualDisbursementDate()))
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getApprovedPrincipal().longValue())
.isEqualTo(body.getApprovedPrincipal().longValue())//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getTimeline().getApprovedOnDate())//
.isEqualTo(FORMATTER_EVENTS.format(body.getTimeline().getApprovedOnDate()))//
.extractingData(loanAccountDataV1 -> loanAccountDataV1.getSummary().getCurrency().getCode())
.isEqualTo(body.getCurrency().getCode());//
}
public void undoApproveLoanEventCheck(Response<PostLoansLoanIdResponse> loanUndoApproveResponse) throws IOException {
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanUndoApproveResponse.body().getLoanId(), false, "", "", "")
.execute();
GetLoansLoanIdResponse body = loanDetails.body();
eventAssertion.assertEventRaised(LoanUndoApprovalEvent.class, body.getId());
}
public void loanRejectedEventCheck(Response<PostLoansLoanIdResponse> loanRejectedResponse) throws IOException {
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanRejectedResponse.body().getLoanId(), false, "", "", "")
.execute();
GetLoansLoanIdResponse body = loanDetails.body();
eventAssertion.assertEventRaised(LoanRejectedEvent.class, body.getId());
}
public void disburseLoanEventCheck(Long loanId) throws IOException {
loanAccountDataV1Check(LoanDisbursalEvent.class, loanId);
}
public void loanBalanceChangedEventCheck(Long loanId) throws IOException {
loanAccountDataV1Check(LoanBalanceChangedEvent.class, loanId);
}
public void loanStatusChangedEventCheck(Long loanId) throws IOException {
loanAccountDataV1Check(LoanStatusChangedEvent.class, loanId);
}
private void loanAccountDataV1Check(Class<? extends AbstractLoanEvent> eventClazz, Long loanId) throws IOException {
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "all", "", "").execute();
GetLoansLoanIdResponse body = loanDetails.body();
eventAssertion.assertEvent(eventClazz, loanId)//
.extractingData(loanAccountDataV1 -> {
Long idActual = loanAccountDataV1.getId();
Long idExpected = body.getId();
Integer statusIdActual = loanAccountDataV1.getStatus().getId();
Integer statusIdExpected = body.getStatus().getId();
String statusCodeActual = loanAccountDataV1.getStatus().getCode();
String statusCodeExpected = body.getStatus().getCode();
Long clientIdActual = loanAccountDataV1.getClientId();
Long clientIdExpected = body.getClientId();
BigDecimal principalDisbursedActual = loanAccountDataV1.getSummary().getPrincipalDisbursed();
Double principalDisbursedExpectedDouble = body.getSummary().getPrincipalDisbursed();
BigDecimal principalDisbursedExpected = new BigDecimal(principalDisbursedExpectedDouble, MathContext.DECIMAL64);
String actualDisbursementDateActual = loanAccountDataV1.getTimeline().getActualDisbursementDate();
String actualDisbursementDateExpected = FORMATTER_EVENTS.format(body.getTimeline().getActualDisbursementDate());
String currencyCodeActual = loanAccountDataV1.getSummary().getCurrency().getCode();
String currencyCodeExpected = body.getSummary().getCurrency().getCode();
BigDecimal totalUnpaidPayableDueInterestActual = loanAccountDataV1.getSummary().getTotalUnpaidPayableDueInterest();
BigDecimal totalUnpaidPayableDueInterestExpected = body.getSummary().getTotalUnpaidPayableDueInterest();
BigDecimal totalUnpaidPayableNotDueInterestActual = loanAccountDataV1.getSummary()
.getTotalUnpaidPayableNotDueInterest();
BigDecimal totalUnpaidPayableNotDueInterestExpected = body.getSummary().getTotalUnpaidPayableNotDueInterest();
BigDecimal totalInterestPaymentWaiverActual = loanAccountDataV1.getSummary().getTotalInterestPaymentWaiver();
Double totalInterestPaymentWaiverExpectedDouble = body.getSummary().getTotalInterestPaymentWaiver();
BigDecimal totalInterestPaymentWaiverExpected = new BigDecimal(totalInterestPaymentWaiverExpectedDouble,
MathContext.DECIMAL64);
BigDecimal delinquentInterestActual = loanAccountDataV1.getDelinquent().getDelinquentInterest();
BigDecimal delinquentInterestExpected = body.getDelinquent().getDelinquentInterest();
BigDecimal delinquentFeeActual = loanAccountDataV1.getDelinquent().getDelinquentFee();
BigDecimal delinquentFeeExpected = body.getDelinquent().getDelinquentFee();
BigDecimal delinquentPenaltyActual = loanAccountDataV1.getDelinquent().getDelinquentPenalty();
BigDecimal delinquentPenaltyExpected = body.getDelinquent().getDelinquentPenalty();
assertThat(idActual).isEqualTo(idExpected);
assertThat(statusIdActual).isEqualTo(statusIdExpected);
assertThat(statusCodeActual).isEqualTo(statusCodeExpected);
assertThat(clientIdActual).isEqualTo(clientIdExpected);
assertThat(areBigDecimalValuesEqual(principalDisbursedActual, principalDisbursedExpected)).isTrue();
assertThat(actualDisbursementDateActual).isEqualTo(actualDisbursementDateExpected);
assertThat(currencyCodeActual).isEqualTo(currencyCodeExpected);
assertThat(areBigDecimalValuesEqual(totalUnpaidPayableDueInterestActual, totalUnpaidPayableDueInterestExpected))
.isTrue();
assertThat(areBigDecimalValuesEqual(totalUnpaidPayableNotDueInterestActual, totalUnpaidPayableNotDueInterestExpected))
.isTrue();
assertThat(areBigDecimalValuesEqual(totalInterestPaymentWaiverActual, totalInterestPaymentWaiverExpected)).isTrue();
assertThat(areBigDecimalValuesEqual(delinquentInterestActual, delinquentInterestExpected)).isTrue();
assertThat(areBigDecimalValuesEqual(delinquentFeeActual, delinquentFeeExpected)).isTrue();
assertThat(areBigDecimalValuesEqual(delinquentPenaltyActual, delinquentPenaltyExpected)).isTrue();
return null;
});
}
private boolean areBigDecimalValuesEqual(BigDecimal actual, BigDecimal expected) {
log.info("--- Checking BigDecimal values.... ---");
log.info("Actual: {}", actual);
log.info("Expected: {}", expected);
return actual.compareTo(expected) == 0;
}
public void loanDisbursalTransactionEventCheck(Response<PostLoansLoanIdResponse> loanDisburseResponse) throws IOException {
Long disbursementTransactionId = loanDisburseResponse.body().getSubResourceId();
@@ -204,6 +284,7 @@ public class EventCheckHelper {
case PAYOUT_REFUND -> LoanTransactionPayoutRefundPostEvent.class;
case MERCHANT_ISSUED_REFUND -> LoanTransactionMerchantIssuedRefundPostEvent.class;
case REFUND_BY_CASH -> LoanRefundPostBusinessEvent.class;
case INTEREST_PAYMENT_WAIVER -> LoanTransactionInterestPaymentWaiverPostEvent.class;
default -> throw new IllegalStateException(String.format("transaction type %s cannot be found", transactionType.getValue()));
};
@@ -404,6 +485,6 @@ public class EventCheckHelper {
}
private BigDecimal zeroConversion(BigDecimal input) {
return input.compareTo(BigDecimal.ZERO) == 0 ? new BigDecimal(input.toEngineeringString()) : input.setScale(8);
return input.compareTo(new BigDecimal("0.000000")) == 0 ? new BigDecimal(input.toEngineeringString()) : input.setScale(8);
}
}
@@ -0,0 +1,27 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.test.messaging.event.loan;
public class LoanRejectedEvent extends AbstractLoanEvent {
@Override
public String getEventName() {
return "LoanRejectedBusinessEvent";
}
}
@@ -0,0 +1,27 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.test.messaging.event.loan;
public class LoanUndoApprovalEvent extends AbstractLoanEvent {
@Override
public String getEventName() {
return "LoanUndoApprovalBusinessEvent";
}
}
@@ -0,0 +1,27 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.test.messaging.event.loan.transaction;
public class LoanTransactionAccrualActivityPostEvent extends AbstractLoanTransactionEvent {
@Override
public String getEventName() {
return "LoanTransactionAccrualActivityPostBusinessEvent";
}
}
@@ -0,0 +1,27 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.test.messaging.event.loan.transaction;
public class LoanTransactionInterestPaymentWaiverPostEvent extends AbstractLoanTransactionEvent {
@Override
public String getEventName() {
return "LoanTransactionInterestPaymentWaiverPostBusinessEvent";
}
}
@@ -71,20 +71,21 @@ public class EventStore {
void receive(byte[] message) throws Exception {
MessageV1 msgObject = MessageV1.fromByteBuffer(ByteBuffer.wrap(message));
String type = msgObject.getType();
String idempotencyKey = msgObject.getIdempotencyKey();
LocalDate businessDate = LocalDate.parse(msgObject.getBusinessDate(), DateTimeFormatter.ISO_LOCAL_DATE);
Object dataObject = getDataObject(msgObject);
if (BULK_BUSINESS_EVENT_TYPE.equals(type)) {
BulkMessagePayloadV1 bulkPayload = (BulkMessagePayloadV1) dataObject;
List<EventMessage<Object>> bulkEvents = bulkPayload.getDatas().stream()
.map((BulkMessageItemV1 item) -> getEventMessageFromBulkItem(item, businessDate)).toList();
.map((BulkMessageItemV1 item) -> getEventMessageFromBulkItem(item, businessDate, idempotencyKey)).toList();
if (log.isDebugEnabled()) {
bulkEvents.forEach(msg -> {
log.debug("Received event {}", new LoggedEvent(msg));
log.debug("Received Bulk event {}", new LoggedEvent(msg));
});
}
receivedEvents.addAll(bulkEvents);
} else {
EventMessage<Object> msg = new EventMessage<>(type, businessDate, dataObject);
EventMessage<Object> msg = new EventMessage<>(type, businessDate, dataObject, idempotencyKey);
if (log.isDebugEnabled()) {
log.debug("Received event {}", new LoggedEvent(msg));
}
@@ -93,12 +94,12 @@ public class EventStore {
log.trace("Data object within event {}", dataObject);
}
private EventMessage<Object> getEventMessageFromBulkItem(BulkMessageItemV1 item, LocalDate businessDate) {
private EventMessage<Object> getEventMessageFromBulkItem(BulkMessageItemV1 item, LocalDate businessDate, String idempotencyKey) {
try {
String dataschema = item.getDataschema();
ByteBuffer data = item.getData();
Object deserialized = deserialize(dataschema, data);
return new EventMessage<>(item.getType(), businessDate, deserialized);
return new EventMessage<>(item.getType(), businessDate, deserialized, idempotencyKey);
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -25,15 +25,17 @@ public class LoggedEvent {
private final String type;
private final String businessDate;
private final String idempotencyKey;
public LoggedEvent(EventMessage<?> message) {
this.type = message.getType();
this.businessDate = DateTimeFormatter.ISO_LOCAL_DATE.format(message.getBusinessDate());
this.idempotencyKey = message.getIdempotencyKey();
}
// Don't use Lombok @ToString since the class name isn't included in the msg
@Override
public String toString() {
return "{" + "type='" + type + '\'' + ", businessDate='" + businessDate + '\'' + '}';
return "{" + "idempotencyKey='" + idempotencyKey + '\'' + ", type='" + type + "', businessDate='" + businessDate + '\'' + '}';
}
}
@@ -27,7 +27,6 @@ import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
@@ -75,6 +74,7 @@ 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.helper.Utils;
import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.loan.LoanRescheduledDueAdjustScheduleEvent;
import org.apache.fineract.test.stepdef.AbstractStepDef;
@@ -175,7 +175,7 @@ public class BatchApiStepDef extends AbstractStepDef {
// request 3 - charge Loan
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
String dateOfCharge = formatter.format(LocalDate.now(Clock.systemUTC()).minusMonths(1L).plusDays(1L));
String dateOfCharge = formatter.format(Utils.now().minusMonths(1L).plusDays(1L));
PostLoansLoanIdChargesRequest loanIdChargesRequest = new PostLoansLoanIdChargesRequest();
loanIdChargesRequest.chargeId(CHARGE_ID_NFS_FEE);
@@ -448,11 +448,11 @@ public class BatchApiStepDef extends AbstractStepDef {
testContext().set(TestContextKey.BATCH_API_CALL_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey);
eventAssertion.assertEvent(LoanRescheduledDueAdjustScheduleEvent.class, loanId).extractingData(loanAccountDataV1 -> {
Optional<LoanSchedulePeriodDataV1> period = loanAccountDataV1.getRepaymentSchedule().getPeriods().stream()
.filter(p -> formatter.format(LocalDate.parse(p.getDueDate())).equals(toDateStr)).findFirst();
LoanSchedulePeriodDataV1 period = loanAccountDataV1.getRepaymentSchedule().getPeriods().stream()
.filter(p -> formatter.format(LocalDate.parse(p.getDueDate())).equals(toDateStr)).findFirst().orElse(null);
String dueDate = "";
if (period.isPresent()) {
dueDate = formatter.format(LocalDate.parse(period.get().getDueDate()));
if (period != null) {
dueDate = formatter.format(LocalDate.parse(period.getDueDate()));
}
assertThat(dueDate).as(ErrorMessageHelper.wrongDataInLastPaymentAmount(dueDate, toDateStr)).isEqualTo(toDateStr);
return null;
@@ -30,8 +30,11 @@ import org.apache.fineract.client.models.BusinessStep;
import org.apache.fineract.client.models.GetBusinessStepConfigResponse;
import org.apache.fineract.client.models.UpdateBusinessStepConfigRequest;
import org.apache.fineract.client.services.BusinessStepConfigurationApi;
import org.apache.fineract.test.data.CobBusinessStep;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContext;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
import retrofit2.Response;
@@ -47,28 +50,20 @@ public class BusinessStepStepDef extends AbstractStepDef {
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";
private static final String BUSINESS_STEP_NAME_ACCRUAL_ACTIVITY_POSTING = "ACCRUAL_ACTIVITY_POSTING";
private static final List<BusinessStep> ORIGINAL_COB_BUSINESS_STEPS = TestContext.GLOBAL
.get(TestContextKey.ORIGINAL_COB_WORKFLOW_JOB_BUSINESS_STEP_LIST);
@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<>(ORIGINAL_COB_BUSINESS_STEPS);
Long lastOrder = businessSteps.get(businessSteps.size() - 1).getOrder();
List<BusinessStep> businessSteps = new ArrayList<>();
businessSteps.add(applyChargeToOverdueLoans);
businessSteps.add(loanDelinquencyClassification);
businessSteps.add(checkLoanRepaymentDue);
businessSteps.add(checkLoanRepaymentOverdue);
businessSteps.add(updateLoanArrearsAging);
businessSteps.add(addPeriodicAccrualEntries);
BusinessStep externalAssetOwnerTransfer = new BusinessStep().stepName(BUSINESS_STEP_NAME_EXTERNAL_ASSET_OWNER_TRANSFER)
.order(lastOrder + 1);
businessSteps.add(externalAssetOwnerTransfer);
UpdateBusinessStepConfigRequest request = new UpdateBusinessStepConfigRequest().businessSteps(businessSteps);
@@ -76,51 +71,22 @@ public class BusinessStepStepDef extends AbstractStepDef {
Response<Void> response = businessStepConfigurationApi.updateJobBusinessStepConfig(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS, request)
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
// --- log changes ---
logChanges();
}
@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);
setBackBusinessStepsToOriginal();
}
@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<>(ORIGINAL_COB_BUSINESS_STEPS);
Long lastOrder = businessSteps.get(businessSteps.size() - 1).getOrder();
List<BusinessStep> businessSteps = new ArrayList<>();
businessSteps.add(applyChargeToOverdueLoans);
businessSteps.add(loanDelinquencyClassification);
businessSteps.add(checkLoanRepaymentDue);
businessSteps.add(checkLoanRepaymentOverdue);
businessSteps.add(updateLoanArrearsAging);
businessSteps.add(addPeriodicAccrualEntries);
BusinessStep checkDueInstallments = new BusinessStep().stepName(BUSINESS_STEP_NAME_CHECK_DUE_INSTALLMENTS).order(lastOrder + 1);
businessSteps.add(checkDueInstallments);
UpdateBusinessStepConfigRequest request = new UpdateBusinessStepConfigRequest().businessSteps(businessSteps);
@@ -128,68 +94,71 @@ public class BusinessStepStepDef extends AbstractStepDef {
Response<Void> response = businessStepConfigurationApi.updateJobBusinessStepConfig(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS, request)
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
// --- log changes ---
logChanges();
}
@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);
setBackBusinessStepsToOriginal();
}
@Given("Admin puts {string} business step into LOAN_CLOSE_OF_BUSINESS workflow")
public void putGivenJobInCOB(String businessStepName) throws IOException {
List<BusinessStep> businessSteps = retrieveLoanCOBJobSteps();
if (businessSteps.stream().anyMatch(businessStep -> businessStep.getStepName().equals(businessStepName))) {
return;
}
List<BusinessStep> businessSteps = new ArrayList<>(ORIGINAL_COB_BUSINESS_STEPS);
Long lastOrder = businessSteps.get(businessSteps.size() - 1).getOrder();
businessSteps.add(new BusinessStep().stepName(businessStepName).order((long) (1 + businessSteps.size())));
CobBusinessStep cobBusinessStep = CobBusinessStep.valueOf(businessStepName);
String stepName = cobBusinessStep.getValue();
BusinessStep businessStepToAdd = new BusinessStep().stepName(stepName).order(lastOrder + 1);
businessSteps.add(businessStepToAdd);
UpdateBusinessStepConfigRequest request = new UpdateBusinessStepConfigRequest().businessSteps(businessSteps);
Response<Void> response = businessStepConfigurationApi.updateJobBusinessStepConfig(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS, request)
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
// --- log changes ---
logChanges();
}
@Then("Admin sets back LOAN_CLOSE_OF_BUSINESS workflow to its initial state")
public void setBackCOBToInitialState() throws IOException {
setBackBusinessStepsToOriginal();
}
@Then("Admin removes {string} business step into LOAN_CLOSE_OF_BUSINESS workflow")
public void removeGivenJobInCOB(String businessStepName) throws IOException {
List<BusinessStep> businessSteps = retrieveLoanCOBJobSteps();
businessSteps.removeIf(businessStep -> businessStep.getStepName().equals(businessStepName));
List<BusinessStep> businessSteps = new ArrayList<>(ORIGINAL_COB_BUSINESS_STEPS);
CobBusinessStep cobBusinessStep = CobBusinessStep.valueOf(businessStepName);
String stepName = cobBusinessStep.getValue();
businessSteps.removeIf(businessStep -> businessStep.getStepName().equals(stepName));
UpdateBusinessStepConfigRequest request = new UpdateBusinessStepConfigRequest().businessSteps(businessSteps);
Response<Void> response = businessStepConfigurationApi.updateJobBusinessStepConfig(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS, request)
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
// --- log changes ---
logChanges();
}
private List<BusinessStep> retrieveLoanCOBJobSteps() throws IOException {
Response<GetBusinessStepConfigResponse> businessStepConfigResponse = businessStepConfigurationApi
.retrieveAllConfiguredBusinessStep(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS).execute();
ErrorHelper.checkSuccessfulApiCall(businessStepConfigResponse);
return businessStepConfigResponse.body().getBusinessSteps();
private void setBackBusinessStepsToOriginal() throws IOException {
log.info("Setting back Business steps to original...");
UpdateBusinessStepConfigRequest request = new UpdateBusinessStepConfigRequest().businessSteps(ORIGINAL_COB_BUSINESS_STEPS);
Response<Void> response = businessStepConfigurationApi.updateJobBusinessStepConfig(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS, request)
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
// --- log changes ---
logChanges();
}
private void logChanges() throws IOException {
@@ -21,6 +21,7 @@ package org.apache.fineract.test.stepdef.common;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
import java.io.IOException;
import org.apache.fineract.client.services.DefaultApi;
import org.apache.fineract.test.helper.GlobalConfigurationHelper;
import org.springframework.beans.factory.annotation.Autowired;
@@ -28,6 +29,8 @@ public class GlobalConfigurationStepDef {
@Autowired
private GlobalConfigurationHelper globalConfigurationHelper;
@Autowired
private DefaultApi defaultApi;
@Given("Global configuration {string} is disabled")
public void disableGlobalConfiguration(String configKey) throws IOException {
@@ -42,6 +45,12 @@ public class GlobalConfigurationStepDef {
@When("Global config {string} value set to {string}")
public void setGlobalConfigValueString(String configKey, String configValue) throws IOException {
globalConfigurationHelper.setGlobalConfigValueString(configKey, configValue);
}
@When("Global config {string} value set to {string} through DefaultApi")
public void setGlobalConfigValueStringDefaultApi(String configKey, String configValue) throws IOException {
Long configValueLong = Long.valueOf(configValue);
defaultApi.updateGlobalConfiguration(configKey, configValueLong);
}
}
@@ -67,7 +67,6 @@ public class JournalEntriesStepDef extends AbstractStepDef {
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)))
@@ -56,4 +56,9 @@ public class SchedulerStepDef extends AbstractStepDef {
public void runCOB() {
jobService.executeAndWait(DefaultJob.LOAN_COB);
}
@When("Admin runs the Accrual Activity Posting job")
public void runAccrualActivityPosting() {
jobService.executeAndWait(DefaultJob.ACCRUAL_ACTIVITY_POSTING);
}
}
@@ -0,0 +1,45 @@
/**
* 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.hook;
import io.cucumber.java.After;
import io.cucumber.java.Scenario;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.test.testrail.TestRailClient;
import org.apache.fineract.test.testrail.TestRailProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
@Slf4j
public class TestRailLifecycleHook {
@Autowired
private TestRailProperties testRailProperties;
@Autowired
private ApplicationContext applicationContext;
@After
public void tearDown(Scenario scenario) {
if (testRailProperties.isEnabled()) {
TestRailClient testRailClient = applicationContext.getBean(TestRailClient.class);
testRailClient.saveScenarioResult(scenario);
}
}
}
@@ -21,12 +21,14 @@ 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.Then;
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 lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.BusinessDateResponse;
import org.apache.fineract.client.models.GetLoansLoanIdLoanChargeData;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
@@ -46,11 +48,14 @@ 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.event.EventCheckHelper;
import org.apache.fineract.test.messaging.store.EventStore;
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 LoanChargeAdjustmentStepDef extends AbstractStepDef {
public static final String DATE_FORMAT = "dd MMMM yyyy";
@@ -65,6 +70,10 @@ public class LoanChargeAdjustmentStepDef extends AbstractStepDef {
private LoanTransactionsApi loanTransactionsApi;
@Autowired
private BusinessDateManagementApi businessDateManagementApi;
@Autowired
private EventCheckHelper eventCheckHelper;
@Autowired
private EventStore eventStore;
@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)
@@ -79,8 +88,8 @@ public class LoanChargeAdjustmentStepDef extends AbstractStepDef {
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 {
@Then("Charge adjustment for the last {string} type charge which is due on {string} with transaction amount {double} which is higher than the available charge amount results an ERROR")
public void loanChargeAdjustmentFailedOnWrongAmount(String chargeTypeEnum, String date, double amount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
@@ -88,7 +97,7 @@ public class LoanChargeAdjustmentStepDef extends AbstractStepDef {
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
Long transactionId = getTransactionIdForLastChargeMetConditions(chargeTypeEnum, date, loanDetailsResponse);
PostLoansLoanIdChargesChargeIdRequest chargeAdjustmentRequest = LoanRequestFactory.defaultChargeAdjustmentRequest().amount(8.0)
PostLoansLoanIdChargesChargeIdRequest chargeAdjustmentRequest = LoanRequestFactory.defaultChargeAdjustmentRequest().amount(amount)
.externalId("");
Response<PostLoansLoanIdChargesChargeIdResponse> chargeAdjustmentResponseFail = loanChargesApi
@@ -108,6 +117,9 @@ public class LoanChargeAdjustmentStepDef extends AbstractStepDef {
assertThat(developerMessageActual)
.as(ErrorMessageHelper.wrongErrorMessageInFailedChargeAdjustment(developerMessageActual, developerMessageExpected))
.isEqualTo(developerMessageExpected);
log.info("Error code: {}", httpStatusCodeActual);
log.info("Error message: {}", developerMessageActual);
}
@When("Admin reverts the charge adjustment which was raised on {string} with {double} EUR transaction amount")
@@ -153,6 +165,7 @@ public class LoanChargeAdjustmentStepDef extends AbstractStepDef {
}
private void makeChargeAdjustmentCall(Long loanId, Long transactionId, String externalId, double transactionAmount) throws IOException {
eventStore.reset();
PostLoansLoanIdChargesChargeIdRequest chargeAdjustmentRequest = LoanRequestFactory.defaultChargeAdjustmentRequest()
.amount(transactionAmount).externalId(externalId);
@@ -160,6 +173,7 @@ public class LoanChargeAdjustmentStepDef extends AbstractStepDef {
.executeLoanCharge2(loanId, transactionId, chargeAdjustmentRequest, "adjustment").execute();
testContext().set(TestContextKey.LOAN_CHARGE_ADJUSTMENT_RESPONSE, chargeAdjustmentResponse);
ErrorHelper.checkSuccessfulApiCall(chargeAdjustmentResponse);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
private Long getTransactionIdForLastChargeMetConditions(String chargeTypeEnum, String date,
@@ -40,8 +40,9 @@ 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.EventCheckHelper;
import org.apache.fineract.test.messaging.event.loan.transaction.LoanChargebackTransactionEvent;
import org.apache.fineract.test.messaging.store.EventStore;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
@@ -61,6 +62,12 @@ public class LoanChargeBackStepDef extends AbstractStepDef {
@Autowired
private PaymentTypeResolver paymentTypeResolver;
@Autowired
private EventCheckHelper eventCheckHelper;
@Autowired
EventStore eventStore;
@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);
@@ -116,6 +123,7 @@ public class LoanChargeBackStepDef extends AbstractStepDef {
}
private void makeChargebackCall(Long loanId, Long transactionId, String repaymentType, double transactionAmount) throws IOException {
eventStore.reset();
DefaultPaymentType paymentType = DefaultPaymentType.valueOf(repaymentType);
Long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
@@ -134,8 +142,7 @@ public class LoanChargeBackStepDef extends AbstractStepDef {
PostLoansLoanIdTransactionsResponse responseBody = chargebackResponse.body();
Long loanId = responseBody.getLoanId();
eventAssertion.assertEvent(LoanBalanceChangedEvent.class, loanId).extractingData(data -> data.getId()).isEqualTo(loanId);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
checkLoanChargebackTransactionEvent(responseBody);
}
@@ -51,7 +51,9 @@ 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.charge.LoanAddChargeEvent;
import org.apache.fineract.test.messaging.store.EventStore;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
@@ -73,6 +75,10 @@ public class LoanChargeStepDef extends AbstractStepDef {
private LoansApi loansApi;
@Autowired
private EventAssertion eventAssertion;
@Autowired
private EventCheckHelper eventCheckHelper;
@Autowired
private EventStore eventStore;
@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 {
@@ -142,6 +148,7 @@ public class LoanChargeStepDef extends AbstractStepDef {
@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 {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest()
@@ -152,10 +159,12 @@ public class LoanChargeStepDef extends AbstractStepDef {
.execute();
ErrorHelper.checkSuccessfulApiCall(loanChargeResponse);
testContext().set(TestContextKey.ADD_PROCESSING_FEE_RESPONSE, loanChargeResponse);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
@And("Admin adds an NSF fee because of payment bounce with {string} transaction date")
public void addNSFfee(String date) throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansLoanIdChargesRequest loanIdChargesRequest = LoanChargeRequestFactory.defaultLoanChargeRequest()
@@ -166,6 +175,7 @@ public class LoanChargeStepDef extends AbstractStepDef {
.execute();
ErrorHelper.checkSuccessfulApiCall(loanChargeResponse);
testContext().set(TestContextKey.ADD_NSF_FEE_RESPONSE, loanChargeResponse);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
@And("Admin waives charge")
@@ -21,7 +21,6 @@ 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;
@@ -629,7 +628,7 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue();
eventAssertion.assertEvent(LoanDelinquencyRangeChangeEvent.class, loanId)//
.extractingData(loanAccountDelinquencyRangeDataV1 -> { //
.extractingData(loanAccountDelinquencyRangeDataV1 -> {
String actualDelinquencyRangeValue = loanAccountDelinquencyRangeDataV1.getDelinquencyRange().getClassification();//
assertThat(actualDelinquencyRangeValue)//
.as(ErrorMessageHelper.delinquencyRangeError(actualDelinquencyRangeValue, expectedDelinquencyRangeValue))//
@@ -648,7 +647,8 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
GetLoansLoanIdDelinquencySummary delinquent = loanDetails.body().getDelinquent();
eventAssertion.assertEvent(LoanDelinquencyRangeChangeEvent.class, loanId)//
.extractingData(loanAccountDelinquencyRangeDataV1 -> { //
.extractingData(loanAccountDelinquencyRangeDataV1 -> {
Long loanLevelDelinquencyRangeId = loanAccountDelinquencyRangeDataV1.getDelinquencyRange().getId();
String loanLevelDelinquencyRange = loanAccountDelinquencyRangeDataV1.getDelinquencyRange().getClassification();
String loanLevelDelinquentDate = loanAccountDelinquencyRangeDataV1.getDelinquentDate();
@@ -732,7 +732,6 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
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) {
@@ -741,6 +740,7 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
case "pausePeriodStart" ->
actualValues.add(t.getPausePeriodStart() == null ? null : FORMATTER.format(t.getPausePeriodStart()));
case "pausePeriodEnd" -> actualValues.add(t.getPausePeriodEnd() == null ? null : FORMATTER.format(t.getPausePeriodEnd()));
default -> throw new IllegalStateException(String.format("Header name %s cannot be found", headerName));
}
}
return actualValues;
@@ -57,8 +57,12 @@ public class LoanReAgingStepDef extends AbstractStepDef {
String startDate = data.get(2);
int numberOfInstallments = Integer.parseInt(data.get(3));
PostLoansLoanIdTransactionsRequest reAgingRequest = LoanRequestFactory.defaultReAgingRequest().frequencyNumber(frequencyNumber)
.frequencyType(frequencyType).startDate(startDate).numberOfInstallments(numberOfInstallments);
PostLoansLoanIdTransactionsRequest reAgingRequest = LoanRequestFactory//
.defaultReAgingRequest()//
.frequencyNumber(frequencyNumber)//
.frequencyType(frequencyType)//
.startDate(startDate)//
.numberOfInstallments(numberOfInstallments);//
Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi.executeLoanTransaction(loanId, reAgingRequest, "reAge")
.execute();
@@ -77,8 +81,12 @@ public class LoanReAgingStepDef extends AbstractStepDef {
String startDate = data.get(2);
int numberOfInstallments = Integer.parseInt(data.get(3));
PostLoansLoanIdTransactionsRequest reAgingRequest = LoanRequestFactory.defaultReAgingRequest().frequencyNumber(frequencyNumber)
.frequencyType(frequencyType).startDate(startDate).numberOfInstallments(numberOfInstallments);
PostLoansLoanIdTransactionsRequest reAgingRequest = LoanRequestFactory//
.defaultReAgingRequest()//
.frequencyNumber(frequencyNumber)//
.frequencyType(frequencyType)//
.startDate(startDate)//
.numberOfInstallments(numberOfInstallments);//
Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi
.executeLoanTransaction1(loanExternalId, reAgingRequest, "reAge").execute();
@@ -61,8 +61,8 @@ 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.messaging.store.EventStore;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
@@ -77,6 +77,7 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
public static final String DEFAULT_CHECK_NB = "1234567890";
public static final String DEFAULT_RECEIPT_NB = "1234567890";
public static final String DEFAULT_BANK_NB = "1234567890";
public static final String DEFAULT_REPAYMENT_TYPE = "AUTOPAY";
@Autowired
private LoanTransactionsApi loanTransactionsApi;
@@ -98,6 +99,9 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
@Autowired
private EventCheckHelper eventCheckHelper;
@Autowired
private EventStore eventStore;
@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);
@@ -111,6 +115,7 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
private void makeRepayment(String repaymentType, String transactionDate, double transactionAmount, String transferExternalOwnerId)
throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
@@ -132,11 +137,12 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
EventAssertion.EventAssertionBuilder<LoanTransactionDataV1> transactionEvent = eventCheckHelper
.transactionEventCheck(repaymentResponse, TransactionType.REPAYMENT, transferExternalOwnerId);
testContext().set(TestContextKey.TRANSACTION_EVENT, transactionEvent);
eventAssertion.assertEventRaised(LoanBalanceChangedEvent.class, loanId);
eventCheckHelper.loanBalanceChangedEventCheck(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 {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
@@ -164,11 +170,12 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
.executeLoanTransaction(loanId, repaymentRequest, "repayment", headerMap).execute();
testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, repaymentResponse);
ErrorHelper.checkSuccessfulApiCall(repaymentResponse);
eventAssertion.assertEventRaised(LoanBalanceChangedEvent.class, loanId);
eventCheckHelper.loanBalanceChangedEventCheck(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 {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
String resourceExternalId = loanResponse.body().getResourceExternalId();
@@ -189,12 +196,13 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, repaymentResponse);
ErrorHelper.checkSuccessfulApiCall(repaymentResponse);
eventAssertion.assertEventRaised(LoanBalanceChangedEvent.class, loanId);
eventCheckHelper.loanBalanceChangedEventCheck(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 {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
String resourceExternalId = loanResponse.body().getResourceExternalId();
@@ -223,7 +231,7 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
.executeLoanTransaction1(resourceExternalId, repaymentRequest, "repayment", headerMap).execute();
testContext().set(TestContextKey.LOAN_REPAYMENT_RESPONSE, repaymentResponse);
ErrorHelper.checkSuccessfulApiCall(repaymentResponse);
eventAssertion.assertEventRaised(LoanBalanceChangedEvent.class, loanId);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
@And("Customer not able to make {string} repayment on {string} with {double} EUR transaction amount")
@@ -282,6 +290,7 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
@When("Refund happens on {string} with {double} EUR transaction amount")
public void makeRefund(String transactionDate, double transactionAmount) throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansLoanIdTransactionsRequest refundRequest = LoanRequestFactory.defaultRefundRequest().transactionDate(transactionDate)
@@ -293,10 +302,12 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
.executeLoanTransaction(loanId, refundRequest, "payoutRefund").execute();
ErrorHelper.checkSuccessfulApiCall(refundResponse);
testContext().set(TestContextKey.LOAN_REFUND_RESPONSE, refundResponse);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
@When("Refund undo happens on {string}")
public void makeRefundUndo(String transactionDate) throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<PostLoansLoanIdTransactionsResponse> refundResponse = testContext().get(TestContextKey.LOAN_REFUND_RESPONSE);
@@ -318,10 +329,12 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getManuallyReversed())
.isEqualTo(Boolean.TRUE);
eventAssertionBuilder.extractingData(LoanTransactionAdjustmentDataV1::getNewTransactionDetail).isEqualTo(null);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
@When("Customer makes a repayment undo on {string}")
public void makeLoanRepaymentUndo(String transactionDate) throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
Response<PostLoansLoanIdTransactionsResponse> repaymentResponse = testContext().get(TestContextKey.LOAN_REPAYMENT_RESPONSE);
@@ -343,6 +356,7 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getManuallyReversed())
.isEqualTo(Boolean.TRUE);
eventAssertionBuilder.extractingData(LoanTransactionAdjustmentDataV1::getNewTransactionDetail).isEqualTo(null);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
@Then("Loan {string} transaction adjust amount {double} must return {int} code")
@@ -361,6 +375,7 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
@When("Customer undo {string}th repayment on {string}")
public void undoNthRepayment(String nthItemStr, String transactionDate) throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
List<GetLoansLoanIdTransactions> transactions = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute().body()
@@ -387,10 +402,12 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getManuallyReversed())
.isEqualTo(Boolean.TRUE);
eventAssertionBuilder.extractingData(LoanTransactionAdjustmentDataV1::getNewTransactionDetail).isEqualTo(null);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
@When("Customer undo {string}th transaction made on {string}")
public void undoNthTransaction(String nthItemStr, String transactionDate) throws IOException {
eventStore.reset();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
@@ -418,10 +435,12 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getManuallyReversed())
.isEqualTo(Boolean.TRUE);
eventAssertionBuilder.extractingData(LoanTransactionAdjustmentDataV1::getNewTransactionDetail).isEqualTo(null);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
@When("Customer undo {string}th {string} transaction made on {string}")
public void undoNthTransactionType(String nthItemStr, String transactionType, String transactionDate) throws IOException {
eventStore.reset();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
@@ -452,6 +471,7 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
loanTransactionAdjustmentDataV1 -> loanTransactionAdjustmentDataV1.getTransactionToAdjust().getManuallyReversed())
.isEqualTo(Boolean.TRUE);
eventAssertionBuilder.extractingData(LoanTransactionAdjustmentDataV1::getNewTransactionDetail).isEqualTo(null);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
@Then("Repayment transaction is created with {double} amount and {string} type")
@@ -516,6 +536,18 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
adjustNthRepaymentWithExternalOwnerCheck(nthItemStr, transactionDate, amount, null);
}
@When("Loan Pay-off is made on {string}")
public void makeLoanPayOff(String transactionDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId1 = loanResponse.body().getLoanId();
Response<GetLoansLoanIdTransactionsTemplateResponse> response = loanTransactionsApi
.retrieveTransactionTemplate(loanId1, "prepayLoan", DATE_FORMAT, transactionDate, DEFAULT_LOCALE).execute();
Double transactionAmount = response.body().getAmount();
log.info("%n--- Loan Pay-off with amount: {} ---", transactionAmount);
makeRepayment(DEFAULT_REPAYMENT_TYPE, transactionDate, transactionAmount, null);
}
private void adjustNthRepaymentWithExternalOwnerCheck(String nthItemStr, String transactionDate, String amount, String externalOwnerId)
throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
@@ -564,16 +596,4 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
}
}
@When("Loan Pay-off is made on {string}")
public void makeLoanPayOff(String transactionDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId1 = loanResponse.body().getLoanId();
Response<GetLoansLoanIdTransactionsTemplateResponse> response = loanTransactionsApi
.retrieveTransactionTemplate(loanId1, "prepayLoan", DATE_FORMAT, transactionDate, DEFAULT_LOCALE).execute();
Double transactionAmount = response.body().getAmount();
log.info("--- Loan Pay-off with amount: {} ---", transactionAmount);
makeRepayment("AUTOPAY", transactionDate, transactionAmount, null);
}
}
@@ -26,6 +26,8 @@ import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.PostCreateRescheduleLoansRequest;
@@ -48,6 +50,10 @@ import retrofit2.Response;
public class LoanRescheduleStepDef extends AbstractStepDef {
private static final Gson GSON = new JSON().getGson();
public static final String DATE_FORMAT_HU = "yyyy-MM-dd";
public static final String DATE_FORMAT_EN = "dd MMMM yyyy";
public static final DateTimeFormatter FORMATTER_HU = DateTimeFormatter.ofPattern(DATE_FORMAT_HU);
public static final DateTimeFormatter FORMATTER_EN = DateTimeFormatter.ofPattern(DATE_FORMAT_EN);
@Autowired
private RescheduleLoansApi rescheduleLoansApi;
@@ -59,13 +65,18 @@ public class LoanRescheduleStepDef extends AbstractStepDef {
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 = rescheduleData.get(3) != null ? Integer.parseInt(rescheduleData.get(3)) : null;
Integer graceOnInterest = rescheduleData.get(4) != null ? Integer.parseInt(rescheduleData.get(4)) : null;
Integer extraTerms = rescheduleData.get(5) != null ? Integer.parseInt(rescheduleData.get(5)) : null;
BigDecimal newInterestRate = rescheduleData.get(6) != null ? new BigDecimal(rescheduleData.get(6)) : null;
Integer graceOfPrincipal = (rescheduleData.get(3) == null || "0".equals(rescheduleData.get(3))) ? null
: Integer.valueOf(rescheduleData.get(3));
Integer graceOnInterest = (rescheduleData.get(4) == null || "0".equals(rescheduleData.get(4))) ? null
: Integer.valueOf(rescheduleData.get(4));
Integer extraTerms = (rescheduleData.get(5) == null || "0".equals(rescheduleData.get(5))) ? null
: Integer.valueOf(rescheduleData.get(5));
BigDecimal newInterestRate = (rescheduleData.get(6) == null || "0".equals(rescheduleData.get(6))) ? null
: new BigDecimal(rescheduleData.get(6));
PostCreateRescheduleLoansRequest request = new PostCreateRescheduleLoansRequest()//
.loanId(loanId)//
@@ -105,10 +116,13 @@ public class LoanRescheduleStepDef extends AbstractStepDef {
String rescheduleFromDate = rescheduleData.get(0);
String submittedOnDate = rescheduleData.get(1);
String adjustedDueDate = rescheduleData.get(2);
Integer graceOfPrincipal = rescheduleData.get(3) != null ? Integer.parseInt(rescheduleData.get(3)) : null;
Integer graceOnInterest = rescheduleData.get(4) != null ? Integer.parseInt(rescheduleData.get(4)) : null;
Integer extraTerms = rescheduleData.get(5) != null ? Integer.parseInt(rescheduleData.get(5)) : null;
BigDecimal newInterestRate = rescheduleData.get(6) != null ? new BigDecimal(rescheduleData.get(6)) : null;
Integer graceOfPrincipal = (rescheduleData.get(3) == null || "0".equals(rescheduleData.get(3))) ? null
: Integer.valueOf(rescheduleData.get(3));
Integer graceOnInterest = (rescheduleData.get(4) == null || "0".equals(rescheduleData.get(4))) ? null
: Integer.valueOf(rescheduleData.get(4));
Integer extraTerms = (rescheduleData.get(5) == null || "0".equals(rescheduleData.get(5))) ? null
: Integer.valueOf(rescheduleData.get(5));
BigDecimal newInterestRate = rescheduleData.get(6) == null ? null : new BigDecimal(rescheduleData.get(6));
PostCreateRescheduleLoansRequest request = new PostCreateRescheduleLoansRequest()//
.loanId(loanId)//
@@ -127,7 +141,18 @@ public class LoanRescheduleStepDef extends AbstractStepDef {
Response<PostCreateRescheduleLoansResponse> createResponse = rescheduleLoansApi.createLoanRescheduleRequest(request).execute();
LoanRescheduleErrorMessage loanRescheduleErrorMessage = LoanRescheduleErrorMessage.valueOf(errorMessageType);
String errorMessageExpected = loanRescheduleErrorMessage.getValue(loanId);
LocalDate localDate = LocalDate.parse(rescheduleFromDate, FORMATTER_EN);
String rescheduleFromDateFormatted = localDate.format(FORMATTER_HU);
String errorMessageExpected = "";
int expectedParameterCount = loanRescheduleErrorMessage.getExpectedParameterCount();
if (expectedParameterCount == 1) {
errorMessageExpected = loanRescheduleErrorMessage.getValue(loanId);
} else if (expectedParameterCount == 2) {
errorMessageExpected = loanRescheduleErrorMessage.getValue(rescheduleFromDateFormatted, loanId);
} else {
throw new IllegalStateException("Parameter count in Error message does not met the criteria");
}
String errorToString = createResponse.errorBody().string();
ErrorResponse errorResponse = GSON.fromJson(errorToString, ErrorResponse.class);
@@ -21,7 +21,7 @@ package org.apache.fineract.test.stepdef.loan;
import static org.apache.fineract.test.data.TransactionProcessingStrategyCode.ADVANCED_PAYMENT_ALLOCATION;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.Assert.assertNotNull;
import com.google.gson.Gson;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
@@ -63,6 +63,7 @@ import org.apache.fineract.client.models.GetLoansLoanIdTimeline;
import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
import org.apache.fineract.client.models.IsCatchUpRunningResponse;
import org.apache.fineract.client.models.PaymentAllocationOrder;
import org.apache.fineract.client.models.PostClientsResponse;
import org.apache.fineract.client.models.PostLoansLoanIdRequest;
import org.apache.fineract.client.models.PostLoansLoanIdResponse;
@@ -100,10 +101,13 @@ import org.apache.fineract.test.helper.Utils;
import org.apache.fineract.test.initializer.global.LoanProductGlobalInitializerStep;
import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.EventCheckHelper;
import org.apache.fineract.test.messaging.event.loan.LoanRescheduledDueAdjustScheduleEvent;
import org.apache.fineract.test.messaging.event.loan.LoanStatusChangedEvent;
import org.apache.fineract.test.messaging.event.loan.transaction.LoanAccrualTransactionCreatedBusinessEvent;
import org.apache.fineract.test.messaging.event.loan.transaction.LoanChargeOffEvent;
import org.apache.fineract.test.messaging.event.loan.transaction.LoanChargeOffUndoEvent;
import org.apache.fineract.test.messaging.event.loan.transaction.LoanTransactionAccrualActivityPostEvent;
import org.apache.fineract.test.messaging.store.EventStore;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.beans.factory.annotation.Autowired;
@@ -117,6 +121,8 @@ public class LoanStepDef extends AbstractStepDef {
public static final String DEFAULT_LOCALE = "en";
public static final String LOAN_STATE_SUBMITTED_AND_PENDING = "Submitted and pending approval";
public static final String LOAN_STATE_APPROVED = "Approved";
public static final String LOAN_STATE_REJECTED = "Rejected";
public static final String LOAN_STATE_WITHDRAWN = "Withdrawn by applicant";
public static final String LOAN_STATE_ACTIVE = "Active";
private static final Gson GSON = new JSON().getGson();
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT);
@@ -149,6 +155,9 @@ public class LoanStepDef extends AbstractStepDef {
@Autowired
private LoanProductsApi loanProductsApi;
@Autowired
private EventStore eventStore;
@When("Admin creates a new Loan")
public void createLoan() throws IOException {
Response<PostClientsResponse> clientResponse = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE);
@@ -240,6 +249,7 @@ public class LoanStepDef extends AbstractStepDef {
private void createTransactionWithIdempotencyKeyAndExternalOwnerCheck(String transactionTypeInput, String transactionPaymentType,
String transactionDate, double transactionAmount, String externalOwnerId) throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
@@ -262,11 +272,13 @@ public class LoanStepDef extends AbstractStepDef {
ErrorHelper.checkSuccessfulApiCall(paymentTransactionResponse);
eventCheckHelper.transactionEventCheck(paymentTransactionResponse, transactionType, externalOwnerId);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
@When("Admin makes {string} transaction with {string} payment type on {string} with {double} EUR transaction amount")
public void createTransactionForRefund(String transactionTypeInput, String transactionPaymentType, String transactionDate,
double transactionAmount) throws IOException, InterruptedException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
@@ -284,6 +296,7 @@ public class LoanStepDef extends AbstractStepDef {
ErrorHelper.checkSuccessfulApiCall(paymentTransactionResponse);
eventCheckHelper.transactionEventCheck(paymentTransactionResponse, transactionType, null);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
@When("Customer makes {string} transaction with {string} payment type on {string} with {double} EUR transaction amount and system-generated Idempotency key")
@@ -303,6 +316,7 @@ public class LoanStepDef extends AbstractStepDef {
private void createTransactionWithAutoIdempotencyKeyAndWithExternalOwner(String transactionTypeInput, String transactionPaymentType,
String transactionDate, double transactionAmount, String externalOwnerId) throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
@@ -321,10 +335,12 @@ public class LoanStepDef extends AbstractStepDef {
ErrorHelper.checkSuccessfulApiCall(paymentTransactionResponse);
eventCheckHelper.transactionEventCheck(paymentTransactionResponse, transactionType, externalOwnerId);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
@When("Admin makes Credit Balance Refund transaction on {string} with {double} EUR transaction amount")
public void createCBR(String transactionDate, double transactionAmount) throws IOException {
eventStore.reset();
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
@@ -337,6 +353,7 @@ public class LoanStepDef extends AbstractStepDef {
.executeLoanTransaction(loanId, paymentTransactionRequest, transactionTypeValue).execute();
testContext().set(TestContextKey.LOAN_PAYMENT_TRANSACTION_RESPONSE, paymentTransactionResponse);
ErrorHelper.checkSuccessfulApiCall(paymentTransactionResponse);
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
@Then("Credit Balance Refund transaction on future date {string} with {double} EUR transaction amount will result an error")
@@ -444,7 +461,8 @@ public class LoanStepDef extends AbstractStepDef {
.expectedDisbursementDate(submitDate)//
.graceOnPrincipalPayment(graceOnPrincipalPayment)//
.graceOnInterestPayment(graceOnInterestPayment)//
.graceOnInterestPayment(graceOnInterestCharged).transactionProcessingStrategyCode(transactionProcessingStrategyCodeValue)//
.graceOnInterestPayment(graceOnInterestCharged)//
.transactionProcessingStrategyCode(transactionProcessingStrategyCodeValue)//
.fixedLength(fixedLength);//
Response<PostLoansResponse> response = loansApi.calculateLoanScheduleOrSubmitLoanApplication(loansRequest, "").execute();
@@ -454,6 +472,84 @@ public class LoanStepDef extends AbstractStepDef {
eventCheckHelper.createLoanEventCheck(response);
}
@When("Trying to create a fully customized loan with fixed length {int} and with the following data will result a {int} ERROR:")
public void createFullyCustomizedLoanFixedLengthError(int fixedLength, int errorCodeExpected, DataTable table) throws IOException {
List<List<String>> data = table.asLists();
List<String> loanData = data.get(1);
String loanProduct = loanData.get(0);
String submitDate = loanData.get(1);
String principal = loanData.get(2);
BigDecimal interestRate = new BigDecimal(loanData.get(3));
String interestType = loanData.get(4);
String interestCalculationPeriod = loanData.get(5);
String amortizationType = loanData.get(6);
Integer loanTermFrequency = Integer.valueOf(loanData.get(7));
String loanTermFrequencyType = loanData.get(8);
Integer repaymentFrequency = Integer.valueOf(loanData.get(9));
String repaymentFrequencyType = loanData.get(10);
Integer numberOfRepayments = Integer.valueOf(loanData.get(11));
Integer graceOnPrincipalPayment = Integer.valueOf(loanData.get(12));
Integer graceOnInterestPayment = Integer.valueOf(loanData.get(13));
Integer graceOnInterestCharged = Integer.valueOf(loanData.get(14));
String transactionProcessingStrategyCode = loanData.get(15);
Response<PostClientsResponse> clientResponse = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE);
Long clientId = clientResponse.body().getClientId();
DefaultLoanProduct product = DefaultLoanProduct.valueOf(loanProduct);
Long loanProductId = loanProductResolver.resolve(product);
LoanTermFrequencyType termFrequencyType = LoanTermFrequencyType.valueOf(loanTermFrequencyType);
Integer loanTermFrequencyTypeValue = termFrequencyType.getValue();
RepaymentFrequencyType repaymentFrequencyType1 = RepaymentFrequencyType.valueOf(repaymentFrequencyType);
Integer repaymentFrequencyTypeValue = repaymentFrequencyType1.getValue();
InterestType interestType1 = InterestType.valueOf(interestType);
Integer interestTypeValue = interestType1.getValue();
InterestCalculationPeriodTime interestCalculationPeriod1 = InterestCalculationPeriodTime.valueOf(interestCalculationPeriod);
Integer interestCalculationPeriodValue = interestCalculationPeriod1.getValue();
AmortizationType amortizationType1 = AmortizationType.valueOf(amortizationType);
Integer amortizationTypeValue = amortizationType1.getValue();
TransactionProcessingStrategyCode processingStrategyCode = TransactionProcessingStrategyCode
.valueOf(transactionProcessingStrategyCode);
String transactionProcessingStrategyCodeValue = processingStrategyCode.getValue();
PostLoansRequest loansRequest = loanRequestFactory.defaultLoansRequest(clientId)//
.productId(loanProductId)//
.principal(new BigDecimal(principal))//
.interestRatePerPeriod(interestRate)//
.interestType(interestTypeValue)//
.interestCalculationPeriodType(interestCalculationPeriodValue)//
.amortizationType(amortizationTypeValue)//
.loanTermFrequency(loanTermFrequency)//
.loanTermFrequencyType(loanTermFrequencyTypeValue)//
.numberOfRepayments(numberOfRepayments)//
.repaymentEvery(repaymentFrequency)//
.repaymentFrequencyType(repaymentFrequencyTypeValue)//
.submittedOnDate(submitDate)//
.expectedDisbursementDate(submitDate)//
.graceOnPrincipalPayment(graceOnPrincipalPayment)//
.graceOnInterestPayment(graceOnInterestPayment)//
.graceOnInterestPayment(graceOnInterestCharged)//
.transactionProcessingStrategyCode(transactionProcessingStrategyCodeValue)//
.fixedLength(fixedLength);//
Response<PostLoansResponse> response = loansApi.calculateLoanScheduleOrSubmitLoanApplication(loansRequest, "").execute();
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);
log.info("ERROR CODE: {}", errorCodeActual);
log.info("ERROR MESSAGE: {}", errorMessageActual);
}
@When("Admin creates a fully customized loan with Advanced payment allocation and with product no Advanced payment allocation set results an error:")
public void createFullyCustomizedLoanNoAdvancedPaymentError(DataTable table) throws IOException {
int errorCodeExpected = 403;
@@ -519,7 +615,8 @@ public class LoanStepDef extends AbstractStepDef {
.expectedDisbursementDate(submitDate)//
.graceOnPrincipalPayment(graceOnPrincipalPayment)//
.graceOnInterestPayment(graceOnInterestPayment)//
.graceOnInterestPayment(graceOnInterestCharged).transactionProcessingStrategyCode(transactionProcessingStrategyCodeValue);//
.graceOnInterestPayment(graceOnInterestCharged)//
.transactionProcessingStrategyCode(transactionProcessingStrategyCodeValue);//
Response<PostLoansResponse> response = loansApi.calculateLoanScheduleOrSubmitLoanApplication(loansRequest, "").execute();
int errorCodeActual = response.code();
@@ -535,6 +632,79 @@ public class LoanStepDef extends AbstractStepDef {
log.info("ERROR MESSAGE: {}", errorMessageActual);
}
@When("Admin creates a fully customized loan with installment level delinquency and with the following data:")
public void createFullyCustomizedLoanWithInstallmentLvlDelinquency(DataTable table) throws IOException {
List<List<String>> data = table.asLists();
List<String> loanData = data.get(1);
String loanProduct = loanData.get(0);
String submitDate = loanData.get(1);
String principal = loanData.get(2);
BigDecimal interestRate = new BigDecimal(loanData.get(3));
String interestType = loanData.get(4);
String interestCalculationPeriod = loanData.get(5);
String amortizationType = loanData.get(6);
Integer loanTermFrequency = Integer.valueOf(loanData.get(7));
String loanTermFrequencyType = loanData.get(8);
Integer repaymentFrequency = Integer.valueOf(loanData.get(9));
String repaymentFrequencyType = loanData.get(10);
Integer numberOfRepayments = Integer.valueOf(loanData.get(11));
Integer graceOnPrincipalPayment = Integer.valueOf(loanData.get(12));
Integer graceOnInterestPayment = Integer.valueOf(loanData.get(13));
Integer graceOnInterestCharged = Integer.valueOf(loanData.get(14));
String transactionProcessingStrategyCode = loanData.get(15);
Response<PostClientsResponse> clientResponse = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE);
Long clientId = clientResponse.body().getClientId();
DefaultLoanProduct product = DefaultLoanProduct.valueOf(loanProduct);
Long loanProductId = loanProductResolver.resolve(product);
LoanTermFrequencyType termFrequencyType = LoanTermFrequencyType.valueOf(loanTermFrequencyType);
Integer loanTermFrequencyTypeValue = termFrequencyType.getValue();
RepaymentFrequencyType repaymentFrequencyType1 = RepaymentFrequencyType.valueOf(repaymentFrequencyType);
Integer repaymentFrequencyTypeValue = repaymentFrequencyType1.getValue();
InterestType interestType1 = InterestType.valueOf(interestType);
Integer interestTypeValue = interestType1.getValue();
InterestCalculationPeriodTime interestCalculationPeriod1 = InterestCalculationPeriodTime.valueOf(interestCalculationPeriod);
Integer interestCalculationPeriodValue = interestCalculationPeriod1.getValue();
AmortizationType amortizationType1 = AmortizationType.valueOf(amortizationType);
Integer amortizationTypeValue = amortizationType1.getValue();
TransactionProcessingStrategyCode processingStrategyCode = TransactionProcessingStrategyCode
.valueOf(transactionProcessingStrategyCode);
String transactionProcessingStrategyCodeValue = processingStrategyCode.getValue();
PostLoansRequest loansRequest = loanRequestFactory.defaultLoansRequest(clientId)//
.productId(loanProductId)//
.principal(new BigDecimal(principal))//
.interestRatePerPeriod(interestRate)//
.interestType(interestTypeValue)//
.interestCalculationPeriodType(interestCalculationPeriodValue)//
.amortizationType(amortizationTypeValue)//
.loanTermFrequency(loanTermFrequency)//
.loanTermFrequencyType(loanTermFrequencyTypeValue)//
.numberOfRepayments(numberOfRepayments)//
.repaymentEvery(repaymentFrequency)//
.repaymentFrequencyType(repaymentFrequencyTypeValue)//
.submittedOnDate(submitDate)//
.expectedDisbursementDate(submitDate)//
.graceOnPrincipalPayment(graceOnPrincipalPayment)//
.graceOnInterestPayment(graceOnInterestPayment)//
.graceOnInterestPayment(graceOnInterestCharged)//
.transactionProcessingStrategyCode(transactionProcessingStrategyCodeValue)//
.enableInstallmentLevelDelinquency(true);//
Response<PostLoansResponse> response = loansApi.calculateLoanScheduleOrSubmitLoanApplication(loansRequest, "").execute();
testContext().set(TestContextKey.LOAN_CREATE_RESPONSE, response);
ErrorHelper.checkSuccessfulApiCall(response);
eventCheckHelper.createLoanEventCheck(response);
}
@Then("Loan details has the following last payment related data:")
public void checkLastPaymentData(DataTable table) throws IOException {
List<List<String>> data = table.asLists();
@@ -730,6 +900,37 @@ public class LoanStepDef extends AbstractStepDef {
eventCheckHelper.approveLoanEventCheck(loanApproveResponse);
}
@And("Admin successfully rejects the loan on {string}")
public void rejectLoan(String rejectDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansLoanIdRequest rejectRequest = LoanRequestFactory.defaultLoanRejectRequest().rejectedOnDate(rejectDate);
Response<PostLoansLoanIdResponse> loanRejectResponse = loansApi.stateTransitions(loanId, rejectRequest, "reject").execute();
testContext().set(TestContextKey.LOAN_REJECT_RESPONSE, loanRejectResponse);
ErrorHelper.checkSuccessfulApiCall(loanRejectResponse);
assertThat(loanRejectResponse.body().getChanges().getStatus().getValue()).isEqualTo(LOAN_STATE_REJECTED);
assertThat(loanRejectResponse.body().getChanges().getStatus().getValue()).isEqualTo(LOAN_STATE_REJECTED);
eventCheckHelper.loanRejectedEventCheck(loanRejectResponse);
}
@And("Admin successfully withdrawn the loan on {string}")
public void withdrawnLoan(String withdrawnDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansLoanIdRequest withdawnRequest = LoanRequestFactory.defaultLoanWithdrawnRequest().withdrawnOnDate(withdrawnDate);
Response<PostLoansLoanIdResponse> loanWithdrawnResponse = loansApi.stateTransitions(loanId, withdawnRequest, "withdrawnByApplicant")
.execute();
testContext().set(TestContextKey.LOAN_WITHDRAWN_RESPONSE, loanWithdrawnResponse);
ErrorHelper.checkSuccessfulApiCall(loanWithdrawnResponse);
assertThat(loanWithdrawnResponse.body().getChanges().getStatus().getValue()).isEqualTo(LOAN_STATE_WITHDRAWN);
assertThat(loanWithdrawnResponse.body().getChanges().getStatus().getValue()).isEqualTo(LOAN_STATE_WITHDRAWN);
eventCheckHelper.undoApproveLoanEventCheck(loanWithdrawnResponse);
}
@And("Admin successfully approves the second loan on {string} with {string} amount and expected disbursement date on {string}")
public void approveSecondLoan(String approveDate, String approvedAmount, String expectedDisbursementDate) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_SECOND_LOAN_RESPONSE);
@@ -785,10 +986,31 @@ public class LoanStepDef extends AbstractStepDef {
@And("Admin successfully disburse the loan on {string} with {string} EUR transaction amount")
public void disburseLoan(String actualDisbursementDate, String transactionAmount) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
assertNotNull(loanResponse.body());
final long loanId = loanResponse.body().getLoanId();
final PostLoansLoanIdRequest disburseRequest = LoanRequestFactory.defaultLoanDisburseRequest()
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansLoanIdRequest disburseRequest = LoanRequestFactory.defaultLoanDisburseRequest()
.actualDisbursementDate(actualDisbursementDate).transactionAmount(new BigDecimal(transactionAmount));
Response<PostLoansLoanIdResponse> loanDisburseResponse = loansApi.stateTransitions(loanId, disburseRequest, "disburse").execute();
testContext().set(TestContextKey.LOAN_DISBURSE_RESPONSE, loanDisburseResponse);
ErrorHelper.checkSuccessfulApiCall(loanDisburseResponse);
Long statusActual = loanDisburseResponse.body().getChanges().getStatus().getId();
Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "", "", "").execute();
Long statusExpected = Long.valueOf(loanDetails.body().getStatus().getId());
assertThat(statusActual)//
.as(ErrorMessageHelper.wrongLoanStatus(Math.toIntExact(statusActual), Math.toIntExact(statusExpected)))//
.isEqualTo(statusExpected);//
eventCheckHelper.disburseLoanEventCheck(loanId);
eventCheckHelper.loanDisbursalTransactionEventCheck(loanDisburseResponse);
}
@And("Admin successfully disburse the loan without auto downpayment on {string} with {string} EUR transaction amount")
public void disburseLoanWithoutAutoDownpayment(String actualDisbursementDate, String transactionAmount) throws IOException {
Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanResponse.body().getLoanId();
PostLoansLoanIdRequest disburseRequest = LoanRequestFactory.defaultLoanDisburseRequest()
.actualDisbursementDate(actualDisbursementDate).transactionAmount(new BigDecimal(transactionAmount));
performLoanDisbursementAndVerifyStatus(loanId, disburseRequest);
}
@@ -829,7 +1051,7 @@ public class LoanStepDef extends AbstractStepDef {
ErrorHelper.checkSuccessfulApiCall(loanDisburseResponse);
assertThat(loanDisburseResponse.body().getChanges().getStatus().getValue()).isEqualTo(LOAN_STATE_ACTIVE);
eventCheckHelper.disburseLoanEventCheck(loanDisburseResponse);
eventCheckHelper.disburseLoanEventCheck(loanId);
eventCheckHelper.loanDisbursalTransactionEventCheck(loanDisburseResponse);
}
@@ -1141,10 +1363,13 @@ public class LoanStepDef extends AbstractStepDef {
List<List<String>> data = table.asLists();
for (int i = 1; i < data.size(); i++) {
List<String> expectedValues = data.get(i);
String transactionDateExpected = expectedValues.get(0);
List<List<String>> actualValuesList = transactions.stream()//
.filter(t -> transactionDateExpected.equals(FORMATTER.format(t.getDate())))//
.map(t -> fetchValuesOfTransaction(table.row(0), t))//
.collect(Collectors.toList());//
boolean containsExpectedValues = actualValuesList.get(i - 1).equals(expectedValues);//
boolean containsExpectedValues = actualValuesList.stream()//
.anyMatch(actualValues -> actualValues.equals(expectedValues));//
assertThat(containsExpectedValues).as(ErrorMessageHelper.wrongValueInLineInTransactionsTab(i, actualValuesList, expectedValues))
.isTrue();
}
@@ -1319,7 +1544,7 @@ public class LoanStepDef extends AbstractStepDef {
assertThat(allInstallmentsObligationsMet).isTrue();
}
@Then("Loan closedon_date is {}")
@Then("Loan closedon_date is {string}")
public void loanClosedonDate(String date) throws IOException {
Response<PostLoansResponse> loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanCreateResponse.body().getLoanId();
@@ -1327,10 +1552,10 @@ public class LoanStepDef extends AbstractStepDef {
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
testContext().set(TestContextKey.LOAN_RESPONSE, loanDetailsResponse);
if (date == null || "null".equals(date)) {
if ("null".equals(date)) {
assertThat(loanDetailsResponse.body().getTimeline().getClosedOnDate()).isNull();
} else {
assertThat(loanDetailsResponse.body().getTimeline().getClosedOnDate()).isEqualTo(date);
assertThat(FORMATTER.format(loanDetailsResponse.body().getTimeline().getClosedOnDate())).isEqualTo(date);
}
}
@@ -1555,7 +1780,7 @@ public class LoanStepDef extends AbstractStepDef {
IsCatchUpRunningResponse isCatchUpRunning = isCatchUpRunningResponse.body();
return isCatchUpRunning.getIsCatchUpRunning();
});
await().pollInterval(2, TimeUnit.SECONDS).atMost(Duration.ofSeconds(240)).until(() -> {
await().pollInterval(2, TimeUnit.SECONDS).atMost(Duration.ofMinutes(4)).until(() -> {
Response<IsCatchUpRunningResponse> isCatchUpRunningResponse = loanCobCatchUpApi.isCatchUpRunning().execute();
ErrorHelper.checkSuccessfulApiCall(isCatchUpRunningResponse);
IsCatchUpRunningResponse isCatchUpRunning = isCatchUpRunningResponse.body();
@@ -1596,6 +1821,31 @@ public class LoanStepDef extends AbstractStepDef {
eventAssertion.assertEventRaised(LoanAccrualTransactionCreatedBusinessEvent.class, accrualTransactionId);
}
@Then("LoanTransactionAccrualActivityPostBusinessEvent is raised on {string}")
public void checkLoanTransactionAccrualActivityPostBusinessEvent(String date) throws IOException {
Response<PostLoansResponse> loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanCreateResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions();
GetLoansLoanIdTransactions accrualTransaction = transactions.stream()
.filter(t -> date.equals(FORMATTER.format(t.getDate())) && "Accrual Activity".equals(t.getType().getValue())).findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("No Accrual activity transaction found on %s", date)));
Long accrualTransactionId = accrualTransaction.getId();
eventAssertion.assertEventRaised(LoanTransactionAccrualActivityPostEvent.class, accrualTransactionId);
}
@Then("LoanRescheduledDueAdjustScheduleBusinessEvent is raised on {string}")
public void checkLoanRescheduledDueAdjustScheduleBusinessEvent(String date) throws IOException {
Response<PostLoansResponse> loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanCreateResponse.body().getLoanId();
eventAssertion.assertEventRaised(LoanRescheduledDueAdjustScheduleEvent.class, loanId);
}
@Then("Loan details and event has the following last repayment related data:")
public void checkLastRepaymentData(DataTable table) throws IOException {
List<List<String>> data = table.asLists();
@@ -1649,7 +1899,6 @@ public class LoanStepDef extends AbstractStepDef {
return null;
});
}
@And("Admin does a charge-off undo the loan with reversal external Id")
@@ -1741,7 +1990,7 @@ public class LoanStepDef extends AbstractStepDef {
String futureInstallmentAllocationRuleNew) throws IOException {
DefaultLoanProduct product = DefaultLoanProduct.valueOf(loanProductName);
Long loanProductId = loanProductResolver.resolve(product);
log.info("loanProductId {}", loanProductId);
log.info("loanProductId: {}", loanProductId);
Response<GetLoanProductsProductIdResponse> loanProductDetails = loanProductsApi.retrieveLoanProductDetails(loanProductId).execute();
ErrorHelper.checkSuccessfulApiCall(loanProductDetails);
@@ -1751,11 +2000,12 @@ public class LoanStepDef extends AbstractStepDef {
paymentAllocation.forEach(e -> {
String transactionTypeOriginal = e.getTransactionType();
String futureInstallmentAllocationRule = e.getFutureInstallmentAllocationRule();
List<PaymentAllocationOrder> paymentAllocationOrder = e.getPaymentAllocationOrder();
if (transactionTypeToChange.equals(transactionTypeOriginal)) {
futureInstallmentAllocationRule = futureInstallmentAllocationRuleNew;
}
newPaymentAllocation.add(
LoanProductGlobalInitializerStep.createPaymentAllocation(transactionTypeOriginal, futureInstallmentAllocationRule));
newPaymentAllocation.add(LoanProductGlobalInitializerStep.editPaymentAllocationFutureInstallment(transactionTypeOriginal,
futureInstallmentAllocationRule, paymentAllocationOrder));
});
PutLoanProductsProductIdRequest putLoanProductsProductIdRequest = new PutLoanProductsProductIdRequest()
@@ -1770,7 +2020,7 @@ public class LoanStepDef extends AbstractStepDef {
public void editRepaymentStartDateType(String loanProductName, String repaymentStartDateType) throws IOException {
DefaultLoanProduct product = DefaultLoanProduct.valueOf(loanProductName);
Long loanProductId = loanProductResolver.resolve(product);
log.info("loanProductId {}", loanProductId);
log.info("loanProductId: {}", loanProductId);
Map<String, Integer> repaymentStartDateTypeMap = Map.of("DISBURSEMENT_DATE", 1, "SUBMITTED_ON_DATE", 2);
@@ -1822,7 +2072,6 @@ public class LoanStepDef extends AbstractStepDef {
.adjustLoanTransaction(loanId, targetTransaction.getId(), transactionUndoRequest, "").execute();
ErrorResponse errorDetails = ErrorResponse.from(transactionUndoResponse);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(503);
}
@Then("Loan {string} repayment transaction on {string} with {double} EUR transaction amount results in error")
@@ -1843,7 +2092,6 @@ public class LoanStepDef extends AbstractStepDef {
ErrorResponse errorDetails = ErrorResponse.from(repaymentResponse);
assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(400);
}
@Then("Loan details has the downpayment amount {string} in summary.totalRepaymentTransaction")
@@ -2036,7 +2284,7 @@ public class LoanStepDef extends AbstractStepDef {
assertThat(statusActual).as(ErrorMessageHelper.wrongLoanStatus(Math.toIntExact(statusActual), Math.toIntExact(statusExpected)))
.isEqualTo(statusExpected);
eventCheckHelper.disburseLoanEventCheck(loanDisburseResponse);
eventCheckHelper.disburseLoanEventCheck(loanId);
eventCheckHelper.loanDisbursalTransactionEventCheck(loanDisburseResponse);
}
@@ -2114,7 +2362,6 @@ public class LoanStepDef extends AbstractStepDef {
return result;
}
@SuppressFBWarnings("SF_SWITCH_NO_DEFAULT")
private List<String> fetchValuesOfTransaction(List<String> header, GetLoansLoanIdTransactions t) {
List<String> actualValues = new ArrayList<>();
for (String headerName : header) {
@@ -2132,12 +2379,16 @@ public class LoanStepDef extends AbstractStepDef {
case "Overpayment" ->
actualValues.add(t.getOverpaymentPortion() == null ? null : String.valueOf(t.getOverpaymentPortion()));
case "Reverted" -> actualValues.add(t.getManuallyReversed() == null ? null : String.valueOf(t.getManuallyReversed()));
case "Replayed" -> {
boolean hasReplayed = t.getTransactionRelations().stream().anyMatch(e -> "REPLAYED".equals(e.getRelationType()));
actualValues.add(hasReplayed ? "true" : "false");
}
default -> throw new IllegalStateException(String.format("Header name %s cannot be found", headerName));
}
}
return actualValues;
}
@SuppressFBWarnings("SF_SWITCH_NO_DEFAULT")
private List<String> fetchValuesOfRepaymentSchedule(List<String> header, GetLoansLoanIdRepaymentPeriod repaymentPeriod) {
List<String> actualValues = new ArrayList<>();
for (String headerName : header) {
@@ -2171,6 +2422,7 @@ public class LoanStepDef extends AbstractStepDef {
: String.valueOf(repaymentPeriod.getTotalWaivedForPeriod()));
case "Outstanding" -> actualValues.add(repaymentPeriod.getTotalOutstandingForPeriod() == null ? null
: String.valueOf(repaymentPeriod.getTotalOutstandingForPeriod()));
default -> throw new IllegalStateException(String.format("Header name %s cannot be found", headerName));
}
}
return actualValues;
@@ -0,0 +1,184 @@
/**
* 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.saving;
import io.cucumber.java.en.And;
import java.io.IOException;
import java.math.BigDecimal;
import org.apache.fineract.client.models.PostClientsResponse;
import org.apache.fineract.client.models.PostSavingsAccountTransactionsRequest;
import org.apache.fineract.client.models.PostSavingsAccountTransactionsResponse;
import org.apache.fineract.client.models.PostSavingsAccountsAccountIdRequest;
import org.apache.fineract.client.models.PostSavingsAccountsAccountIdResponse;
import org.apache.fineract.client.models.PostSavingsAccountsRequest;
import org.apache.fineract.client.models.PostSavingsAccountsResponse;
import org.apache.fineract.client.services.SavingsAccountApi;
import org.apache.fineract.client.services.SavingsAccountTransactionsApi;
import org.apache.fineract.test.factory.SavingsAccountRequestFactory;
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 SavingsAccountStepDef extends AbstractStepDef {
@Autowired
private SavingsAccountTransactionsApi savingsAccountTransactionsApi;
@Autowired
private SavingsAccountApi savingsAccountApi;
@And("Client creates a new EUR savings account with {string} submitted on date")
public void createSavingsAccountEUR(String submittedOnDate) throws IOException {
Response<PostClientsResponse> clientResponse = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE);
long clientId = clientResponse.body().getClientId();
PostSavingsAccountsRequest createSavingsAccountRequest = SavingsAccountRequestFactory.defaultEURSavingsAccountRequest()
.clientId((int) clientId).submittedOnDate(submittedOnDate);
Response<PostSavingsAccountsResponse> createSavingsAccountResponse = savingsAccountApi
.submitApplication2(createSavingsAccountRequest).execute();
testContext().set(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE, createSavingsAccountResponse);
}
@And("Client creates a new USD savings account with {string} submitted on date")
public void createSavingsAccountUSD(String submittedOnDate) throws IOException {
Response<PostClientsResponse> clientResponse = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE);
long clientId = clientResponse.body().getClientId();
PostSavingsAccountsRequest createSavingsAccountRequest = SavingsAccountRequestFactory.defaultUSDSavingsAccountRequest()
.clientId((int) clientId).submittedOnDate(submittedOnDate);
Response<PostSavingsAccountsResponse> createSavingsAccountResponse = savingsAccountApi
.submitApplication2(createSavingsAccountRequest).execute();
testContext().set(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE, createSavingsAccountResponse);
}
@And("Approve EUR savings account on {string} date")
public void approveEurSavingsAccount(String approvedOnDate) throws IOException {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext()
.get(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
PostSavingsAccountsAccountIdRequest approveSavingsAccountRequest = SavingsAccountRequestFactory.defaultApproveRequest()
.approvedOnDate(approvedOnDate);
Response<PostSavingsAccountsAccountIdResponse> approveSavingsAccountResponse = savingsAccountApi
.handleCommands6(savingsAccountID, approveSavingsAccountRequest, "approve").execute();
testContext().set(TestContextKey.EUR_SAVINGS_ACCOUNT_APPROVE_RESPONSE, approveSavingsAccountResponse);
}
@And("Approve USD savings account on {string} date")
public void approveUsdSavingsAccount(String approvedOnDate) throws IOException {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext()
.get(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
PostSavingsAccountsAccountIdRequest approveSavingsAccountRequest = SavingsAccountRequestFactory.defaultApproveRequest()
.approvedOnDate(approvedOnDate);
Response<PostSavingsAccountsAccountIdResponse> approveSavingsAccountResponse = savingsAccountApi
.handleCommands6(savingsAccountID, approveSavingsAccountRequest, "approve").execute();
testContext().set(TestContextKey.USD_SAVINGS_ACCOUNT_APPROVE_RESPONSE, approveSavingsAccountResponse);
}
@And("Activate EUR savings account on {string} date")
public void activateSavingsAccount(String activatedOnDate) throws IOException {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext()
.get(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
PostSavingsAccountsAccountIdRequest activateSavingsAccountRequest = SavingsAccountRequestFactory.defaultActivateRequest()
.activatedOnDate(activatedOnDate);
Response<PostSavingsAccountsAccountIdResponse> activateSavingsAccountResponse = savingsAccountApi
.handleCommands6(savingsAccountID, activateSavingsAccountRequest, "activate").execute();
testContext().set(TestContextKey.EUR_SAVINGS_ACCOUNT_ACTIVATED_RESPONSE, activateSavingsAccountResponse);
}
@And("Activate USD savings account on {string} date")
public void activateUsdSavingsAccount(String activatedOnDate) throws IOException {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext()
.get(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
PostSavingsAccountsAccountIdRequest activateSavingsAccountRequest = SavingsAccountRequestFactory.defaultActivateRequest()
.activatedOnDate(activatedOnDate);
Response<PostSavingsAccountsAccountIdResponse> activateSavingsAccountResponse = savingsAccountApi
.handleCommands6(savingsAccountID, activateSavingsAccountRequest, "activate").execute();
testContext().set(TestContextKey.USD_SAVINGS_ACCOUNT_ACTIVATED_RESPONSE, activateSavingsAccountResponse);
}
@And("Client successfully deposits {double} EUR to the savings account on {string} date")
public void createEurDeposit(double depositAmount, String depositDate) throws IOException {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext()
.get(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
PostSavingsAccountTransactionsRequest depositRequest = SavingsAccountRequestFactory.defaultDepositRequest()
.transactionDate(depositDate).transactionAmount(BigDecimal.valueOf(depositAmount));
Response<PostSavingsAccountTransactionsResponse> depositResponse = savingsAccountTransactionsApi
.transaction2(savingsAccountID, depositRequest, "deposit").execute();
testContext().set(TestContextKey.EUR_SAVINGS_ACCOUNT_DEPOSIT_RESPONSE, depositResponse);
}
@And("Client successfully deposits {double} USD to the savings account on {string} date")
public void createUsdDeposit(double depositAmount, String depositDate) throws IOException {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext()
.get(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
PostSavingsAccountTransactionsRequest depositRequest = SavingsAccountRequestFactory.defaultDepositRequest()
.transactionDate(depositDate).transactionAmount(BigDecimal.valueOf(depositAmount));
Response<PostSavingsAccountTransactionsResponse> depositResponse = savingsAccountTransactionsApi
.transaction2(savingsAccountID, depositRequest, "deposit").execute();
testContext().set(TestContextKey.USD_SAVINGS_ACCOUNT_DEPOSIT_RESPONSE, depositResponse);
}
@And("Client successfully withdraw {double} EUR from the savings account on {string} date")
public void createEurWithdraw(double withdrawAmount, String transcationDate) throws IOException {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext()
.get(TestContextKey.EUR_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
PostSavingsAccountTransactionsRequest withdrawRequest = SavingsAccountRequestFactory.defaultWithdrawRequest()
.transactionDate(transcationDate).transactionAmount(BigDecimal.valueOf(withdrawAmount));
Response<PostSavingsAccountTransactionsResponse> withdrawalResponse = savingsAccountTransactionsApi
.transaction2(savingsAccountID, withdrawRequest, "withdrawal").execute();
testContext().set(TestContextKey.EUR_SAVINGS_ACCOUNT_WITHDRAW_RESPONSE, withdrawalResponse);
}
@And("Client successfully withdraw {double} USD from the savings account on {string} date")
public void createUsdWithdraw(double withdrawAmount, String transcationDate) throws IOException {
Response<PostSavingsAccountsResponse> savingsAccountResponse = testContext()
.get(TestContextKey.USD_SAVINGS_ACCOUNT_CREATE_RESPONSE);
long savingsAccountID = savingsAccountResponse.body().getSavingsId();
PostSavingsAccountTransactionsRequest withdrawRequest = SavingsAccountRequestFactory.defaultWithdrawRequest()
.transactionDate(transcationDate).transactionAmount(BigDecimal.valueOf(withdrawAmount));
Response<PostSavingsAccountTransactionsResponse> withdrawalResponse = savingsAccountTransactionsApi
.transaction2(savingsAccountID, withdrawRequest, "withdrawal").execute();
testContext().set(TestContextKey.USD_SAVINGS_ACCOUNT_WITHDRAW_RESPONSE, withdrawalResponse);
}
}
@@ -25,7 +25,8 @@ import java.util.Map;
public enum TestContext {
INSTANCE;
INSTANCE, //
GLOBAL;//
@SuppressWarnings("ImmutableEnumChecker")
private final ThreadLocal<Map<String, Object>> testContexts = withInitial(HashMap::new);
@@ -24,6 +24,7 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public abstract class TestContextKey {
public static final String ORIGINAL_COB_WORKFLOW_JOB_BUSINESS_STEP_LIST = "originalCOBWorkflowJobBusinessStepList";
public static final String CLIENT_CREATE_RESPONSE = "clientCreateResponse";
public static final String CLIENT_CREATE_SECOND_CLIENT_RESPONSE = "clientCreateSecondClientResponse";
public static final String LOAN_CREATE_RESPONSE = "loanCreateResponse";
@@ -35,6 +36,8 @@ public abstract class TestContextKey {
public static final String WAIVE_CHARGE_RESPONSE = "waiveChargeResponse";
public static final String UNDO_WAIVE_RESPONSE = "waiveNsfFeeResponse";
public static final String LOAN_APPROVAL_RESPONSE = "loanApprovalResponse";
public static final String LOAN_REJECT_RESPONSE = "loanRejectResponse";
public static final String LOAN_WITHDRAWN_RESPONSE = "loanWithdrawnResponse";
public static final String LOAN_APPROVAL_SECOND_LOAN_RESPONSE = "loanApprovalSecondLoanResponse";
public static final String LOAN_UNDO_APPROVAL_RESPONSE = "loanUndoApprovalResponse";
public static final String LOAN_DISBURSE_RESPONSE = "loanDisburseResponse";
@@ -61,27 +64,44 @@ public abstract class TestContextKey {
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_INTEREST_DECLINING_PERIOD_SAME_AS_PAYMENT = "loanProductCreateResponseLP1InterestDecliningPeriodSameAsPayment";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_1MONTH_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_MONTHLY = "loanProductCreateResponseLP1InterestDecliningPeriodSameAsPaymentRecalculation";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_NONE = "loanProductCreateResponseLP1InterestDecliningBalanceDailyRecalculationCompoundingNone";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADVANCED_CUSTOM_PAYMENT_ALLOCATION_PROGRESSIVE_LOAN_SCHEDULE = "loanProductCreateResponseLP2ProgressiveLoanScheduleCustomPaymentAllocation";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_SAME_AS_REPAYMENT_COMPOUNDING_NONE = "loanProductCreateResponseLP1InterestDecliningBalanceDailyRecalculationSameAsRepaymentCompoundingNone";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_INTEREST_DECLINING_BALANCE_SAR_RECALCULATION_SAME_AS_REPAYMENT_COMPOUNDING_NONE_MULTI_DISBURSEMENT = "loanProductCreateResponseLP1InterestDecliningBalanceDailyRecalculationSameAsRepaymentCompoundingNoneMultiDisbursement";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_NONE_RESCHEDULE_REDUCE_NR_INSTALLMENTS = "loanProductCreateResponseLP1InterestDecliningBalanceDailyRecalculationCompoundingNoneRescheduleReduceNrInstallments";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_NONE_RESCHEDULE_NEXT_REPAYMENTS = "loanProductCreateResponseLP1InterestDecliningBalanceDailyRecalculationCompoundingNoneRescheduleNextRepayments";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_INTEREST_DECLINING_PERIOD_DAILY = "loanProductCreateResponseLP1InterestDecliningPeriodDaily";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_INTEREST_DECLINING_PERIOD_DAILY_ACCRUAL_ACTIVITY = "loanProductCreateResponseLP1InterestDecliningPeriodDailyAccrualActivity";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_INTEREST_DECLINING_BALANCE_DAILY_RECALCULATION_COMPOUNDING_NONE_ACCRUAL_ACTIVITY = "loanProductCreateResponseLP1InterestDecliningBalanceRecalculationCompoundingNoneAccrualActivity";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION = "loanProductCreateResponseLP2DownPaymentAutoAdvancedPaymentAllocation";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_AUTO_ADVANCED_REPAYMENT_ALLOCATION_PAYMENT_START_SUBMITTED = "loanProductCreateResponseLP2DownPaymentAutoAdvancedPaymentAllocationPaymentStartSubmitted";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_ADVANCED_PAYMENT_ALLOCATION = "loanProductCreateResponseLP2DownPaymentAdvancedPaymentAllocation";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT = "loanProductCreateResponseLP2DownPayment";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_INTEREST = "loanProductCreateResponseLP2DownPaymentInterest";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_INTEREST_AUTO = "loanProductCreateResponseLP2DownPaymentInterestAuto";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_AUTO = "loanProductCreateResponseLP2DownPaymentAuto";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_ADVANCED_PAYMENT_ALLOCATION_PROGRESSIVE_LOAN_SCHEDULE = "loanProductCreateResponseLP2DownPaymentProgressiveLoanSchedule";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADVANCED_CUSTOM_PAYMENT_ALLOCATION_PROGRESSIVE_LOAN_SCHEDULE = "loanProductCreateResponseLP2ProgressiveLoanScheduleCustomPaymentAllocation";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_ADVANCED_PAYMENT_ALLOCATION_PROGRESSIVE_LOAN_SCHEDULE_VERTICAL = "loanProductCreateResponseLP2DownPaymentProgressiveLoanScheduleVertical";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_ADVANCED_PAYMENT_ALLOCATION_PROGRESSIVE_LOAN_SCHEDULE_INSTALLMENT_LEVEL_DELINQUENCY = "loanProductCreateResponseLP2DownPaymentProgressiveLoanScheduleInstallmentLevelDelinquency";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROG_SCHEDULE_HOR_INST_LVL_DELINQUENCY_CREDIT_ALLOCATION = "loanProductCreateResponseLP2DownPaymentProgressiveLoanScheduleHorizontalInstallmentLevelDelinquencyCreditAllocation";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_ADV_PMT_ALLOC_FIXED_LENGTH = "loanProductCreateResponseLP2DownPaymentProgressiveLoanScheduleFixedLength";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_TILL_PRECLOSE = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationTillPreClose";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_TILL_REST_FREQUENCY = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationTillRestFrequency";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL = "loanProductCreateResponseLP2ProgressiveLoanScheduleHorizontal";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL_LAST = "loanProductCreateResponseLP2ProgressiveLoanScheduleHorizontal";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_INTEREST_FLAT_ADV_PMT_ALLOC = "loanProductCreateResponseLP2DownPaymentInterestFlatAdvancedPaymentAllocation";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmiActualActual";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_ACCRUAL_ACTIVITY = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmiActualActualAccrualActivity";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30 = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyTillPreClose";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE_LAST_INSTALLMENT = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyTillPreCloseLastInstallment";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE_PMT_ALLOC_1 = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyTillPreClosePmtAlloc1";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_SAME_AS_REP_TILL_PRECLOSE = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationSameAsRepTillPreClose";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_REST_FREQUENCY_DATE = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyTillRestFrequencyDate";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_SAME_AS_REP_TILL_REST_FREQUENCY_DATE = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationSameAsRepTillRestFrequencyDate";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_INTEREST_REFUND_FULL = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmiActualActualInterestRefundFull";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_MULTIDISBURSE = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030MultiDisburse";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_MULTIDISBURSE_DOWNPAYMENT = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030MultiDisburseDownPayment";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_365_ACTUAL = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi365Actual";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_DOWNPAYMENT = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030DownPayment";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_INTEREST_REFUND = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmiActualActualInterestRefund";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_ADVANCED_PAYMENT_ALLOCATION_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL = "loanProductCreateResponseLP1ProgressiveLoanScheduleHorizontal";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE_WHOLE_TERM = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationSameAsRepTillPreCloseWholeTerm";
public static final String CHARGE_FOR_LOAN_PERCENT_LATE_CREATE_RESPONSE = "ChargeForLoanPercentLateCreateResponse";
public static final String CHARGE_FOR_LOAN_PERCENT_LATE_AMOUNT_PLUS_INTEREST_CREATE_RESPONSE = "ChargeForLoanPercentLateAmountPlusInterestCreateResponse";
public static final String CHARGE_FOR_LOAN_PERCENT_PROCESSING_CREATE_RESPONSE = "ChargeForLoanPercentProcessingCreateResponse";
@@ -132,5 +152,4 @@ public abstract class TestContextKey {
public static final String TRANSACTION_EVENT = "transactionEvent";
public static final String LOAN_WRITE_OFF_RESPONSE = "loanWriteOffResponse";
public static final String LOAN_DELINQUENCY_ACTION_RESPONSE = "loanDelinquencyActionResponse";
}
@@ -0,0 +1,37 @@
/**
* 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.testrail;
import com.google.gson.annotations.SerializedName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
class AddResultForCaseRequest {
@SerializedName("status_id")
private TestRailStatus statusId;
private String comment;
}
@@ -0,0 +1,32 @@
/**
* 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.testrail;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.Path;
interface TestRailApiClient {
@Headers({ "Content-Type:application/json" })
@POST("/api/v2/add_result_for_case/{runId}/{caseId}")
Call<Void> addResultForCase(@Path("runId") int runId, @Path("caseId") int caseId, @Body AddResultForCaseRequest request);
}
@@ -0,0 +1,141 @@
/**
* 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.testrail;
import io.cucumber.java.Scenario;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.ResponseBody;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.junit.runner.Result;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component;
import retrofit2.Response;
@Slf4j
@Component
@RequiredArgsConstructor
@Conditional(TestRailEnabledCondition.class)
public class TestRailClient implements InitializingBean {
public static final Pattern TESTRAIL_TAG_PATTERN = Pattern.compile("@TestRailId:C([0-9]+)");
private static final String SUCCESS_COMMENT = "Test passed";
private static final String FAILED_COMMENT = "Test failed";
private final TestRailProperties testRailProperties;
private final TestRailApiClient apiClient;
@Override
public void afterPropertiesSet() {
log.warn("Configured TestRail Run ID: '{}'", testRailProperties.getRunId());
}
public void saveScenarioResult(Scenario scenario) {
Integer caseId = getScenarioCaseId(scenario);
AddResultForCaseRequest request;
if (scenario.isFailed()) {
request = createFailedRequest(scenario);
} else {
request = createSuccessRequest(scenario);
}
if (caseId != null) {
try {
Response<Void> response = apiClient.addResultForCase(testRailProperties.getRunId(), caseId, request).execute();
if (response.code() != HttpStatus.SC_OK) {
handleApiError(response);
}
} catch (IOException e) {
throw new RuntimeException("Error while reporting to TestRail", e);
}
}
}
private Integer getScenarioCaseId(Scenario scenario) {
Integer caseId = null;
for (String s : scenario.getSourceTagNames()) {
if (s.contains("TestRail")) {
Matcher matcher = TESTRAIL_TAG_PATTERN.matcher(s);
if (matcher.matches()) {
caseId = Integer.parseInt(matcher.group(1));
}
}
}
return caseId;
}
private void handleApiError(Response<Void> response) throws IOException {
String exceptionMsg = "Error while reporting to TestRail";
ResponseBody errorBody = response.errorBody();
if (errorBody != null) {
exceptionMsg += " " + errorBody.string();
}
throw new RuntimeException(exceptionMsg);
}
private AddResultForCaseRequest createFailedRequest(Scenario scenario) {
return new AddResultForCaseRequest.AddResultForCaseRequestBuilder().statusId(TestRailStatus.FAILED)
.comment(createFailedComment(scenario)).build();
}
private AddResultForCaseRequest createSuccessRequest(Scenario scenario) {
return new AddResultForCaseRequest.AddResultForCaseRequestBuilder().statusId(TestRailStatus.PASSED)
.comment(createSuccessComment(scenario)).build();
}
private String createSuccessComment(Scenario scenario) {
return SUCCESS_COMMENT;
}
private String createFailedComment(Scenario scenario) {
try {
Class c = ClassUtils.getClass("cucumber.runtime.java.JavaHookDefinition$ScenarioAdaptor");
Field fieldScenario = FieldUtils.getField(c, "scenario", true);
if (fieldScenario != null) {
fieldScenario.setAccessible(true);
Object objectScenario = fieldScenario.get(scenario);
Field fieldStepResults = objectScenario.getClass().getDeclaredField("stepResults");
fieldStepResults.setAccessible(true);
ArrayList<Result> results = (ArrayList<Result>) fieldStepResults.get(objectScenario);
for (Result result : results) {
if (result.getFailures() != null) {
return FAILED_COMMENT + "%n" + result.getFailures();
}
}
}
return FAILED_COMMENT;
} catch (IllegalAccessException | NoSuchFieldException | ClassNotFoundException e) {
return FAILED_COMMENT;
}
}
}
@@ -0,0 +1,80 @@
/**
* 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.testrail;
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.fineract.client.auth.HttpBasicAuth;
import org.apache.fineract.client.util.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import retrofit2.Retrofit;
@Configuration
@Conditional(TestRailEnabledCondition.class)
public class TestRailConfiguration {
@Autowired
private TestRailProperties testRailProperties;
@Bean
public TestRailApiClient testRailApiClient() {
String testRailBaseUrl = testRailProperties.getBaseUrl();
String testRailUsername = testRailProperties.getUsername();
String testRailPassword = testRailProperties.getPassword();
if (isBlank(testRailBaseUrl)) {
throw new IllegalStateException("TestRail base URL has not been set");
}
if (isBlank(testRailUsername)) {
throw new IllegalStateException("TestRail username has not been set");
}
if (isBlank(testRailPassword)) {
throw new IllegalStateException("TestRail password has not been set");
}
HttpBasicAuth httpBasicAuth = new HttpBasicAuth();
httpBasicAuth.setCredentials(testRailUsername, testRailPassword);
OkHttpClient httpClient = new OkHttpClient.Builder().addInterceptor(httpBasicAuth).addInterceptor(new TestRailIndexPhpInterceptor())
.build();
Retrofit retrofit = new Retrofit.Builder().addConverterFactory(JSON.GsonCustomConverterFactory.create(new JSON().getGson()))
.client(httpClient).baseUrl(testRailBaseUrl).build();
return retrofit.create(TestRailApiClient.class);
}
// Needed otherwise Retrofit 2 will be mad on the URL query/path parameters
private static final class TestRailIndexPhpInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String finalUrl = request.url().toString().replace("api/v2", "index.php?/api/v2");
return chain.proceed(request.newBuilder().url(finalUrl).build());
}
}
}
@@ -0,0 +1,34 @@
/**
* 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.testrail;
import org.apache.fineract.test.support.PropertiesCondition;
public class TestRailEnabledCondition extends PropertiesCondition<TestRailProperties> {
@Override
protected Class<TestRailProperties> getPropertiesClass() {
return TestRailProperties.class;
}
@Override
protected boolean matches(TestRailProperties properties) {
return properties.isEnabled();
}
}
@@ -0,0 +1,39 @@
/**
* 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.testrail;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
@Getter
public class TestRailProperties {
@Value("${fineract-test.testrail.enabled}")
private boolean enabled;
@Value("${fineract-test.testrail.base-url}")
private String baseUrl;
@Value("${fineract-test.testrail.username}")
private String username;
@Value("${fineract-test.testrail.password}")
private String password;
@Value("${fineract-test.testrail.run-id}")
private int runId;
}
@@ -0,0 +1,34 @@
/**
* 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.testrail;
import com.google.gson.annotations.SerializedName;
enum TestRailStatus {
@SerializedName("1") //
PASSED, //
@SerializedName("2") //
BLOCKED, //
@SerializedName("3") //
UNTESTED, //
@SerializedName("4") //
RETEST, //
@SerializedName("5") //
FAILED//
}