From af183927e6a91783f8baab8ae42d798f49194597 Mon Sep 17 00:00:00 2001 From: mariiaKraievska Date: Tue, 7 Jul 2026 14:54:03 +0300 Subject: [PATCH 1/3] FINERACT-2455: WC - Transaction Type - GoodWill Credit - Payment Allocation handling --- .../DefaultWorkingCapitalLoanProduct.java | 3 +- .../fineract/test/support/TestContextKey.java | 1 + .../global/WorkingCapitalInitializerStep.java | 21 + ...talGoodwillCreditPaymentAllocation.feature | 788 ++++++++++++++++++ ...ngCapitalLoanAllocationRequestFactory.java | 45 +- ...oanTransactionReprocessingServiceImpl.java | 2 +- ...ngCapitalLoanWritePlatformServiceImpl.java | 2 +- 7 files changed, 842 insertions(+), 20 deletions(-) create mode 100644 fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalGoodwillCreditPaymentAllocation.feature diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/workingcapitalproduct/DefaultWorkingCapitalLoanProduct.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/workingcapitalproduct/DefaultWorkingCapitalLoanProduct.java index 91f2df5bb0c..3295a1df86e 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/workingcapitalproduct/DefaultWorkingCapitalLoanProduct.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/workingcapitalproduct/DefaultWorkingCapitalLoanProduct.java @@ -36,7 +36,8 @@ public enum DefaultWorkingCapitalLoanProduct implements WorkingCapitalLoanProduc WCLP_DUE_FEE_PENALTY_PRINCIPAL, // WCLP_IN_ADVANCE_PENALTY_FEE_PRINCIPAL, // WCLP_DUE_FEE_PRINCIPAL_PENALTY, // - WCLP_DUE_PRINCIPAL_FEE_PENALTY; // + WCLP_DUE_PRINCIPAL_FEE_PENALTY, // + WCLP_GOODWILL_CREDIT_ALLOCATION; // @Override public String getName() { diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java index aacdfe57c2a..70f7d5878db 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java @@ -345,6 +345,7 @@ public abstract class TestContextKey { public static final String DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE_WCLP_IN_ADVANCE_PENALTY_FEE_PRINCIPAL = "workingCapitalLoanProductCreateResponseWCLPInAdvancePenaltyFeePrincipal"; public static final String DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE_WCLP_DUE_FEE_PRINCIPAL_PENALTY = "workingCapitalLoanProductCreateResponseWCLPDueFeePrincipalPenalty"; public static final String DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE_WCLP_DUE_PRINCIPAL_FEE_PENALTY = "workingCapitalLoanProductCreateResponseWCLPDuePrincipalFeePenalty"; + public static final String DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE_WCLP_GOODWILL_CREDIT_ALLOCATION = "workingCapitalLoanProductCreateResponseWCLPGoodwillCreditAllocation"; public static final String WC_LOAN_IDS = "wcLoanIds"; public static final String DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_REQUEST_FOR_UPDATE_WCLP = "workingCapitalLoanProductCreateRequestForUpdateWCLP"; public static final String DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE_FOR_UPDATE_WCLP = "workingCapitalLoanProductCreateResponseForUpdateWCLP"; diff --git a/fineract-e2e-tests-runner/src/test/java/org/apache/fineract/test/initializer/global/WorkingCapitalInitializerStep.java b/fineract-e2e-tests-runner/src/test/java/org/apache/fineract/test/initializer/global/WorkingCapitalInitializerStep.java index bcad8aff284..d1dcfc43d57 100644 --- a/fineract-e2e-tests-runner/src/test/java/org/apache/fineract/test/initializer/global/WorkingCapitalInitializerStep.java +++ b/fineract-e2e-tests-runner/src/test/java/org/apache/fineract/test/initializer/global/WorkingCapitalInitializerStep.java @@ -225,6 +225,27 @@ public void initialize() throws Exception { List.of(DUE_PRINCIPAL, DUE_FEE, DUE_PENALTY, IN_ADVANCE_PRINCIPAL, IN_ADVANCE_FEE, IN_ADVANCE_PENALTY)))) .overpaymentLiabilityAccountId(accountTypeResolver.resolve(DefaultAccountType.OTHER_CREDIT_LIABILITY)) + .paymentChannelToFundSourceMappings( + List.of(new org.apache.fineract.client.models.WorkingCapitalLoanPaymentChannelToFundSourceMappings() + .paymentTypeId(paymentTypeResolver.resolve(DefaultPaymentType.MONEY_TRANSFER)) + .fundSourceAccountId(accountTypeResolver.resolve(DefaultAccountType.FUND_RECEIVABLES)))))), + () -> TestContext.INSTANCE.set( + TestContextKey.DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE_WCLP_GOODWILL_CREDIT_ALLOCATION, + createWorkingCapitalLoanProductIdempotent(workingCapitalRequestFactory + .defaultWorkingCapitalLoanProductRequestWithAccrualAccounting() + .name(DefaultWorkingCapitalLoanProduct.WCLP_GOODWILL_CREDIT_ALLOCATION.getName()) + .allowAttributeOverrides(allowAttributeOverrides) + .overpaymentLiabilityAccountId(accountTypeResolver.resolve(DefaultAccountType.OTHER_CREDIT_LIABILITY)) + .paymentAllocation(List.of( + createPaymentAllocation(PostPaymentAllocation.TransactionTypeEnum.DEFAULT.getValue(), + List.of(DUE_FEE, DUE_PENALTY, DUE_PRINCIPAL, IN_ADVANCE_FEE, IN_ADVANCE_PENALTY, + IN_ADVANCE_PRINCIPAL)), + createPaymentAllocation(PostPaymentAllocation.TransactionTypeEnum.REPAYMENT.getValue(), + List.of(DUE_FEE, DUE_PENALTY, DUE_PRINCIPAL, IN_ADVANCE_FEE, IN_ADVANCE_PENALTY, + IN_ADVANCE_PRINCIPAL)), + createPaymentAllocation(PostPaymentAllocation.TransactionTypeEnum.GOODWILL_CREDIT.getValue(), + List.of(DUE_PRINCIPAL, DUE_FEE, DUE_PENALTY, IN_ADVANCE_PRINCIPAL, IN_ADVANCE_FEE, + IN_ADVANCE_PENALTY)))) .paymentChannelToFundSourceMappings( List.of(new org.apache.fineract.client.models.WorkingCapitalLoanPaymentChannelToFundSourceMappings() .paymentTypeId(paymentTypeResolver.resolve(DefaultPaymentType.MONEY_TRANSFER)) diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalGoodwillCreditPaymentAllocation.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalGoodwillCreditPaymentAllocation.feature new file mode 100644 index 00000000000..9a46c07ac2d --- /dev/null +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalGoodwillCreditPaymentAllocation.feature @@ -0,0 +1,788 @@ +@WorkingCapital +@WorkingCapitalGoodwillCreditPaymentAllocationFeature +Feature: Working Capital Goodwill Credit Payment Allocation + + Scenario: Verify Working Capital Goodwill Credit falls back to DEFAULT payment allocation rule with DUE_FEE_PENALTY_PRINCIPAL order - UC1 + Given Admin sets the business date to "01 January 2026" + And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_DUE_FEE_PENALTY_PRINCIPAL | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 | + When Admin sets the business date to "10 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "12 January 2026" due date and 15.0 transaction amount + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_PENALTY" specified due date charge to working capital loan with "12 January 2026" due date and 25.0 transaction amount + Then Working Capital Loan has charges with the following data: + | Charge Name | Due Date | Amount | Currency | isPenalty | Charge Time Type | Charge Calculation Type | Charge Payment mode | + | Working Capital Loan Fee | 12 January 2026 | 15.0 | EUR | false | Specified due date | Flat | Regular | + | Working Capital Loan Penalty | 12 January 2026 | 25.0 | EUR | true | Specified due date | Flat | Regular | + When Admin sets the business date to "12 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Customer makes "GOODWILL_CREDIT" transaction on "12 January 2026" with 270.0 transaction amount on Working Capital loan + And Working capital loan account has the correct data: + | principal | totalPaidPrincipal | totalPaymentVolume | realizedIncome | unrealizedIncome | overpaymentAmount | + | 9000.0 | 230.0 | 100000.0 | 0.0 | 0.0 | 0.0 | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 15.0 | 0.0 | 15.0 | 25.0 | 0.0 | 25.0 | + And Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 12 January 2026 | Goodwill Credit | 270.0 | 230.0 | 15.0 | 25.0 | false | + When Admin sets the business date to "15 March 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + Then Admin closes the Working Capital loan with a full repayment on "15 March 2026" + + Scenario: Verify Working Capital Goodwill Credit transaction with fee and penalty added with DUE_PENALTY_FEE_PRINCIPAL allocation - UC2 + Given Admin sets the business date to "01 January 2026" + And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_ADVANCED_ACCOUNTING | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 | + When Admin sets the business date to "10 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "12 January 2026" due date and 15.0 transaction amount + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_PENALTY" specified due date charge to working capital loan with "12 January 2026" due date and 25.0 transaction amount + Then Working Capital Loan has charges with the following data: + | Charge Name | Due Date | Amount | Currency | isPenalty | Charge Time Type | Charge Calculation Type | Charge Payment mode | + | Working Capital Loan Fee | 12 January 2026 | 15.0 | EUR | false | Specified due date | Flat | Regular | + | Working Capital Loan Penalty | 12 January 2026 | 25.0 | EUR | true | Specified due date | Flat | Regular | + When Admin sets the business date to "12 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Customer makes "GOODWILL_CREDIT" transaction on "12 January 2026" with 270.0 transaction amount on Working Capital loan + And Working capital loan account has the correct data: + | principal | totalPaidPrincipal | totalPaymentVolume | realizedIncome | unrealizedIncome | overpaymentAmount | + | 9000.0 | 230.0 | 100000.0 | 0.0 | 0.0 | 0.0 | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 15.0 | 0.0 | 15.0 | 25.0 | 0.0 | 25.0 | + And Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 12 January 2026 | Goodwill Credit | 270.0 | 230.0 | 15.0 | 25.0 | false | + When Admin sets the business date to "15 March 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + Then Admin closes the Working Capital loan with a full repayment on "15 March 2026" + + Scenario: Verify Working Capital Goodwill Credit transaction with fee and penalty added with DUE_PRINCIPAL_FEE_PENALTY allocation - UC3 + Given Admin sets the business date to "01 January 2026" + And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_DUE_PRINCIPAL_FEE_PENALTY | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 | + When Admin sets the business date to "10 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "12 January 2026" due date and 15.0 transaction amount + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_PENALTY" specified due date charge to working capital loan with "12 January 2026" due date and 25.0 transaction amount + Then Working Capital Loan has charges with the following data: + | Charge Name | Due Date | Amount | Currency | isPenalty | Charge Time Type | Charge Calculation Type | Charge Payment mode | + | Working Capital Loan Fee | 12 January 2026 | 15.0 | EUR | false | Specified due date | Flat | Regular | + | Working Capital Loan Penalty | 12 January 2026 | 25.0 | EUR | true | Specified due date | Flat | Regular | + When Admin sets the business date to "12 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Customer makes "GOODWILL_CREDIT" transaction on "12 January 2026" with 270.0 transaction amount on Working Capital loan + And Working capital loan account has the correct data: + | principal | totalPaidPrincipal | totalPaymentVolume | realizedIncome | unrealizedIncome | overpaymentAmount | + | 9000.0 | 270.0 | 100000.0 | 0.0 | 0.0 | 0.0 | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 15.0 | 15.0 | 0.0 | 25.0 | 25.0 | 0.0 | + And Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 12 January 2026 | Goodwill Credit | 270.0 | 270.0 | 0.0 | 0.0 | false | + When Admin sets the business date to "15 March 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + Then Admin closes the Working Capital loan with a full repayment on "15 March 2026" + + Scenario: Verify Working Capital Goodwill Credit transaction with fee and penalty added with DUE_FEE_PRINCIPAL_PENALTY allocation - UC4 + Given Admin sets the business date to "01 January 2026" + And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_DUE_FEE_PRINCIPAL_PENALTY | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 | + When Admin sets the business date to "10 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "12 January 2026" due date and 15.0 transaction amount + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_PENALTY" specified due date charge to working capital loan with "12 January 2026" due date and 25.0 transaction amount + Then Working Capital Loan has charges with the following data: + | Charge Name | Due Date | Amount | Currency | isPenalty | Charge Time Type | Charge Calculation Type | Charge Payment mode | + | Working Capital Loan Fee | 12 January 2026 | 15.0 | EUR | false | Specified due date | Flat | Regular | + | Working Capital Loan Penalty | 12 January 2026 | 25.0 | EUR | true | Specified due date | Flat | Regular | + When Admin sets the business date to "12 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Customer makes "GOODWILL_CREDIT" transaction on "12 January 2026" with 270.0 transaction amount on Working Capital loan + And Working capital loan account has the correct data: + | principal | totalPaidPrincipal | totalPaymentVolume | realizedIncome | unrealizedIncome | overpaymentAmount | + | 9000.0 | 255.0 | 100000.0 | 0.0 | 0.0 | 0.0 | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 15.0 | 0.0 | 15.0 | 25.0 | 25.0 | 0.0 | + And Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 12 January 2026 | Goodwill Credit | 270.0 | 255.0 | 15.0 | 0.0 | false | + When Admin sets the business date to "15 March 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + Then Admin closes the Working Capital loan with a full repayment on "15 March 2026" + + Scenario: Verify Working Capital Goodwill Credit transaction with fee and penalty added with IN_ADVANCE_PENALTY_FEE_PRINCIPAL allocation - UC5 + Given Admin sets the business date to "01 January 2026" + And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_IN_ADVANCE_PENALTY_FEE_PRINCIPAL | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 | + When Admin sets the business date to "10 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "12 January 2026" due date and 15.0 transaction amount + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_PENALTY" specified due date charge to working capital loan with "12 January 2026" due date and 25.0 transaction amount + Then Working Capital Loan has charges with the following data: + | Charge Name | Due Date | Amount | Currency | isPenalty | Charge Time Type | Charge Calculation Type | Charge Payment mode | + | Working Capital Loan Fee | 12 January 2026 | 15.0 | EUR | false | Specified due date | Flat | Regular | + | Working Capital Loan Penalty | 12 January 2026 | 25.0 | EUR | true | Specified due date | Flat | Regular | + And Customer makes "GOODWILL_CREDIT" transaction on "10 January 2026" with 270.0 transaction amount on Working Capital loan + And Working capital loan account has the correct data: + | principal | totalPaidPrincipal | totalPaymentVolume | realizedIncome | unrealizedIncome | overpaymentAmount | + | 9000.0 | 230.0 | 100000.0 | 0.0 | 0.0 | 0.0 | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 15.0 | 0.0 | 15.0 | 25.0 | 0.0 | 25.0 | + And Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 10 January 2026 | Goodwill Credit | 270.0 | 230.0 | 15.0 | 25.0 | false | + When Admin sets the business date to "15 March 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + Then Admin closes the Working Capital loan with a full repayment on "15 March 2026" + + Scenario: Verify Working Capital Goodwill Credit transaction that closes loan with fee and penalty added with DUE_PRINCIPAL_FEE_PENALTY allocation - UC6 + Given Admin sets the business date to "01 January 2026" + And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_DUE_PRINCIPAL_FEE_PENALTY | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 | + When Admin sets the business date to "10 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "12 January 2026" due date and 15.0 transaction amount + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_PENALTY" specified due date charge to working capital loan with "12 January 2026" due date and 25.0 transaction amount + Then Working Capital Loan has charges with the following data: + | Charge Name | Due Date | Amount | Currency | isPenalty | Charge Time Type | Charge Calculation Type | Charge Payment mode | + | Working Capital Loan Fee | 12 January 2026 | 15.0 | EUR | false | Specified due date | Flat | Regular | + | Working Capital Loan Penalty | 12 January 2026 | 25.0 | EUR | true | Specified due date | Flat | Regular | + When Admin sets the business date to "12 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Customer makes "GOODWILL_CREDIT" transaction on "12 January 2026" with 9040.0 transaction amount on Working Capital loan + And Working capital loan account has the correct data: + | principal | totalPaidPrincipal | totalPaymentVolume | realizedIncome | unrealizedIncome | overpaymentAmount | + | 9000.0 | 9000.0 | 100000.0 | 0.0 | 0.0 | 0.0 | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 15.0 | 0.0 | 15.0 | 25.0 | 0.0 | 25.0 | + And Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 12 January 2026 | Goodwill Credit | 9040.0 | 9000.0 | 15.0 | 25.0 | false | + + Scenario: Verify Working Capital Goodwill Credit transaction that overpays loan with following CBR trn and with fee and penalty added with DUE_PRINCIPAL_FEE_PENALTY allocation - UC7 + Given Admin sets the business date to "01 January 2026" + And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_ADVANCED_ACCOUNTING | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 | + When Admin sets the business date to "10 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "12 January 2026" due date and 15.0 transaction amount + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_PENALTY" specified due date charge to working capital loan with "12 January 2026" due date and 25.0 transaction amount + Then Working Capital Loan has charges with the following data: + | Charge Name | Due Date | Amount | Currency | isPenalty | Charge Time Type | Charge Calculation Type | Charge Payment mode | + | Working Capital Loan Fee | 12 January 2026 | 15.0 | EUR | false | Specified due date | Flat | Regular | + | Working Capital Loan Penalty | 12 January 2026 | 25.0 | EUR | true | Specified due date | Flat | Regular | + When Admin sets the business date to "12 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Customer makes "GOODWILL_CREDIT" transaction on "12 January 2026" with 9200.0 transaction amount on Working Capital loan + And Working capital loan account has the correct data: + | principal | totalPaidPrincipal | totalPaymentVolume | realizedIncome | unrealizedIncome | overpaymentAmount | + | 9000.0 | 9000.0 | 100000.0 | 0.0 | 0.0 | 160.0 | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 15.0 | 0.0 | 15.0 | 25.0 | 0.0 | 25.0 | + And Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 12 January 2026 | Goodwill Credit | 9200.0 | 9000.0 | 15.0 | 25.0 | false | +# --- make CBR trn to refund overpaid amount --- # + And Customer makes credit balance refund on "12 January 2026" with 160.0 transaction amount on Working Capital loan + And Working capital loan account has the correct data: + | principal | totalPaidPrincipal | totalPaymentVolume | realizedIncome | unrealizedIncome | overpaymentAmount | + | 9000.0 | 9000.0 | 100000.0 | 0.0 | 0.0 | 0.0 | + And Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 12 January 2026 | Goodwill Credit | 9200.0 | 9000.0 | 15.0 | 25.0 | false | + | 12 January 2026 | Credit Balance Refund | 160.0 | 160.0 | 0.0 | 0.0 | false | + + Scenario: Verify Working Capital Goodwill Credit transaction allocation with charges has been reprocessed successfully after additional backdated goodwill credit - UC8 + Given Admin sets the business date to "01 January 2026" + And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_DUE_FEE_PENALTY_PRINCIPAL | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 | + When Admin sets the business date to "10 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "10 January 2026" due date and 100.0 transaction amount + Then Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | + When Admin sets the business date to "20 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Customer makes "GOODWILL_CREDIT" transaction on "20 January 2026" with 100.0 transaction amount on Working Capital loan + Then Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 20 January 2026 | Goodwill Credit | 100.0 | 0.0 | 100.0 | 0.0 | false | + And Working Capital Loan has charges with the following data: + | Charge Name | Due Date | Amount | Currency | isPenalty | Charge Time Type | Charge Calculation Type | Charge Payment mode | + | Working Capital Loan Fee | 10 January 2026 | 100.0 | EUR | false | Specified due date | Flat | Regular | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 100.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | +# --- backdated goodwill credit on Jan, 10, 2026 --- # + And Customer makes "GOODWILL_CREDIT" transaction on "10 January 2026" with 100.0 transaction amount on Working Capital loan + Then Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 10 January 2026 | Goodwill Credit | 100.0 | 0.0 | 100.0 | 0.0 | false | + | 20 January 2026 | Goodwill Credit | 100.0 | 100.0 | 0.0 | 0.0 | false | + And Working Capital Loan has charges with the following data: + | Charge Name | Due Date | Amount | Currency | isPenalty | Charge Time Type | Charge Calculation Type | Charge Payment mode | + | Working Capital Loan Fee | 10 January 2026 | 100.0 | EUR | false | Specified due date | Flat | Regular | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 100.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | + When Admin sets the business date to "25 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + Then Admin closes the Working Capital loan with a full repayment on "25 January 2026" + + Scenario: Verify Working Capital Goodwill Credit and fee charge adjustment transaction allocation is processed successfully - UC9 + Given Admin sets the business date to "01 January 2026" + And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_ADVANCED_ACCOUNTING | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 | + When Admin sets the business date to "10 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "10 January 2026" due date and 100.0 transaction amount + Then Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | + When Admin sets the business date to "11 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Customer makes "GOODWILL_CREDIT" transaction on "11 January 2026" with 30.0 transaction amount on Working Capital loan + When Admin makes a charge adjustment for the last added charge with 70.0 amount on working capital loan + Then Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 11 January 2026 | Goodwill Credit | 30.0 | 0.0 | 30.0 | 0.0 | false | + | 11 January 2026 | Charge Adjustment | 70.0 | 0.0 | 70.0 | 0.0 | false | + And Working Capital Loan has charges with the following data: + | Charge Name | Due Date | Amount | Currency | isPenalty | Charge Time Type | Charge Calculation Type | Charge Payment mode | + | Working Capital Loan Fee | 10 January 2026 | 100.0 | EUR | false | Specified due date | Flat | Regular | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 100.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | + When Admin sets the business date to "20 February 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + Then Admin closes the Working Capital loan with a full repayment on "20 February 2026" + + Scenario: Verify Working Capital Goodwill Credit transaction with charge within amortization schedule with DUE_PENALTY_FEE_PRINCIPAL allocation - UC10 + Given Admin sets the business date to "01 January 2026" + And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_ADVANCED_ACCOUNTING | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 | + When Admin sets the business date to "10 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_PENALTY" specified due date charge to working capital loan with "10 January 2026" due date and 35.0 transaction amount + Then Working Capital Loan has charges with the following data: + | Charge Name | Due Date | Amount | Currency | isPenalty | Charge Time Type | Charge Calculation Type | Charge Payment mode | + | Working Capital Loan Penalty | 10 January 2026 | 35.0 | EUR | true | Specified due date | Flat | Regular | + Then Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + And Customer makes "GOODWILL_CREDIT" transaction on "10 January 2026" with 270.0 transaction amount on Working Capital loan + And Working capital loan account has the correct data: + | principal | totalPaidPrincipal | totalPaymentVolume | realizedIncome | unrealizedIncome | overpaymentAmount | + | 9000.0 | 235.0 | 100000.0 | 0.0 | 0.0 | 0.0 | + And Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 10 January 2026 | Goodwill Credit | 270.0 | 235.0 | 0.0 | 35.0 | false | + When Admin sets the business date to "11 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + Then Working Capital loan amortization schedule has 186 periods, with the following data for periods: + | paymentNo | paymentDate | expectedPaymentAmount | actualPaymentAmount | expectedBalance | actualBalance | expectedAmortizationAmount | actualAmortizationAmount | expectedDiscountFeeBalance | + | 0 | 01 January 2026 | -9000.00 | | 9000.00 | 9000.00 | | | 0.00 | + | 1 | 02 January 2026 | 50.00 | 0.00 | 8950.00 | 9000.00 | 0.00 | 0.00 | 0.00 | + | 2 | 03 January 2026 | 50.00 | 0.00 | 8900.00 | 9000.00 | 0.00 | 0.00 | 0.00 | + | 3 | 04 January 2026 | 50.00 | 0.00 | 8850.00 | 9000.00 | 0.00 | 0.00 | 0.00 | + | 4 | 05 January 2026 | 50.00 | 0.00 | 8800.00 | 9000.00 | 0.00 | 0.00 | 0.00 | + | 5 | 06 January 2026 | 50.00 | 0.00 | 8750.00 | 9000.00 | 0.00 | 0.00 | 0.00 | + | 6 | 07 January 2026 | 50.00 | 0.00 | 8700.00 | 9000.00 | 0.00 | 0.00 | 0.00 | + | 7 | 08 January 2026 | 50.00 | 0.00 | 8650.00 | 9000.00 | 0.00 | 0.00 | 0.00 | + | 8 | 09 January 2026 | 50.00 | 0.00 | 8600.00 | 9000.00 | 0.00 | 0.00 | 0.00 | + | 9 | 10 January 2026 | 50.00 | 235.00 | 8550.00 | 8765.00 | 0.00 | 0.00 | 0.00 | + | 10 | 11 January 2026 | 50.00 | | 8500.00 | | 0.00 | | 0.00 | + | 11 | 12 January 2026 | 50.00 | | 8450.00 | | 0.00 | | 0.00 | + | 12 | 13 January 2026 | 50.00 | | 8400.00 | | 0.00 | | 0.00 | + | 13 | 14 January 2026 | 50.00 | | 8350.00 | | 0.00 | | 0.00 | + | 14 | 15 January 2026 | 50.00 | | 8300.00 | | 0.00 | | 0.00 | + | 15 | 16 January 2026 | 50.00 | | 8250.00 | | 0.00 | | 0.00 | + | 16 | 17 January 2026 | 50.00 | | 8200.00 | | 0.00 | | 0.00 | + | 17 | 18 January 2026 | 50.00 | | 8150.00 | | 0.00 | | 0.00 | + | 18 | 19 January 2026 | 50.00 | | 8100.00 | | 0.00 | | 0.00 | + | 19 | 20 January 2026 | 50.00 | | 8050.00 | | 0.00 | | 0.00 | + | 20 | 21 January 2026 | 50.00 | | 8000.00 | | 0.00 | | 0.00 | + | 21 | 22 January 2026 | 50.00 | | 7950.00 | | 0.00 | | 0.00 | + | 22 | 23 January 2026 | 50.00 | | 7900.00 | | 0.00 | | 0.00 | + | 23 | 24 January 2026 | 50.00 | | 7850.00 | | 0.00 | | 0.00 | + | 24 | 25 January 2026 | 50.00 | | 7800.00 | | 0.00 | | 0.00 | + | 25 | 26 January 2026 | 50.00 | | 7750.00 | | 0.00 | | 0.00 | + | 26 | 27 January 2026 | 50.00 | | 7700.00 | | 0.00 | | 0.00 | + | 27 | 28 January 2026 | 50.00 | | 7650.00 | | 0.00 | | 0.00 | + | 28 | 29 January 2026 | 50.00 | | 7600.00 | | 0.00 | | 0.00 | + | 29 | 30 January 2026 | 50.00 | | 7550.00 | | 0.00 | | 0.00 | + | 30 | 31 January 2026 | 50.00 | | 7500.00 | | 0.00 | | 0.00 | + | 31 | 01 February 2026 | 50.00 | | 7450.00 | | 0.00 | | 0.00 | + | 32 | 02 February 2026 | 50.00 | | 7400.00 | | 0.00 | | 0.00 | + | 33 | 03 February 2026 | 50.00 | | 7350.00 | | 0.00 | | 0.00 | + | 34 | 04 February 2026 | 50.00 | | 7300.00 | | 0.00 | | 0.00 | + | 35 | 05 February 2026 | 50.00 | | 7250.00 | | 0.00 | | 0.00 | + | 36 | 06 February 2026 | 50.00 | | 7200.00 | | 0.00 | | 0.00 | + | 37 | 07 February 2026 | 50.00 | | 7150.00 | | 0.00 | | 0.00 | + | 38 | 08 February 2026 | 50.00 | | 7100.00 | | 0.00 | | 0.00 | + | 39 | 09 February 2026 | 50.00 | | 7050.00 | | 0.00 | | 0.00 | + | 40 | 10 February 2026 | 50.00 | | 7000.00 | | 0.00 | | 0.00 | + | 41 | 11 February 2026 | 50.00 | | 6950.00 | | 0.00 | | 0.00 | + | 42 | 12 February 2026 | 50.00 | | 6900.00 | | 0.00 | | 0.00 | + | 43 | 13 February 2026 | 50.00 | | 6850.00 | | 0.00 | | 0.00 | + | 44 | 14 February 2026 | 50.00 | | 6800.00 | | 0.00 | | 0.00 | + | 45 | 15 February 2026 | 50.00 | | 6750.00 | | 0.00 | | 0.00 | + | 46 | 16 February 2026 | 50.00 | | 6700.00 | | 0.00 | | 0.00 | + | 47 | 17 February 2026 | 50.00 | | 6650.00 | | 0.00 | | 0.00 | + | 48 | 18 February 2026 | 50.00 | | 6600.00 | | 0.00 | | 0.00 | + | 49 | 19 February 2026 | 50.00 | | 6550.00 | | 0.00 | | 0.00 | + | 50 | 20 February 2026 | 50.00 | | 6500.00 | | 0.00 | | 0.00 | + | 51 | 21 February 2026 | 50.00 | | 6450.00 | | 0.00 | | 0.00 | + | 52 | 22 February 2026 | 50.00 | | 6400.00 | | 0.00 | | 0.00 | + | 53 | 23 February 2026 | 50.00 | | 6350.00 | | 0.00 | | 0.00 | + | 54 | 24 February 2026 | 50.00 | | 6300.00 | | 0.00 | | 0.00 | + | 55 | 25 February 2026 | 50.00 | | 6250.00 | | 0.00 | | 0.00 | + | 56 | 26 February 2026 | 50.00 | | 6200.00 | | 0.00 | | 0.00 | + | 57 | 27 February 2026 | 50.00 | | 6150.00 | | 0.00 | | 0.00 | + | 58 | 28 February 2026 | 50.00 | | 6100.00 | | 0.00 | | 0.00 | + | 59 | 01 March 2026 | 50.00 | | 6050.00 | | 0.00 | | 0.00 | + | 60 | 02 March 2026 | 50.00 | | 6000.00 | | 0.00 | | 0.00 | + | 61 | 03 March 2026 | 50.00 | | 5950.00 | | 0.00 | | 0.00 | + | 62 | 04 March 2026 | 50.00 | | 5900.00 | | 0.00 | | 0.00 | + | 63 | 05 March 2026 | 50.00 | | 5850.00 | | 0.00 | | 0.00 | + | 64 | 06 March 2026 | 50.00 | | 5800.00 | | 0.00 | | 0.00 | + | 65 | 07 March 2026 | 50.00 | | 5750.00 | | 0.00 | | 0.00 | + | 66 | 08 March 2026 | 50.00 | | 5700.00 | | 0.00 | | 0.00 | + | 67 | 09 March 2026 | 50.00 | | 5650.00 | | 0.00 | | 0.00 | + | 68 | 10 March 2026 | 50.00 | | 5600.00 | | 0.00 | | 0.00 | + | 69 | 11 March 2026 | 50.00 | | 5550.00 | | 0.00 | | 0.00 | + | 70 | 12 March 2026 | 50.00 | | 5500.00 | | 0.00 | | 0.00 | + | 71 | 13 March 2026 | 50.00 | | 5450.00 | | 0.00 | | 0.00 | + | 72 | 14 March 2026 | 50.00 | | 5400.00 | | 0.00 | | 0.00 | + | 73 | 15 March 2026 | 50.00 | | 5350.00 | | 0.00 | | 0.00 | + | 74 | 16 March 2026 | 50.00 | | 5300.00 | | 0.00 | | 0.00 | + | 75 | 17 March 2026 | 50.00 | | 5250.00 | | 0.00 | | 0.00 | + | 76 | 18 March 2026 | 50.00 | | 5200.00 | | 0.00 | | 0.00 | + | 77 | 19 March 2026 | 50.00 | | 5150.00 | | 0.00 | | 0.00 | + | 78 | 20 March 2026 | 50.00 | | 5100.00 | | 0.00 | | 0.00 | + | 79 | 21 March 2026 | 50.00 | | 5050.00 | | 0.00 | | 0.00 | + | 80 | 22 March 2026 | 50.00 | | 5000.00 | | 0.00 | | 0.00 | + | 81 | 23 March 2026 | 50.00 | | 4950.00 | | 0.00 | | 0.00 | + | 82 | 24 March 2026 | 50.00 | | 4900.00 | | 0.00 | | 0.00 | + | 83 | 25 March 2026 | 50.00 | | 4850.00 | | 0.00 | | 0.00 | + | 84 | 26 March 2026 | 50.00 | | 4800.00 | | 0.00 | | 0.00 | + | 85 | 27 March 2026 | 50.00 | | 4750.00 | | 0.00 | | 0.00 | + | 86 | 28 March 2026 | 50.00 | | 4700.00 | | 0.00 | | 0.00 | + | 87 | 29 March 2026 | 50.00 | | 4650.00 | | 0.00 | | 0.00 | + | 88 | 30 March 2026 | 50.00 | | 4600.00 | | 0.00 | | 0.00 | + | 89 | 31 March 2026 | 50.00 | | 4550.00 | | 0.00 | | 0.00 | + | 90 | 01 April 2026 | 50.00 | | 4500.00 | | 0.00 | | 0.00 | + | 91 | 02 April 2026 | 50.00 | | 4450.00 | | 0.00 | | 0.00 | + | 92 | 03 April 2026 | 50.00 | | 4400.00 | | 0.00 | | 0.00 | + | 93 | 04 April 2026 | 50.00 | | 4350.00 | | 0.00 | | 0.00 | + | 94 | 05 April 2026 | 50.00 | | 4300.00 | | 0.00 | | 0.00 | + | 95 | 06 April 2026 | 50.00 | | 4250.00 | | 0.00 | | 0.00 | + | 96 | 07 April 2026 | 50.00 | | 4200.00 | | 0.00 | | 0.00 | + | 97 | 08 April 2026 | 50.00 | | 4150.00 | | 0.00 | | 0.00 | + | 98 | 09 April 2026 | 50.00 | | 4100.00 | | 0.00 | | 0.00 | + | 99 | 10 April 2026 | 50.00 | | 4050.00 | | 0.00 | | 0.00 | + | 100 | 11 April 2026 | 50.00 | | 4000.00 | | 0.00 | | 0.00 | + | 101 | 12 April 2026 | 50.00 | | 3950.00 | | 0.00 | | 0.00 | + | 102 | 13 April 2026 | 50.00 | | 3900.00 | | 0.00 | | 0.00 | + | 103 | 14 April 2026 | 50.00 | | 3850.00 | | 0.00 | | 0.00 | + | 104 | 15 April 2026 | 50.00 | | 3800.00 | | 0.00 | | 0.00 | + | 105 | 16 April 2026 | 50.00 | | 3750.00 | | 0.00 | | 0.00 | + | 106 | 17 April 2026 | 50.00 | | 3700.00 | | 0.00 | | 0.00 | + | 107 | 18 April 2026 | 50.00 | | 3650.00 | | 0.00 | | 0.00 | + | 108 | 19 April 2026 | 50.00 | | 3600.00 | | 0.00 | | 0.00 | + | 109 | 20 April 2026 | 50.00 | | 3550.00 | | 0.00 | | 0.00 | + | 110 | 21 April 2026 | 50.00 | | 3500.00 | | 0.00 | | 0.00 | + | 111 | 22 April 2026 | 50.00 | | 3450.00 | | 0.00 | | 0.00 | + | 112 | 23 April 2026 | 50.00 | | 3400.00 | | 0.00 | | 0.00 | + | 113 | 24 April 2026 | 50.00 | | 3350.00 | | 0.00 | | 0.00 | + | 114 | 25 April 2026 | 50.00 | | 3300.00 | | 0.00 | | 0.00 | + | 115 | 26 April 2026 | 50.00 | | 3250.00 | | 0.00 | | 0.00 | + | 116 | 27 April 2026 | 50.00 | | 3200.00 | | 0.00 | | 0.00 | + | 117 | 28 April 2026 | 50.00 | | 3150.00 | | 0.00 | | 0.00 | + | 118 | 29 April 2026 | 50.00 | | 3100.00 | | 0.00 | | 0.00 | + | 119 | 30 April 2026 | 50.00 | | 3050.00 | | 0.00 | | 0.00 | + | 120 | 01 May 2026 | 50.00 | | 3000.00 | | 0.00 | | 0.00 | + | 121 | 02 May 2026 | 50.00 | | 2950.00 | | 0.00 | | 0.00 | + | 122 | 03 May 2026 | 50.00 | | 2900.00 | | 0.00 | | 0.00 | + | 123 | 04 May 2026 | 50.00 | | 2850.00 | | 0.00 | | 0.00 | + | 124 | 05 May 2026 | 50.00 | | 2800.00 | | 0.00 | | 0.00 | + | 125 | 06 May 2026 | 50.00 | | 2750.00 | | 0.00 | | 0.00 | + | 126 | 07 May 2026 | 50.00 | | 2700.00 | | 0.00 | | 0.00 | + | 127 | 08 May 2026 | 50.00 | | 2650.00 | | 0.00 | | 0.00 | + | 128 | 09 May 2026 | 50.00 | | 2600.00 | | 0.00 | | 0.00 | + | 129 | 10 May 2026 | 50.00 | | 2550.00 | | 0.00 | | 0.00 | + | 130 | 11 May 2026 | 50.00 | | 2500.00 | | 0.00 | | 0.00 | + | 131 | 12 May 2026 | 50.00 | | 2450.00 | | 0.00 | | 0.00 | + | 132 | 13 May 2026 | 50.00 | | 2400.00 | | 0.00 | | 0.00 | + | 133 | 14 May 2026 | 50.00 | | 2350.00 | | 0.00 | | 0.00 | + | 134 | 15 May 2026 | 50.00 | | 2300.00 | | 0.00 | | 0.00 | + | 135 | 16 May 2026 | 50.00 | | 2250.00 | | 0.00 | | 0.00 | + | 136 | 17 May 2026 | 50.00 | | 2200.00 | | 0.00 | | 0.00 | + | 137 | 18 May 2026 | 50.00 | | 2150.00 | | 0.00 | | 0.00 | + | 138 | 19 May 2026 | 50.00 | | 2100.00 | | 0.00 | | 0.00 | + | 139 | 20 May 2026 | 50.00 | | 2050.00 | | 0.00 | | 0.00 | + | 140 | 21 May 2026 | 50.00 | | 2000.00 | | 0.00 | | 0.00 | + | 141 | 22 May 2026 | 50.00 | | 1950.00 | | 0.00 | | 0.00 | + | 142 | 23 May 2026 | 50.00 | | 1900.00 | | 0.00 | | 0.00 | + | 143 | 24 May 2026 | 50.00 | | 1850.00 | | 0.00 | | 0.00 | + | 144 | 25 May 2026 | 50.00 | | 1800.00 | | 0.00 | | 0.00 | + | 145 | 26 May 2026 | 50.00 | | 1750.00 | | 0.00 | | 0.00 | + | 146 | 27 May 2026 | 50.00 | | 1700.00 | | 0.00 | | 0.00 | + | 147 | 28 May 2026 | 50.00 | | 1650.00 | | 0.00 | | 0.00 | + | 148 | 29 May 2026 | 50.00 | | 1600.00 | | 0.00 | | 0.00 | + | 149 | 30 May 2026 | 50.00 | | 1550.00 | | 0.00 | | 0.00 | + | 150 | 31 May 2026 | 50.00 | | 1500.00 | | 0.00 | | 0.00 | + | 151 | 01 June 2026 | 50.00 | | 1450.00 | | 0.00 | | 0.00 | + | 152 | 02 June 2026 | 50.00 | | 1400.00 | | 0.00 | | 0.00 | + | 153 | 03 June 2026 | 50.00 | | 1350.00 | | 0.00 | | 0.00 | + | 154 | 04 June 2026 | 50.00 | | 1300.00 | | 0.00 | | 0.00 | + | 155 | 05 June 2026 | 50.00 | | 1250.00 | | 0.00 | | 0.00 | + | 156 | 06 June 2026 | 50.00 | | 1200.00 | | 0.00 | | 0.00 | + | 157 | 07 June 2026 | 50.00 | | 1150.00 | | 0.00 | | 0.00 | + | 158 | 08 June 2026 | 50.00 | | 1100.00 | | 0.00 | | 0.00 | + | 159 | 09 June 2026 | 50.00 | | 1050.00 | | 0.00 | | 0.00 | + | 160 | 10 June 2026 | 50.00 | | 1000.00 | | 0.00 | | 0.00 | + | 161 | 11 June 2026 | 50.00 | | 950.00 | | 0.00 | | 0.00 | + | 162 | 12 June 2026 | 50.00 | | 900.00 | | 0.00 | | 0.00 | + | 163 | 13 June 2026 | 50.00 | | 850.00 | | 0.00 | | 0.00 | + | 164 | 14 June 2026 | 50.00 | | 800.00 | | 0.00 | | 0.00 | + | 165 | 15 June 2026 | 50.00 | | 750.00 | | 0.00 | | 0.00 | + | 166 | 16 June 2026 | 50.00 | | 700.00 | | 0.00 | | 0.00 | + | 167 | 17 June 2026 | 50.00 | | 650.00 | | 0.00 | | 0.00 | + | 168 | 18 June 2026 | 50.00 | | 600.00 | | 0.00 | | 0.00 | + | 169 | 19 June 2026 | 50.00 | | 550.00 | | 0.00 | | 0.00 | + | 170 | 20 June 2026 | 50.00 | | 500.00 | | 0.00 | | 0.00 | + | 171 | 21 June 2026 | 50.00 | | 450.00 | | 0.00 | | 0.00 | + | 172 | 22 June 2026 | 50.00 | | 400.00 | | 0.00 | | 0.00 | + | 173 | 23 June 2026 | 50.00 | | 350.00 | | 0.00 | | 0.00 | + | 174 | 24 June 2026 | 50.00 | | 300.00 | | 0.00 | | 0.00 | + | 175 | 25 June 2026 | 50.00 | | 250.00 | | 0.00 | | 0.00 | + | 176 | 26 June 2026 | 50.00 | | 200.00 | | 0.00 | | 0.00 | + | 177 | 27 June 2026 | 50.00 | | 150.00 | | 0.00 | | 0.00 | + | 178 | 28 June 2026 | 50.00 | | 100.00 | | 0.00 | | 0.00 | + | 179 | 29 June 2026 | 50.00 | | 50.00 | | 0.00 | | 0.00 | + | 180 | 30 June 2026 | 50.00 | | 0.00 | | 0.00 | | 0.00 | + | 181 | 01 July 2026 | 50.00 | | 0.00 | | 0.00 | | 0.00 | + | 182 | 02 July 2026 | 50.00 | | 0.00 | | 0.00 | | 0.00 | + | 183 | 03 July 2026 | 50.00 | | 0.00 | | 0.00 | | 0.00 | + | 184 | 04 July 2026 | 50.00 | | 0.00 | | 0.00 | | 0.00 | + | 185 | 05 July 2026 | 15.00 | | 0.00 | | 0.00 | | 0.00 | + When Admin sets the business date to "15 March 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + Then Admin closes the Working Capital loan with a full repayment on "15 March 2026" + + Scenario: Verify Working Capital Goodwill Credit with fee and penalty charges within amortization schedule with DUE_FEE_PENALTY_PRINCIPAL allocation - UC11 + Given Admin sets the business date to "01 January 2026" + And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_DUE_FEE_PENALTY_PRINCIPAL | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 | + When Admin sets the business date to "10 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "12 January 2026" due date and 35.0 transaction amount + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_PENALTY" specified due date charge to working capital loan with "12 January 2026" due date and 43.0 transaction amount + Then Working Capital Loan has charges with the following data: + | Charge Name | Due Date | Amount | Currency | isPenalty | Charge Time Type | Charge Calculation Type | Charge Payment mode | + | Working Capital Loan Fee | 12 January 2026 | 35.0 | EUR | false | Specified due date | Flat | Regular | + | Working Capital Loan Penalty | 12 January 2026 | 43.0 | EUR | true | Specified due date | Flat | Regular | + When Admin sets the business date to "12 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Customer makes "GOODWILL_CREDIT" transaction on "12 January 2026" with 270.0 transaction amount on Working Capital loan + And Working capital loan account has the correct data: + | principal | totalPaidPrincipal | totalPaymentVolume | realizedIncome | unrealizedIncome | overpaymentAmount | + | 9000.0 | 192.0 | 100000.0 | 0.0 | 0.0 | 0.0 | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 35.0 | 0.0 | 35.0 | 43.0 | 0.0 | 43.0 | + And Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 12 January 2026 | Goodwill Credit | 270.0 | 192.0 | 35.0 | 43.0 | false | + When Admin sets the business date to "13 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + Then Working Capital loan amortization schedule has 189 periods, with the following data for periods: + | paymentNo | paymentDate | expectedPaymentAmount | actualPaymentAmount | expectedBalance | actualBalance | expectedAmortizationAmount | actualAmortizationAmount | expectedDiscountFeeBalance | + | 0 | 01 January 2026 | -9000.00 | | 9000.00 | 9000.00 | | | 0.00 | + | 1 | 02 January 2026 | 50.00 | 0.00 | 8950.00 | 9000.00 | 0.00 | 0.00 | 0.00 | + | 2 | 03 January 2026 | 50.00 | 0.00 | 8900.00 | 9000.00 | 0.00 | 0.00 | 0.00 | + | 3 | 04 January 2026 | 50.00 | 0.00 | 8850.00 | 9000.00 | 0.00 | 0.00 | 0.00 | + | 4 | 05 January 2026 | 50.00 | 0.00 | 8800.00 | 9000.00 | 0.00 | 0.00 | 0.00 | + | 5 | 06 January 2026 | 50.00 | 0.00 | 8750.00 | 9000.00 | 0.00 | 0.00 | 0.00 | + | 6 | 07 January 2026 | 50.00 | 0.00 | 8700.00 | 9000.00 | 0.00 | 0.00 | 0.00 | + | 7 | 08 January 2026 | 50.00 | 0.00 | 8650.00 | 9000.00 | 0.00 | 0.00 | 0.00 | + | 8 | 09 January 2026 | 50.00 | 0.00 | 8600.00 | 9000.00 | 0.00 | 0.00 | 0.00 | + | 9 | 10 January 2026 | 50.00 | 0.00 | 8550.00 | 9000.00 | 0.00 | 0.00 | 0.00 | + | 10 | 11 January 2026 | 50.00 | 0.00 | 8500.00 | 9000.00 | 0.00 | 0.00 | 0.00 | + | 11 | 12 January 2026 | 50.00 | 192.00 | 8450.00 | 8808.00 | 0.00 | 0.00 | 0.00 | + | 12 | 13 January 2026 | 50.00 | | 8400.00 | | 0.00 | | 0.00 | + | 13 | 14 January 2026 | 50.00 | | 8350.00 | | 0.00 | | 0.00 | + | 14 | 15 January 2026 | 50.00 | | 8300.00 | | 0.00 | | 0.00 | + | 15 | 16 January 2026 | 50.00 | | 8250.00 | | 0.00 | | 0.00 | + | 16 | 17 January 2026 | 50.00 | | 8200.00 | | 0.00 | | 0.00 | + | 17 | 18 January 2026 | 50.00 | | 8150.00 | | 0.00 | | 0.00 | + | 18 | 19 January 2026 | 50.00 | | 8100.00 | | 0.00 | | 0.00 | + | 19 | 20 January 2026 | 50.00 | | 8050.00 | | 0.00 | | 0.00 | + | 20 | 21 January 2026 | 50.00 | | 8000.00 | | 0.00 | | 0.00 | + | 21 | 22 January 2026 | 50.00 | | 7950.00 | | 0.00 | | 0.00 | + | 22 | 23 January 2026 | 50.00 | | 7900.00 | | 0.00 | | 0.00 | + | 23 | 24 January 2026 | 50.00 | | 7850.00 | | 0.00 | | 0.00 | + | 24 | 25 January 2026 | 50.00 | | 7800.00 | | 0.00 | | 0.00 | + | 25 | 26 January 2026 | 50.00 | | 7750.00 | | 0.00 | | 0.00 | + | 26 | 27 January 2026 | 50.00 | | 7700.00 | | 0.00 | | 0.00 | + | 27 | 28 January 2026 | 50.00 | | 7650.00 | | 0.00 | | 0.00 | + | 28 | 29 January 2026 | 50.00 | | 7600.00 | | 0.00 | | 0.00 | + | 29 | 30 January 2026 | 50.00 | | 7550.00 | | 0.00 | | 0.00 | + | 30 | 31 January 2026 | 50.00 | | 7500.00 | | 0.00 | | 0.00 | + | 31 | 01 February 2026 | 50.00 | | 7450.00 | | 0.00 | | 0.00 | + | 32 | 02 February 2026 | 50.00 | | 7400.00 | | 0.00 | | 0.00 | + | 33 | 03 February 2026 | 50.00 | | 7350.00 | | 0.00 | | 0.00 | + | 34 | 04 February 2026 | 50.00 | | 7300.00 | | 0.00 | | 0.00 | + | 35 | 05 February 2026 | 50.00 | | 7250.00 | | 0.00 | | 0.00 | + | 36 | 06 February 2026 | 50.00 | | 7200.00 | | 0.00 | | 0.00 | + | 37 | 07 February 2026 | 50.00 | | 7150.00 | | 0.00 | | 0.00 | + | 38 | 08 February 2026 | 50.00 | | 7100.00 | | 0.00 | | 0.00 | + | 39 | 09 February 2026 | 50.00 | | 7050.00 | | 0.00 | | 0.00 | + | 40 | 10 February 2026 | 50.00 | | 7000.00 | | 0.00 | | 0.00 | + | 41 | 11 February 2026 | 50.00 | | 6950.00 | | 0.00 | | 0.00 | + | 42 | 12 February 2026 | 50.00 | | 6900.00 | | 0.00 | | 0.00 | + | 43 | 13 February 2026 | 50.00 | | 6850.00 | | 0.00 | | 0.00 | + | 44 | 14 February 2026 | 50.00 | | 6800.00 | | 0.00 | | 0.00 | + | 45 | 15 February 2026 | 50.00 | | 6750.00 | | 0.00 | | 0.00 | + | 46 | 16 February 2026 | 50.00 | | 6700.00 | | 0.00 | | 0.00 | + | 47 | 17 February 2026 | 50.00 | | 6650.00 | | 0.00 | | 0.00 | + | 48 | 18 February 2026 | 50.00 | | 6600.00 | | 0.00 | | 0.00 | + | 49 | 19 February 2026 | 50.00 | | 6550.00 | | 0.00 | | 0.00 | + | 50 | 20 February 2026 | 50.00 | | 6500.00 | | 0.00 | | 0.00 | + | 51 | 21 February 2026 | 50.00 | | 6450.00 | | 0.00 | | 0.00 | + | 52 | 22 February 2026 | 50.00 | | 6400.00 | | 0.00 | | 0.00 | + | 53 | 23 February 2026 | 50.00 | | 6350.00 | | 0.00 | | 0.00 | + | 54 | 24 February 2026 | 50.00 | | 6300.00 | | 0.00 | | 0.00 | + | 55 | 25 February 2026 | 50.00 | | 6250.00 | | 0.00 | | 0.00 | + | 56 | 26 February 2026 | 50.00 | | 6200.00 | | 0.00 | | 0.00 | + | 57 | 27 February 2026 | 50.00 | | 6150.00 | | 0.00 | | 0.00 | + | 58 | 28 February 2026 | 50.00 | | 6100.00 | | 0.00 | | 0.00 | + | 59 | 01 March 2026 | 50.00 | | 6050.00 | | 0.00 | | 0.00 | + | 60 | 02 March 2026 | 50.00 | | 6000.00 | | 0.00 | | 0.00 | + | 61 | 03 March 2026 | 50.00 | | 5950.00 | | 0.00 | | 0.00 | + | 62 | 04 March 2026 | 50.00 | | 5900.00 | | 0.00 | | 0.00 | + | 63 | 05 March 2026 | 50.00 | | 5850.00 | | 0.00 | | 0.00 | + | 64 | 06 March 2026 | 50.00 | | 5800.00 | | 0.00 | | 0.00 | + | 65 | 07 March 2026 | 50.00 | | 5750.00 | | 0.00 | | 0.00 | + | 66 | 08 March 2026 | 50.00 | | 5700.00 | | 0.00 | | 0.00 | + | 67 | 09 March 2026 | 50.00 | | 5650.00 | | 0.00 | | 0.00 | + | 68 | 10 March 2026 | 50.00 | | 5600.00 | | 0.00 | | 0.00 | + | 69 | 11 March 2026 | 50.00 | | 5550.00 | | 0.00 | | 0.00 | + | 70 | 12 March 2026 | 50.00 | | 5500.00 | | 0.00 | | 0.00 | + | 71 | 13 March 2026 | 50.00 | | 5450.00 | | 0.00 | | 0.00 | + | 72 | 14 March 2026 | 50.00 | | 5400.00 | | 0.00 | | 0.00 | + | 73 | 15 March 2026 | 50.00 | | 5350.00 | | 0.00 | | 0.00 | + | 74 | 16 March 2026 | 50.00 | | 5300.00 | | 0.00 | | 0.00 | + | 75 | 17 March 2026 | 50.00 | | 5250.00 | | 0.00 | | 0.00 | + | 76 | 18 March 2026 | 50.00 | | 5200.00 | | 0.00 | | 0.00 | + | 77 | 19 March 2026 | 50.00 | | 5150.00 | | 0.00 | | 0.00 | + | 78 | 20 March 2026 | 50.00 | | 5100.00 | | 0.00 | | 0.00 | + | 79 | 21 March 2026 | 50.00 | | 5050.00 | | 0.00 | | 0.00 | + | 80 | 22 March 2026 | 50.00 | | 5000.00 | | 0.00 | | 0.00 | + | 81 | 23 March 2026 | 50.00 | | 4950.00 | | 0.00 | | 0.00 | + | 82 | 24 March 2026 | 50.00 | | 4900.00 | | 0.00 | | 0.00 | + | 83 | 25 March 2026 | 50.00 | | 4850.00 | | 0.00 | | 0.00 | + | 84 | 26 March 2026 | 50.00 | | 4800.00 | | 0.00 | | 0.00 | + | 85 | 27 March 2026 | 50.00 | | 4750.00 | | 0.00 | | 0.00 | + | 86 | 28 March 2026 | 50.00 | | 4700.00 | | 0.00 | | 0.00 | + | 87 | 29 March 2026 | 50.00 | | 4650.00 | | 0.00 | | 0.00 | + | 88 | 30 March 2026 | 50.00 | | 4600.00 | | 0.00 | | 0.00 | + | 89 | 31 March 2026 | 50.00 | | 4550.00 | | 0.00 | | 0.00 | + | 90 | 01 April 2026 | 50.00 | | 4500.00 | | 0.00 | | 0.00 | + | 91 | 02 April 2026 | 50.00 | | 4450.00 | | 0.00 | | 0.00 | + | 92 | 03 April 2026 | 50.00 | | 4400.00 | | 0.00 | | 0.00 | + | 93 | 04 April 2026 | 50.00 | | 4350.00 | | 0.00 | | 0.00 | + | 94 | 05 April 2026 | 50.00 | | 4300.00 | | 0.00 | | 0.00 | + | 95 | 06 April 2026 | 50.00 | | 4250.00 | | 0.00 | | 0.00 | + | 96 | 07 April 2026 | 50.00 | | 4200.00 | | 0.00 | | 0.00 | + | 97 | 08 April 2026 | 50.00 | | 4150.00 | | 0.00 | | 0.00 | + | 98 | 09 April 2026 | 50.00 | | 4100.00 | | 0.00 | | 0.00 | + | 99 | 10 April 2026 | 50.00 | | 4050.00 | | 0.00 | | 0.00 | + | 100 | 11 April 2026 | 50.00 | | 4000.00 | | 0.00 | | 0.00 | + | 101 | 12 April 2026 | 50.00 | | 3950.00 | | 0.00 | | 0.00 | + | 102 | 13 April 2026 | 50.00 | | 3900.00 | | 0.00 | | 0.00 | + | 103 | 14 April 2026 | 50.00 | | 3850.00 | | 0.00 | | 0.00 | + | 104 | 15 April 2026 | 50.00 | | 3800.00 | | 0.00 | | 0.00 | + | 105 | 16 April 2026 | 50.00 | | 3750.00 | | 0.00 | | 0.00 | + | 106 | 17 April 2026 | 50.00 | | 3700.00 | | 0.00 | | 0.00 | + | 107 | 18 April 2026 | 50.00 | | 3650.00 | | 0.00 | | 0.00 | + | 108 | 19 April 2026 | 50.00 | | 3600.00 | | 0.00 | | 0.00 | + | 109 | 20 April 2026 | 50.00 | | 3550.00 | | 0.00 | | 0.00 | + | 110 | 21 April 2026 | 50.00 | | 3500.00 | | 0.00 | | 0.00 | + | 111 | 22 April 2026 | 50.00 | | 3450.00 | | 0.00 | | 0.00 | + | 112 | 23 April 2026 | 50.00 | | 3400.00 | | 0.00 | | 0.00 | + | 113 | 24 April 2026 | 50.00 | | 3350.00 | | 0.00 | | 0.00 | + | 114 | 25 April 2026 | 50.00 | | 3300.00 | | 0.00 | | 0.00 | + | 115 | 26 April 2026 | 50.00 | | 3250.00 | | 0.00 | | 0.00 | + | 116 | 27 April 2026 | 50.00 | | 3200.00 | | 0.00 | | 0.00 | + | 117 | 28 April 2026 | 50.00 | | 3150.00 | | 0.00 | | 0.00 | + | 118 | 29 April 2026 | 50.00 | | 3100.00 | | 0.00 | | 0.00 | + | 119 | 30 April 2026 | 50.00 | | 3050.00 | | 0.00 | | 0.00 | + | 120 | 01 May 2026 | 50.00 | | 3000.00 | | 0.00 | | 0.00 | + | 121 | 02 May 2026 | 50.00 | | 2950.00 | | 0.00 | | 0.00 | + | 122 | 03 May 2026 | 50.00 | | 2900.00 | | 0.00 | | 0.00 | + | 123 | 04 May 2026 | 50.00 | | 2850.00 | | 0.00 | | 0.00 | + | 124 | 05 May 2026 | 50.00 | | 2800.00 | | 0.00 | | 0.00 | + | 125 | 06 May 2026 | 50.00 | | 2750.00 | | 0.00 | | 0.00 | + | 126 | 07 May 2026 | 50.00 | | 2700.00 | | 0.00 | | 0.00 | + | 127 | 08 May 2026 | 50.00 | | 2650.00 | | 0.00 | | 0.00 | + | 128 | 09 May 2026 | 50.00 | | 2600.00 | | 0.00 | | 0.00 | + | 129 | 10 May 2026 | 50.00 | | 2550.00 | | 0.00 | | 0.00 | + | 130 | 11 May 2026 | 50.00 | | 2500.00 | | 0.00 | | 0.00 | + | 131 | 12 May 2026 | 50.00 | | 2450.00 | | 0.00 | | 0.00 | + | 132 | 13 May 2026 | 50.00 | | 2400.00 | | 0.00 | | 0.00 | + | 133 | 14 May 2026 | 50.00 | | 2350.00 | | 0.00 | | 0.00 | + | 134 | 15 May 2026 | 50.00 | | 2300.00 | | 0.00 | | 0.00 | + | 135 | 16 May 2026 | 50.00 | | 2250.00 | | 0.00 | | 0.00 | + | 136 | 17 May 2026 | 50.00 | | 2200.00 | | 0.00 | | 0.00 | + | 137 | 18 May 2026 | 50.00 | | 2150.00 | | 0.00 | | 0.00 | + | 138 | 19 May 2026 | 50.00 | | 2100.00 | | 0.00 | | 0.00 | + | 139 | 20 May 2026 | 50.00 | | 2050.00 | | 0.00 | | 0.00 | + | 140 | 21 May 2026 | 50.00 | | 2000.00 | | 0.00 | | 0.00 | + | 141 | 22 May 2026 | 50.00 | | 1950.00 | | 0.00 | | 0.00 | + | 142 | 23 May 2026 | 50.00 | | 1900.00 | | 0.00 | | 0.00 | + | 143 | 24 May 2026 | 50.00 | | 1850.00 | | 0.00 | | 0.00 | + | 144 | 25 May 2026 | 50.00 | | 1800.00 | | 0.00 | | 0.00 | + | 145 | 26 May 2026 | 50.00 | | 1750.00 | | 0.00 | | 0.00 | + | 146 | 27 May 2026 | 50.00 | | 1700.00 | | 0.00 | | 0.00 | + | 147 | 28 May 2026 | 50.00 | | 1650.00 | | 0.00 | | 0.00 | + | 148 | 29 May 2026 | 50.00 | | 1600.00 | | 0.00 | | 0.00 | + | 149 | 30 May 2026 | 50.00 | | 1550.00 | | 0.00 | | 0.00 | + | 150 | 31 May 2026 | 50.00 | | 1500.00 | | 0.00 | | 0.00 | + | 151 | 01 June 2026 | 50.00 | | 1450.00 | | 0.00 | | 0.00 | + | 152 | 02 June 2026 | 50.00 | | 1400.00 | | 0.00 | | 0.00 | + | 153 | 03 June 2026 | 50.00 | | 1350.00 | | 0.00 | | 0.00 | + | 154 | 04 June 2026 | 50.00 | | 1300.00 | | 0.00 | | 0.00 | + | 155 | 05 June 2026 | 50.00 | | 1250.00 | | 0.00 | | 0.00 | + | 156 | 06 June 2026 | 50.00 | | 1200.00 | | 0.00 | | 0.00 | + | 157 | 07 June 2026 | 50.00 | | 1150.00 | | 0.00 | | 0.00 | + | 158 | 08 June 2026 | 50.00 | | 1100.00 | | 0.00 | | 0.00 | + | 159 | 09 June 2026 | 50.00 | | 1050.00 | | 0.00 | | 0.00 | + | 160 | 10 June 2026 | 50.00 | | 1000.00 | | 0.00 | | 0.00 | + | 161 | 11 June 2026 | 50.00 | | 950.00 | | 0.00 | | 0.00 | + | 162 | 12 June 2026 | 50.00 | | 900.00 | | 0.00 | | 0.00 | + | 163 | 13 June 2026 | 50.00 | | 850.00 | | 0.00 | | 0.00 | + | 164 | 14 June 2026 | 50.00 | | 800.00 | | 0.00 | | 0.00 | + | 165 | 15 June 2026 | 50.00 | | 750.00 | | 0.00 | | 0.00 | + | 166 | 16 June 2026 | 50.00 | | 700.00 | | 0.00 | | 0.00 | + | 167 | 17 June 2026 | 50.00 | | 650.00 | | 0.00 | | 0.00 | + | 168 | 18 June 2026 | 50.00 | | 600.00 | | 0.00 | | 0.00 | + | 169 | 19 June 2026 | 50.00 | | 550.00 | | 0.00 | | 0.00 | + | 170 | 20 June 2026 | 50.00 | | 500.00 | | 0.00 | | 0.00 | + | 171 | 21 June 2026 | 50.00 | | 450.00 | | 0.00 | | 0.00 | + | 172 | 22 June 2026 | 50.00 | | 400.00 | | 0.00 | | 0.00 | + | 173 | 23 June 2026 | 50.00 | | 350.00 | | 0.00 | | 0.00 | + | 174 | 24 June 2026 | 50.00 | | 300.00 | | 0.00 | | 0.00 | + | 175 | 25 June 2026 | 50.00 | | 250.00 | | 0.00 | | 0.00 | + | 176 | 26 June 2026 | 50.00 | | 200.00 | | 0.00 | | 0.00 | + | 177 | 27 June 2026 | 50.00 | | 150.00 | | 0.00 | | 0.00 | + | 178 | 28 June 2026 | 50.00 | | 100.00 | | 0.00 | | 0.00 | + | 179 | 29 June 2026 | 50.00 | | 50.00 | | 0.00 | | 0.00 | + | 180 | 30 June 2026 | 50.00 | | 0.00 | | 0.00 | | 0.00 | + | 181 | 01 July 2026 | 50.00 | | 0.00 | | 0.00 | | 0.00 | + | 182 | 02 July 2026 | 50.00 | | 0.00 | | 0.00 | | 0.00 | + | 183 | 03 July 2026 | 50.00 | | 0.00 | | 0.00 | | 0.00 | + | 184 | 04 July 2026 | 50.00 | | 0.00 | | 0.00 | | 0.00 | + | 185 | 05 July 2026 | 50.00 | | 0.00 | | 0.00 | | 0.00 | + | 186 | 06 July 2026 | 50.00 | | 0.00 | | 0.00 | | 0.00 | + | 187 | 07 July 2026 | 50.00 | | 0.00 | | 0.00 | | 0.00 | + | 188 | 08 July 2026 | 8.00 | | 0.00 | | 0.00 | | 0.00 | + When Admin sets the business date to "15 March 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + Then Admin closes the Working Capital loan with a full repayment on "15 March 2026" + + Scenario: Verify Working Capital Goodwill Credit uses GOODWILL_CREDIT specific payment allocation rule (DUE_PRINCIPAL_FEE_PENALTY) - UC12 + Given Admin sets the business date to "01 January 2026" + And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_GOODWILL_CREDIT_ALLOCATION | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 | + When Admin sets the business date to "10 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "12 January 2026" due date and 15.0 transaction amount + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_PENALTY" specified due date charge to working capital loan with "12 January 2026" due date and 25.0 transaction amount + Then Working Capital Loan has charges with the following data: + | Charge Name | Due Date | Amount | Currency | isPenalty | Charge Time Type | Charge Calculation Type | Charge Payment mode | + | Working Capital Loan Fee | 12 January 2026 | 15.0 | EUR | false | Specified due date | Flat | Regular | + | Working Capital Loan Penalty | 12 January 2026 | 25.0 | EUR | true | Specified due date | Flat | Regular | + When Admin sets the business date to "12 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Customer makes "GOODWILL_CREDIT" transaction on "12 January 2026" with 270.0 transaction amount on Working Capital loan + And Working capital loan account has the correct data: + | principal | totalPaidPrincipal | totalPaymentVolume | realizedIncome | unrealizedIncome | overpaymentAmount | + | 9000.0 | 270.0 | 100000.0 | 0.0 | 0.0 | 0.0 | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 15.0 | 15.0 | 0.0 | 25.0 | 25.0 | 0.0 | + And Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 12 January 2026 | Goodwill Credit | 270.0 | 270.0 | 0.0 | 0.0 | false | + When Admin sets the business date to "15 March 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + Then Admin closes the Working Capital loan with a full repayment on "15 March 2026" + + Scenario: Verify Working Capital Goodwill Credit (DUE_PRINCIPAL_FEE_PENALTY) and Repayment (DUE_FEE_PENALTY_PRINCIPAL) apply different payment allocation rules on the same loan - UC13 + Given Admin sets the business date to "01 January 2026" + And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_GOODWILL_CREDIT_ALLOCATION | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 | + When Admin sets the business date to "10 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "12 January 2026" due date and 15.0 transaction amount + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_PENALTY" specified due date charge to working capital loan with "12 January 2026" due date and 25.0 transaction amount + When Admin sets the business date to "12 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Customer makes "GOODWILL_CREDIT" transaction on "12 January 2026" with 270.0 transaction amount on Working Capital loan + Then Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 12 January 2026 | Goodwill Credit | 270.0 | 270.0 | 0.0 | 0.0 | false | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 15.0 | 15.0 | 0.0 | 25.0 | 25.0 | 0.0 | + When Admin sets the business date to "15 January 2026" + And Customer makes repayment on "15 January 2026" with 40.0 transaction amount on Working Capital loan + Then Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 12 January 2026 | Goodwill Credit | 270.0 | 270.0 | 0.0 | 0.0 | false | + | 15 January 2026 | Repayment | 40.0 | 0.0 | 15.0 | 25.0 | false | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 15.0 | 0.0 | 15.0 | 25.0 | 0.0 | 25.0 | + When Admin sets the business date to "20 February 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + Then Admin closes the Working Capital loan with a full repayment on "20 February 2026" diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAllocationRequestFactory.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAllocationRequestFactory.java index 71906044244..90104476971 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAllocationRequestFactory.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAllocationRequestFactory.java @@ -21,7 +21,7 @@ import java.math.BigDecimal; import java.time.LocalDate; import java.util.List; -import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationTransactionType; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; import org.apache.fineract.portfolio.workingcapitalloan.data.WorkingCapitalLoanAllocationRequest; import org.apache.fineract.portfolio.workingcapitalloan.data.WorkingCapitalLoanAllocationRequest.ChargeBalance; import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoan; @@ -34,37 +34,48 @@ /** * Builds the immutable {@link WorkingCapitalLoanAllocationRequest} from the live loan / balance / charge entities. Owns * the JPA access for the allocation decision: maps charges to {@link ChargeBalance} and resolves the configured payment - * allocation order (REPAYMENT rule, falling back to the DEFAULT rule, falling back to principal-only). The processor - * itself stays free of entities. + * allocation order for the transaction type (specific rule, falling back to DEFAULT, then principal-only). The + * processor itself stays free of entities. */ @Component public class WorkingCapitalLoanAllocationRequestFactory { public WorkingCapitalLoanAllocationRequest build(final WorkingCapitalLoan loan, final WorkingCapitalLoanBalance balance, - final List charges, final LocalDate transactionDate, final BigDecimal amount) { + final List charges, final LocalDate transactionDate, final BigDecimal amount, + final LoanTransactionType transactionType) { final List chargeBalances = charges.stream().map( charge -> new ChargeBalance(charge.getId(), charge.getAmountOutstanding(), charge.getDueDate(), charge.isPenaltyCharge())) .toList(); - return new WorkingCapitalLoanAllocationRequest(transactionDate, amount, resolveAllocationOrder(loan), + return new WorkingCapitalLoanAllocationRequest(transactionDate, amount, resolveAllocationOrder(loan, transactionType), balance.getPrincipalOutstanding(), chargeBalances); } - private List resolveAllocationOrder(final WorkingCapitalLoan loan) { - final List rules = loan.getPaymentAllocationRules(); - if (rules != null && !rules.isEmpty()) { - final WorkingCapitalLoanPaymentAllocationRule repaymentRule = findRule(rules, PaymentAllocationTransactionType.REPAYMENT); - final WorkingCapitalLoanPaymentAllocationRule rule = repaymentRule != null ? repaymentRule - : findRule(rules, PaymentAllocationTransactionType.DEFAULT); - if (rule != null && rule.getAllocationTypes() != null && !rule.getAllocationTypes().isEmpty()) { - return rule.getAllocationTypes(); - } + private List resolveAllocationOrder(final WorkingCapitalLoan loan, + final LoanTransactionType transactionType) { + final WorkingCapitalLoanPaymentAllocationRule rule = transactionType == null ? getDefaultAllocationRule(loan) + : getAllocationRule(loan, transactionType); + if (rule != null && rule.getAllocationTypes() != null && !rule.getAllocationTypes().isEmpty()) { + return rule.getAllocationTypes(); } // No configured order: keep the legacy principal-only behaviour. return List.of(WorkingCapitalPaymentAllocationType.DUE_PRINCIPAL); } - private WorkingCapitalLoanPaymentAllocationRule findRule(final List rules, - final PaymentAllocationTransactionType transactionType) { - return rules.stream().filter(rule -> transactionType.equals(rule.getTransactionType())).findFirst().orElse(null); + private WorkingCapitalLoanPaymentAllocationRule getAllocationRule(final WorkingCapitalLoan loan, + final LoanTransactionType transactionType) { + final List rules = loan.getPaymentAllocationRules(); + if (rules == null || rules.isEmpty()) { + return null; + } + return rules.stream().filter(rule -> transactionType.equals(rule.getTransactionType().getLoanTransactionType())).findFirst() + .orElseGet(() -> getDefaultAllocationRule(loan)); + } + + private WorkingCapitalLoanPaymentAllocationRule getDefaultAllocationRule(final WorkingCapitalLoan loan) { + final List rules = loan.getPaymentAllocationRules(); + if (rules == null || rules.isEmpty()) { + return null; + } + return rules.stream().filter(rule -> rule.getTransactionType().isDefault()).findFirst().orElse(null); } } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanTransactionReprocessingServiceImpl.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanTransactionReprocessingServiceImpl.java index 67caf462c18..aa3333705f3 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanTransactionReprocessingServiceImpl.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanTransactionReprocessingServiceImpl.java @@ -144,7 +144,7 @@ private void reprocessTransactions(final WorkingCapitalLoan loan, final List Date: Fri, 10 Jul 2026 16:24:17 +0200 Subject: [PATCH 2/3] FINERACT-2455: added e2e tests for validation of WC - Transaction Type - GoodWill Credit - Payment Allocation handling --- .../DefaultWorkingCapitalLoanProduct.java | 3 +- .../factory/WorkingCapitalRequestFactory.java | 6 + .../test/helper/ErrorMessageHelper.java | 4 + .../stepdef/loan/WorkingCapitalStepDef.java | 81 ++++--- .../fineract/test/support/TestContextKey.java | 1 + .../global/WorkingCapitalInitializerStep.java | 21 ++ ...talGoodwillCreditPaymentAllocation.feature | 215 +++++++++++++++++- ...oanGoodwillCreditAccountingEntries.feature | 18 ++ .../WorkingCapitalLoanProduct.feature | 4 + 9 files changed, 316 insertions(+), 37 deletions(-) diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/workingcapitalproduct/DefaultWorkingCapitalLoanProduct.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/workingcapitalproduct/DefaultWorkingCapitalLoanProduct.java index 3295a1df86e..884e208d68e 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/workingcapitalproduct/DefaultWorkingCapitalLoanProduct.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/workingcapitalproduct/DefaultWorkingCapitalLoanProduct.java @@ -37,7 +37,8 @@ public enum DefaultWorkingCapitalLoanProduct implements WorkingCapitalLoanProduc WCLP_IN_ADVANCE_PENALTY_FEE_PRINCIPAL, // WCLP_DUE_FEE_PRINCIPAL_PENALTY, // WCLP_DUE_PRINCIPAL_FEE_PENALTY, // - WCLP_GOODWILL_CREDIT_ALLOCATION; // + WCLP_GOODWILL_CREDIT_ALLOCATION, // + WCLP_REPAYMENT_DIFF_DEFAULT; // @Override public String getName() { diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/WorkingCapitalRequestFactory.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/WorkingCapitalRequestFactory.java index 0cf52835a52..02db6285451 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/WorkingCapitalRequestFactory.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/factory/WorkingCapitalRequestFactory.java @@ -237,6 +237,12 @@ public List invalidPaymentAllocationRulesForWorkingCapita List.of(DUE_FEE, DUE_PRINCIPAL, "INTEREST", IN_ADVANCE_FEE, IN_ADVANCE_PRINCIPAL, IN_ADVANCE_PENALTY)));// } + public List paymentAllocationRulesWithoutDefaultForWorkingCapitalLoanProductCreateRequest() { + return List.of(// + createPaymentAllocation(PostPaymentAllocation.TransactionTypeEnum.GOODWILL_CREDIT.getValue(), // + List.of(DUE_PRINCIPAL, DUE_FEE, DUE_PENALTY, IN_ADVANCE_PRINCIPAL, IN_ADVANCE_FEE, IN_ADVANCE_PENALTY)));// + } + public List invalidNumberOfPaymentAllocationRulesForWorkingCapitalLoanProductUpdateRequest() { return List.of(// createPaymentAllocation(PostPaymentAllocation.TransactionTypeEnum.DEFAULT.getValue(), // diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java index 2fb1ceb0415..c20be4369a8 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java @@ -1054,6 +1054,10 @@ public static String paymentAllocationRulesDuplicateFailure() { return "The list of provided payment allocation rules must not contain any duplicates"; } + public static String paymentAllocationRulesWithoutDefaultFailure() { + return "At least one DEFAULT payment allocation must be provided"; + } + public static String workingCapitalLoanProductIdentifiedDoesNotExistFailure(String identifierId) { return String.format("Working Capital Loan Product with identifier %s does not exist", identifierId); } diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalStepDef.java index dacef19ecc3..047e23e01b2 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalStepDef.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalStepDef.java @@ -126,6 +126,7 @@ public class WorkingCapitalStepDef extends AbstractStepDef { public static final String LOCALE_FIELD_NAME = "locale"; private static final long NON_EXISTENT_GL_ACCOUNT_ID = 999999L; + private static final int RANDOM_NAME_SUFFIX_LENGTH = 10; private WorkingCapitalLoanProductsApi workingCapitalApi() { return fineractFeignClient.workingCapitalLoanProducts(); @@ -134,7 +135,7 @@ private WorkingCapitalLoanProductsApi workingCapitalApi() { @When("Admin creates a new Working Capital Loan Product") public void createWorkingCapitalLoanProduct() { final String workingCapitalProductDefaultName = DefaultWorkingCapitalLoanProduct.WCLP.getName() - + Utils.randomStringGenerator("_", 10); + + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest defaultWorkingCapitalLoanProductCreateRequest = workingCapitalRequestFactory .defaultWorkingCapitalLoanProductRequest() // .name(workingCapitalProductDefaultName); // @@ -150,7 +151,7 @@ public void createWorkingCapitalLoanProductWithBreachAndNearBreach() { final Long breachId = getWcBreachIdForFrequency(2, WorkingCapitalBreachFrequencyType.MONTHS.getCode()); final Long nearBreachId = getWcNearBreachIdForFrequency(1, WorkingCapitalBreachFrequencyType.MONTHS.getCode()); - final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); + final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest request = workingCapitalRequestFactory.defaultWorkingCapitalLoanProductRequest() // .name(name) // .breachId(breachId) // @@ -168,7 +169,7 @@ public void createWorkingCapitalLoanProductWithBreachAndNearBreach(int breachFre final Long breachId = getWcBreachIdForFrequency(breachFrequency, breachFrequencyType); final Long nearBreachId = getWcNearBreachIdForFrequency(nearBreachFrequency, nearBreachFrequencyType); - final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); + final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest request = workingCapitalRequestFactory.defaultWorkingCapitalLoanProductRequest() // .name(name) // .breachId(breachId) // @@ -184,7 +185,7 @@ public void createWorkingCapitalLoanProductWithBreachAndNearBreach(int breachFre public void createWorkingCapitalLoanProductWithoutBreachButNearBreachFailure() { final Long nearBreachId = getWcNearBreachIdForFrequency(1, WorkingCapitalBreachFrequencyType.MONTHS.getCode()); - final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); + final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest request = workingCapitalRequestFactory.defaultWorkingCapitalLoanProductRequest() // .name(name) // .nearBreachId(nearBreachId); // @@ -204,7 +205,7 @@ public void createWCLPWithBreachFrequencyLowerThenNearBreachFailure(int breachFr final Long breachId = getWcBreachIdForFrequency(breachFrequency, breachFrequencyType); final Long nearBreachId = getWcNearBreachIdForFrequency(nearBreachFrequency, nearBreachFrequencyType); - final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); + final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest request = workingCapitalRequestFactory.defaultWorkingCapitalLoanProductRequest() // .name(name) // .breachId(breachId) // @@ -273,7 +274,7 @@ public void updateWCLPWithBreachFrequencyLowerThenNearBreachFailure(int breachFr public void updateWorkingCapitalLoanProductWithoutBreachButNearBreachFailure() { final Long nearBreachId = getWcNearBreachIdForFrequency(1, WorkingCapitalBreachFrequencyType.MONTHS.getCode()); - final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); + final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); PutWorkingCapitalLoanProductsProductIdRequest defaultWorkingCapitalLoanProductUpdateRequest = new PutWorkingCapitalLoanProductsProductIdRequest() .name(name) // .nearBreachId(nearBreachId); // @@ -293,7 +294,7 @@ public void createWorkingCapitalLoanProductWithBreachId() { final Long breachId = breachCreateResponse.getResourceId(); testContext().set(WORKING_CAPITAL_BREACH_ID, breachId); - final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); + final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest request = workingCapitalRequestFactory.defaultWorkingCapitalLoanProductRequest() // .name(name) // .breachId(breachId); @@ -311,7 +312,7 @@ public void createWorkingCapitalLoanProductWithBreachIdAndOverrides() { final Long breachId = breachCreateResponse.getResourceId(); testContext().set(TestContextKey.WORKING_CAPITAL_BREACH_ID, breachId); - final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); + final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest request = workingCapitalRequestFactory .defaultWorkingCapitalLoanProductAllowAttributesOverrideRequest() // .name(name) // @@ -343,7 +344,7 @@ public void createWorkingCapitalLoanProductWithCustomBreachConfig(final DataTabl final Integer breachGraceDays = breachGraceDaysStr != null && !breachGraceDaysStr.isEmpty() ? Integer.valueOf(breachGraceDaysStr) : null; - final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); + final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest request = workingCapitalRequestFactory .defaultWorkingCapitalLoanProductAllowAttributesOverrideRequest() // .name(name) // @@ -387,7 +388,7 @@ public void createWorkingCapitalLoanProductWithBreachAndNearBreachConfig(final D final Integer breachGraceDays = breachGraceDaysStr != null && !breachGraceDaysStr.isEmpty() ? Integer.valueOf(breachGraceDaysStr) : null; - final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); + final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest request = workingCapitalRequestFactory .defaultWorkingCapitalLoanProductAllowAttributesOverrideRequest() // .name(name) // @@ -405,7 +406,7 @@ public void createWorkingCapitalLoanProductWithBreachAndNearBreachConfig(final D @When("Admin creates a new Working Capital Loan Product with existing WC Delinquency Bucket") public void createWorkingCapitalLoanProductWithExistingDelinquencyBucket() { final Long bucketId = TestContext.GLOBAL.get(TestContextKey.DELINQUENCY_BUCKET_ID); - final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); + final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest request = workingCapitalRequestFactory.defaultWorkingCapitalLoanProductRequest() // .name(name) // .delinquencyBucketId(bucketId); @@ -418,7 +419,7 @@ public void createWorkingCapitalLoanProductWithExistingDelinquencyBucket() { @When("Admin creates a new Working Capital Loan Product with existing WC Breach") public void createWorkingCapitalLoanProductWithExistingBreach() { final Long breachId = TestContext.INSTANCE.get(TestContextKey.WORKING_CAPITAL_BREACH_ID); - final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); + final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest request = workingCapitalRequestFactory.defaultWorkingCapitalLoanProductRequest() // .name(name) // .breachId(breachId); @@ -431,7 +432,7 @@ public void createWorkingCapitalLoanProductWithExistingBreach() { @When("Admin creates a new Working Capital Loan Product with external-id") public void createWorkingCapitalLoanProductWithExternalId() { final String workingCapitalProductDefaultName = DefaultWorkingCapitalLoanProduct.WCLP.getName() - + Utils.randomStringGenerator("_", 10); + + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest defaultWorkingCapitalLoanProductCreateRequest = workingCapitalRequestFactory .defaultWorkingCapitalLoanProductRequest() // .name(workingCapitalProductDefaultName) // @@ -452,7 +453,7 @@ public void createWorkingCapitalLoanProductWithEmptyDataFailed(String fieldName, @Then("Admin failed to create a new Working Capital Loan Product field {string} with max length data {int} while max allowed is {int}") public void createWorkingCapitalLoanProductWithMaxLengthDataFailed(String fieldName, int maxLengthValue, int maxAllowedLengthValue) { final String workingCapitalProductDefaultName = DefaultWorkingCapitalLoanProduct.WCLP.getName() - + Utils.randomStringGenerator("_", 10); + + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest defaultWorkingCapitalLoanProductCreateRequest = workingCapitalRequestFactory .defaultWorkingCapitalLoanProductRequest() // .name(workingCapitalProductDefaultName); // @@ -483,7 +484,7 @@ public void createWorkingCapitalLoanProductWithBreachWithInvalidDataFailed(Strin @Then("Admin failed to create a new Working Capital Loan Product with invalid number of payment allocation rules") public void createWorkingCapitalLoanProductWithInvalidNumberPaymentAllocationFailed() { final String workingCapitalProductDefaultName = DefaultWorkingCapitalLoanProduct.WCLP.getName() - + Utils.randomStringGenerator("_", 10); + + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest defaultWorkingCapitalLoanProductCreateRequest = workingCapitalRequestFactory .defaultWorkingCapitalLoanProductRequest() // .name(workingCapitalProductDefaultName) // @@ -494,10 +495,24 @@ public void createWorkingCapitalLoanProductWithInvalidNumberPaymentAllocationFai checkCreateWorkingCapitalLoanProductWithInvalidDataFailure(defaultWorkingCapitalLoanProductCreateRequest, 400, errorMessage); } + @Then("Admin failed to create a new Working Capital Loan Product with payment allocation rules missing DEFAULT transaction type") + public void createWorkingCapitalLoanProductWithoutDefaultPaymentAllocationFailed() { + final String workingCapitalProductDefaultName = DefaultWorkingCapitalLoanProduct.WCLP.getName() + + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); + final PostWorkingCapitalLoanProductsRequest defaultWorkingCapitalLoanProductCreateRequest = workingCapitalRequestFactory + .defaultWorkingCapitalLoanProductRequest() // + .name(workingCapitalProductDefaultName) // + .paymentAllocation( + workingCapitalRequestFactory.paymentAllocationRulesWithoutDefaultForWorkingCapitalLoanProductCreateRequest()); + + String errorMessage = ErrorMessageHelper.paymentAllocationRulesWithoutDefaultFailure(); + checkCreateWorkingCapitalLoanProductWithInvalidDataFailure(defaultWorkingCapitalLoanProductCreateRequest, 400, errorMessage); + } + @Then("Admin failed to create a new Working Capital Loan Product with invalid value of payment allocation rules") public void createWorkingCapitalLoanProductWithInvalidPaymentAllocationFailed() { final String workingCapitalProductDefaultName = DefaultWorkingCapitalLoanProduct.WCLP.getName() - + Utils.randomStringGenerator("_", 10); + + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest defaultWorkingCapitalLoanProductCreateRequest = workingCapitalRequestFactory .defaultWorkingCapitalLoanProductRequest() // .name(workingCapitalProductDefaultName) // @@ -510,7 +525,7 @@ public void createWorkingCapitalLoanProductWithInvalidPaymentAllocationFailed() @When("Admin updates a Working Capital Loan Product") public void updateWorkingCapitalLoanProduct() { final String workingCapitalProductDefaultName = DefaultWorkingCapitalLoanProduct.WCLP.getName() - + Utils.randomStringGenerator("_", 10); + + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final String workingCapitalProductDefaultShortName = Utils.randomStringGenerator(4); final PutWorkingCapitalLoanProductsProductIdRequest workingCapitalLoanProductUpdateRequest = workingCapitalRequestFactory .defaultWorkingCapitalLoanProductRequestUpdate() // @@ -533,7 +548,7 @@ public void updateWorkingCapitalLoanProduct() { @When("Admin updates a Working Capital Loan Product via external-id") public void updateWorkingCapitalLoanProductViaExternalId() { final String workingCapitalProductDefaultName = DefaultWorkingCapitalLoanProduct.WCLP.getName() - + Utils.randomStringGenerator("_", 10); + + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final String workingCapitalProductDefaultShortName = Utils.randomStringGenerator(4); final PutWorkingCapitalLoanProductsProductIdRequest workingCapitalLoanProductUpdateRequest = workingCapitalRequestFactory .defaultWorkingCapitalLoanProductRequestUpdate() // @@ -708,7 +723,7 @@ public void checkWorkingCapitalLoanProductIsDeletedViaExternalId() { @When("Admin creates a new Working Capital Loan Product with accounting rule {string}") public void createWorkingCapitalLoanProductWithAccountingRule(final String accountingRule) { final String workingCapitalProductDefaultName = DefaultWorkingCapitalLoanProduct.WCLP.getName() - + Utils.randomStringGenerator("_", 10); + + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest request; if ("ACC_DEF_REV_AM".equals(accountingRule)) { request = workingCapitalRequestFactory.defaultWorkingCapitalLoanProductRequestWithAccrualAccounting()// @@ -726,7 +741,7 @@ public void createWorkingCapitalLoanProductWithAccountingRule(final String accou @When("Admin creates a new Working Capital Loan Product with Accrual with deferred revenue amortization accounting for GL mapping verification") public void createWorkingCapitalLoanProductWithAccrualAccountingForGLMappingVerification() { final String workingCapitalProductDefaultName = DefaultWorkingCapitalLoanProduct.WCLP.getName() - + Utils.randomStringGenerator("_", 10); + + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest request = workingCapitalRequestFactory .defaultWorkingCapitalLoanProductRequestWithDistinctAccrualAccountingMappings()// .name(workingCapitalProductDefaultName); @@ -771,7 +786,7 @@ public void verifyWorkingCapitalLoanProductHasAccountingRule(final String expect @Then("Admin failed to create a new Working Capital Loan Product with Accrual with deferred revenue amortization accounting and missing required GL accounts") public void createWorkingCapitalLoanProductWithAccrualAccountingMissingRequiredAccountsFailed() { final String workingCapitalProductDefaultName = DefaultWorkingCapitalLoanProduct.WCLP.getName() - + Utils.randomStringGenerator("_", 10); + + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest request = workingCapitalRequestFactory.defaultWorkingCapitalLoanProductRequest()// .name(workingCapitalProductDefaultName)// .accountingRule(AccountingRuleEnum.ACC_DEF_REV_AM); @@ -906,7 +921,7 @@ public void verifyWorkingCapitalLoanProductHasNoAccountingMappings() { @Then("Admin failed to create a Working Capital Loan Product with Accrual with deferred revenue amortization accounting and non-existent GL account ID with status {int}") public void createWorkingCapitalLoanProductWithNonExistentGLAccountFailed(final int expectedStatus) { - final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); + final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest request = workingCapitalRequestFactory .defaultWorkingCapitalLoanProductRequestWithAccrualAccounting()// .name(name)// @@ -987,7 +1002,7 @@ public void verifyWorkingCapitalLoanProductListContainsProductWithAccountingRule @Then("Admin failed to create a Working Capital Loan Product with wrong GL account type for loanPortfolio with status {int}") public void createWorkingCapitalLoanProductWithWrongGLAccountTypeFailed(final int expectedStatus) { - final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); + final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final Long incomeAccountId = accountTypeResolver.resolve(DefaultAccountType.INTEREST_INCOME); final PostWorkingCapitalLoanProductsRequest request = workingCapitalRequestFactory @@ -1272,7 +1287,7 @@ public void checkWorkingCapitalLoanProductCreate(PostWorkingCapitalLoanProductsR public void updateWorkingCapitalLoanProductWithBreachAndNearBreach(Long breachId, Long nearBreachId) { final String workingCapitalProductDefaultName = DefaultWorkingCapitalLoanProduct.WCLP.getName() - + Utils.randomStringGenerator("_", 10); + + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final String workingCapitalProductDefaultShortName = Utils.randomStringGenerator(4); final PutWorkingCapitalLoanProductsProductIdRequest workingCapitalLoanProductUpdateRequest = workingCapitalRequestFactory .defaultWorkingCapitalLoanProductRequestUpdate() // @@ -1439,7 +1454,7 @@ public void checkWorkingCapitalLoanProductUpdate(PutWorkingCapitalLoanProductsPr public void createWorkingCapitalLoanProductWithInvalidDataFailure(String fieldName, String value, String errorMessage) { final String workingCapitalProductDefaultName = DefaultWorkingCapitalLoanProduct.WCLP.getName() - + Utils.randomStringGenerator("_", 10); + + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest defaultWorkingCapitalLoanProductCreateRequest = workingCapitalRequestFactory .defaultWorkingCapitalLoanProductRequest() // .name(workingCapitalProductDefaultName); // @@ -1454,7 +1469,7 @@ public void createWorkingCapitalLoanProductWithBreachWithInvalidDataFailure(Stri final Long breachId = getWcBreachIdForFrequency(3, WorkingCapitalBreachFrequencyType.MONTHS.getCode()); final String workingCapitalProductDefaultName = DefaultWorkingCapitalLoanProduct.WCLP.getName() - + Utils.randomStringGenerator("_", 10); + + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest defaultWorkingCapitalLoanProductCreateRequest = workingCapitalRequestFactory .defaultWorkingCapitalLoanProductRequest() // .breachId(breachId).name(workingCapitalProductDefaultName); // @@ -1732,7 +1747,7 @@ public void checkWorkingCapitalLoanProductDeleteFailure(Long productId) { @When("Admin creates a new Working Capital Loan Product with delinquencyGraceDays {int} and delinquencyStartType {string}") public void createWorkingCapitalLoanProductWithGraceDays(int graceDays, String startType) { - final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); + final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest request = workingCapitalRequestFactory.defaultWorkingCapitalLoanProductRequest() // .name(name) // .delinquencyGraceDays(graceDays) // @@ -1792,7 +1807,8 @@ public void retrieveProductTemplate() { @When("Admin creates a new Working Capital Loan Product with Accrual with deferred revenue amortization accounting and advanced mappings") public void createWorkingCapitalLoanProductWithAdvancedMappings() { - final String productName = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); + final String productName = DefaultWorkingCapitalLoanProduct.WCLP.getName() + + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final PostWorkingCapitalLoanProductsRequest request = workingCapitalRequestFactory .defaultWorkingCapitalLoanProductRequestWithAccrualAccounting().name(productName); @@ -1881,7 +1897,8 @@ public void verifyTemplateAdvancedPaymentAllocationTypes(final DataTable table) @When("Admin creates a new Working Capital Loan Product with payment allocation order:") public void createWorkingCapitalLoanProductWithPaymentAllocationOrder(final DataTable table) { final List rules = table.asList(); - final String productName = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); + final String productName = DefaultWorkingCapitalLoanProduct.WCLP.getName() + + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); // Allow attribute overrides so loans created from this product can supply their own discount (the loan // creation step always sends a discount value). final PostWorkingCapitalLoanProductsRequest request = workingCapitalRequestFactory @@ -1938,7 +1955,8 @@ public void verifyWorkingCapitalLoanProductPaymentAllocationOrder(final DataTabl @Then("Admin failed to create a new Working Capital Loan Product with duplicate payment allocation rules") public void createWorkingCapitalLoanProductWithDuplicatePaymentAllocationRulesFailed() { - final String productName = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); + final String productName = DefaultWorkingCapitalLoanProduct.WCLP.getName() + + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); final List duplicateRules = List.of(// WorkingCapitalRequestFactory.DUE_PENALTY, WorkingCapitalRequestFactory.DUE_PENALTY, WorkingCapitalRequestFactory.DUE_FEE, WorkingCapitalRequestFactory.DUE_PRINCIPAL, WorkingCapitalRequestFactory.IN_ADVANCE_FEE, @@ -2099,7 +2117,8 @@ private PostWorkingCapitalLoanProductsRequest buildAdvancedMappingsRequest( List paymentChannelMappings, List chargeOffMappings, List writeOffMappings) { - final String productName = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); + final String productName = DefaultWorkingCapitalLoanProduct.WCLP.getName() + + Utils.randomStringGenerator("_", RANDOM_NAME_SUFFIX_LENGTH); return workingCapitalRequestFactory.defaultWorkingCapitalLoanProductAllowAttributesOverrideRequest().name(productName) .accountingRule(AccountingRuleEnum.ACC_DEF_REV_AM) .fundSourceAccountId(accountTypeResolver.resolve(DefaultAccountType.FUND_RECEIVABLES)) diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java index 70f7d5878db..e3b4b711486 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java @@ -346,6 +346,7 @@ public abstract class TestContextKey { public static final String DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE_WCLP_DUE_FEE_PRINCIPAL_PENALTY = "workingCapitalLoanProductCreateResponseWCLPDueFeePrincipalPenalty"; public static final String DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE_WCLP_DUE_PRINCIPAL_FEE_PENALTY = "workingCapitalLoanProductCreateResponseWCLPDuePrincipalFeePenalty"; public static final String DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE_WCLP_GOODWILL_CREDIT_ALLOCATION = "workingCapitalLoanProductCreateResponseWCLPGoodwillCreditAllocation"; + public static final String DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE_WCLP_REPAYMENT_DIFF_DEFAULT = "workingCapitalLoanProductCreateResponseWCLPRepaymentDiffDefault"; public static final String WC_LOAN_IDS = "wcLoanIds"; public static final String DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_REQUEST_FOR_UPDATE_WCLP = "workingCapitalLoanProductCreateRequestForUpdateWCLP"; public static final String DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE_FOR_UPDATE_WCLP = "workingCapitalLoanProductCreateResponseForUpdateWCLP"; diff --git a/fineract-e2e-tests-runner/src/test/java/org/apache/fineract/test/initializer/global/WorkingCapitalInitializerStep.java b/fineract-e2e-tests-runner/src/test/java/org/apache/fineract/test/initializer/global/WorkingCapitalInitializerStep.java index d1dcfc43d57..532f24966fc 100644 --- a/fineract-e2e-tests-runner/src/test/java/org/apache/fineract/test/initializer/global/WorkingCapitalInitializerStep.java +++ b/fineract-e2e-tests-runner/src/test/java/org/apache/fineract/test/initializer/global/WorkingCapitalInitializerStep.java @@ -246,6 +246,27 @@ public void initialize() throws Exception { createPaymentAllocation(PostPaymentAllocation.TransactionTypeEnum.GOODWILL_CREDIT.getValue(), List.of(DUE_PRINCIPAL, DUE_FEE, DUE_PENALTY, IN_ADVANCE_PRINCIPAL, IN_ADVANCE_FEE, IN_ADVANCE_PENALTY)))) + .paymentChannelToFundSourceMappings( + List.of(new org.apache.fineract.client.models.WorkingCapitalLoanPaymentChannelToFundSourceMappings() + .paymentTypeId(paymentTypeResolver.resolve(DefaultPaymentType.MONEY_TRANSFER)) + .fundSourceAccountId(accountTypeResolver.resolve(DefaultAccountType.FUND_RECEIVABLES)))))), + () -> TestContext.INSTANCE.set( + TestContextKey.DEFAULT_WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE_WCLP_REPAYMENT_DIFF_DEFAULT, + createWorkingCapitalLoanProductIdempotent(workingCapitalRequestFactory + .defaultWorkingCapitalLoanProductRequestWithAccrualAccounting() + .name(DefaultWorkingCapitalLoanProduct.WCLP_REPAYMENT_DIFF_DEFAULT.getName()) + .allowAttributeOverrides(allowAttributeOverrides) + .overpaymentLiabilityAccountId(accountTypeResolver.resolve(DefaultAccountType.OTHER_CREDIT_LIABILITY)) + // DEFAULT differs from REPAYMENT on purpose and no GOODWILL_CREDIT rule is configured: + // goodwill credits must fall back to DEFAULT (principal-first), not to REPAYMENT + // (fee-first). + .paymentAllocation(List.of( + createPaymentAllocation(PostPaymentAllocation.TransactionTypeEnum.DEFAULT.getValue(), + List.of(DUE_PRINCIPAL, DUE_FEE, DUE_PENALTY, IN_ADVANCE_PRINCIPAL, IN_ADVANCE_FEE, + IN_ADVANCE_PENALTY)), + createPaymentAllocation(PostPaymentAllocation.TransactionTypeEnum.REPAYMENT.getValue(), + List.of(DUE_FEE, DUE_PENALTY, DUE_PRINCIPAL, IN_ADVANCE_FEE, IN_ADVANCE_PENALTY, + IN_ADVANCE_PRINCIPAL)))) .paymentChannelToFundSourceMappings( List.of(new org.apache.fineract.client.models.WorkingCapitalLoanPaymentChannelToFundSourceMappings() .paymentTypeId(paymentTypeResolver.resolve(DefaultPaymentType.MONEY_TRANSFER)) diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalGoodwillCreditPaymentAllocation.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalGoodwillCreditPaymentAllocation.feature index 9a46c07ac2d..8f22cca33d2 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalGoodwillCreditPaymentAllocation.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalGoodwillCreditPaymentAllocation.feature @@ -2,7 +2,8 @@ @WorkingCapitalGoodwillCreditPaymentAllocationFeature Feature: Working Capital Goodwill Credit Payment Allocation - Scenario: Verify Working Capital Goodwill Credit falls back to DEFAULT payment allocation rule with DUE_FEE_PENALTY_PRINCIPAL order - UC1 + @TestRailId:C85511 + Scenario: Verify Working Capital Goodwill Credit transaction with fee and penalty added with DUE_FEE_PENALTY_PRINCIPAL allocation - UC1 Given Admin sets the business date to "01 January 2026" And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | @@ -32,6 +33,7 @@ Feature: Working Capital Goodwill Credit Payment Allocation And Admin runs inline COB job for Working Capital Loan by loanId Then Admin closes the Working Capital loan with a full repayment on "15 March 2026" + @TestRailId:C85512 Scenario: Verify Working Capital Goodwill Credit transaction with fee and penalty added with DUE_PENALTY_FEE_PRINCIPAL allocation - UC2 Given Admin sets the business date to "01 January 2026" And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: @@ -47,21 +49,22 @@ Feature: Working Capital Goodwill Credit Payment Allocation | Working Capital Loan Penalty | 12 January 2026 | 25.0 | EUR | true | Specified due date | Flat | Regular | When Admin sets the business date to "12 January 2026" And Admin runs inline COB job for Working Capital Loan by loanId - And Customer makes "GOODWILL_CREDIT" transaction on "12 January 2026" with 270.0 transaction amount on Working Capital loan + And Customer makes "GOODWILL_CREDIT" transaction on "12 January 2026" with 30.0 transaction amount on Working Capital loan And Working capital loan account has the correct data: | principal | totalPaidPrincipal | totalPaymentVolume | realizedIncome | unrealizedIncome | overpaymentAmount | - | 9000.0 | 230.0 | 100000.0 | 0.0 | 0.0 | 0.0 | + | 9000.0 | 0.0 | 100000.0 | 0.0 | 0.0 | 0.0 | And Working Capital Loan charge balances has the following data: | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | - | 15.0 | 0.0 | 15.0 | 25.0 | 0.0 | 25.0 | + | 15.0 | 10.0 | 5.0 | 25.0 | 0.0 | 25.0 | And Working Capital Loan has transactions: | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | - | 12 January 2026 | Goodwill Credit | 270.0 | 230.0 | 15.0 | 25.0 | false | + | 12 January 2026 | Goodwill Credit | 30.0 | 0.0 | 5.0 | 25.0 | false | When Admin sets the business date to "15 March 2026" And Admin runs inline COB job for Working Capital Loan by loanId Then Admin closes the Working Capital loan with a full repayment on "15 March 2026" + @TestRailId:C85513 Scenario: Verify Working Capital Goodwill Credit transaction with fee and penalty added with DUE_PRINCIPAL_FEE_PENALTY allocation - UC3 Given Admin sets the business date to "01 January 2026" And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: @@ -92,6 +95,7 @@ Feature: Working Capital Goodwill Credit Payment Allocation And Admin runs inline COB job for Working Capital Loan by loanId Then Admin closes the Working Capital loan with a full repayment on "15 March 2026" + @TestRailId:C85514 Scenario: Verify Working Capital Goodwill Credit transaction with fee and penalty added with DUE_FEE_PRINCIPAL_PENALTY allocation - UC4 Given Admin sets the business date to "01 January 2026" And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: @@ -122,6 +126,7 @@ Feature: Working Capital Goodwill Credit Payment Allocation And Admin runs inline COB job for Working Capital Loan by loanId Then Admin closes the Working Capital loan with a full repayment on "15 March 2026" + @TestRailId:C85515 Scenario: Verify Working Capital Goodwill Credit transaction with fee and penalty added with IN_ADVANCE_PENALTY_FEE_PRINCIPAL allocation - UC5 Given Admin sets the business date to "01 January 2026" And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: @@ -150,6 +155,7 @@ Feature: Working Capital Goodwill Credit Payment Allocation And Admin runs inline COB job for Working Capital Loan by loanId Then Admin closes the Working Capital loan with a full repayment on "15 March 2026" + @TestRailId:C85516 Scenario: Verify Working Capital Goodwill Credit transaction that closes loan with fee and penalty added with DUE_PRINCIPAL_FEE_PENALTY allocation - UC6 Given Admin sets the business date to "01 January 2026" And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: @@ -177,6 +183,7 @@ Feature: Working Capital Goodwill Credit Payment Allocation | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | | 12 January 2026 | Goodwill Credit | 9040.0 | 9000.0 | 15.0 | 25.0 | false | + @TestRailId:C85517 Scenario: Verify Working Capital Goodwill Credit transaction that overpays loan with following CBR trn and with fee and penalty added with DUE_PRINCIPAL_FEE_PENALTY allocation - UC7 Given Admin sets the business date to "01 January 2026" And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: @@ -214,6 +221,7 @@ Feature: Working Capital Goodwill Credit Payment Allocation | 12 January 2026 | Goodwill Credit | 9200.0 | 9000.0 | 15.0 | 25.0 | false | | 12 January 2026 | Credit Balance Refund | 160.0 | 160.0 | 0.0 | 0.0 | false | + @TestRailId:C85518 Scenario: Verify Working Capital Goodwill Credit transaction allocation with charges has been reprocessed successfully after additional backdated goodwill credit - UC8 Given Admin sets the business date to "01 January 2026" And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: @@ -255,6 +263,7 @@ Feature: Working Capital Goodwill Credit Payment Allocation And Admin runs inline COB job for Working Capital Loan by loanId Then Admin closes the Working Capital loan with a full repayment on "25 January 2026" + @TestRailId:C85519 Scenario: Verify Working Capital Goodwill Credit and fee charge adjustment transaction allocation is processed successfully - UC9 Given Admin sets the business date to "01 January 2026" And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: @@ -285,6 +294,7 @@ Feature: Working Capital Goodwill Credit Payment Allocation And Admin runs inline COB job for Working Capital Loan by loanId Then Admin closes the Working Capital loan with a full repayment on "20 February 2026" + @TestRailId:C85520 Scenario: Verify Working Capital Goodwill Credit transaction with charge within amortization schedule with DUE_PENALTY_FEE_PRINCIPAL allocation - UC10 Given Admin sets the business date to "01 January 2026" And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: @@ -501,6 +511,7 @@ Feature: Working Capital Goodwill Credit Payment Allocation And Admin runs inline COB job for Working Capital Loan by loanId Then Admin closes the Working Capital loan with a full repayment on "15 March 2026" + @TestRailId:C85521 Scenario: Verify Working Capital Goodwill Credit with fee and penalty charges within amortization schedule with DUE_FEE_PENALTY_PRINCIPAL allocation - UC11 Given Admin sets the business date to "01 January 2026" And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: @@ -724,6 +735,7 @@ Feature: Working Capital Goodwill Credit Payment Allocation And Admin runs inline COB job for Working Capital Loan by loanId Then Admin closes the Working Capital loan with a full repayment on "15 March 2026" + @TestRailId:C85522 Scenario: Verify Working Capital Goodwill Credit uses GOODWILL_CREDIT specific payment allocation rule (DUE_PRINCIPAL_FEE_PENALTY) - UC12 Given Admin sets the business date to "01 January 2026" And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: @@ -754,6 +766,7 @@ Feature: Working Capital Goodwill Credit Payment Allocation And Admin runs inline COB job for Working Capital Loan by loanId Then Admin closes the Working Capital loan with a full repayment on "15 March 2026" + @TestRailId:C85523 Scenario: Verify Working Capital Goodwill Credit (DUE_PRINCIPAL_FEE_PENALTY) and Repayment (DUE_FEE_PENALTY_PRINCIPAL) apply different payment allocation rules on the same loan - UC13 Given Admin sets the business date to "01 January 2026" And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: @@ -786,3 +799,195 @@ Feature: Working Capital Goodwill Credit Payment Allocation When Admin sets the business date to "20 February 2026" And Admin runs inline COB job for Working Capital Loan by loanId Then Admin closes the Working Capital loan with a full repayment on "20 February 2026" + + @TestRailId:C85524 + Scenario: Verify Working Capital Goodwill Credit paid in full follows the GOODWILL_CREDIT specific payment allocation rule - UC14 + Given Admin sets the business date to "01 January 2026" + And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_GOODWILL_CREDIT_ALLOCATION | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 | + When Admin sets the business date to "10 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "12 January 2026" due date and 15.0 transaction amount + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_PENALTY" specified due date charge to working capital loan with "12 January 2026" due date and 25.0 transaction amount + When Admin sets the business date to "12 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Customer makes "GOODWILL_CREDIT" transaction on "12 January 2026" with 9040.0 transaction amount on Working Capital loan + And Working capital loan account has the correct data: + | principal | totalPaidPrincipal | totalPaymentVolume | realizedIncome | unrealizedIncome | overpaymentAmount | + | 9000.0 | 9000.0 | 100000.0 | 0.0 | 0.0 | 0.0 | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 15.0 | 0.0 | 15.0 | 25.0 | 0.0 | 25.0 | + And Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 12 January 2026 | Goodwill Credit | 9040.0 | 9000.0 | 15.0 | 25.0 | false | + + @TestRailId:C85525 + Scenario: Verify Working Capital Goodwill Credit paid in excess follows the GOODWILL_CREDIT specific payment allocation rule with following CBR transaction - UC15 + Given Admin sets the business date to "01 January 2026" + And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_GOODWILL_CREDIT_ALLOCATION | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 | + When Admin sets the business date to "10 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "12 January 2026" due date and 15.0 transaction amount + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_PENALTY" specified due date charge to working capital loan with "12 January 2026" due date and 25.0 transaction amount + When Admin sets the business date to "12 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Customer makes "GOODWILL_CREDIT" transaction on "12 January 2026" with 9200.0 transaction amount on Working Capital loan + And Working capital loan account has the correct data: + | principal | totalPaidPrincipal | totalPaymentVolume | realizedIncome | unrealizedIncome | overpaymentAmount | + | 9000.0 | 9000.0 | 100000.0 | 0.0 | 0.0 | 160.0 | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 15.0 | 0.0 | 15.0 | 25.0 | 0.0 | 25.0 | + And Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 12 January 2026 | Goodwill Credit | 9200.0 | 9000.0 | 15.0 | 25.0 | false | +# --- make CBR transaction to refund overpaid amount --- # + And Customer makes credit balance refund on "12 January 2026" with 160.0 transaction amount on Working Capital loan + And Working capital loan account has the correct data: + | principal | totalPaidPrincipal | totalPaymentVolume | realizedIncome | unrealizedIncome | overpaymentAmount | + | 9000.0 | 9000.0 | 100000.0 | 0.0 | 0.0 | 0.0 | + And Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 12 January 2026 | Goodwill Credit | 9200.0 | 9000.0 | 15.0 | 25.0 | false | + | 12 January 2026 | Credit Balance Refund | 160.0 | 160.0 | 0.0 | 0.0 | false | + + @TestRailId:C85526 + Scenario: Verify Working Capital Goodwill Credit falls back to DEFAULT rule (DUE_PRINCIPAL first) and not to REPAYMENT rule (DUE_FEE first) when no GOODWILL_CREDIT rule is configured - UC16 + Given Admin sets the business date to "01 January 2026" + And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_REPAYMENT_DIFF_DEFAULT | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 | + When Admin sets the business date to "10 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "12 January 2026" due date and 15.0 transaction amount + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_PENALTY" specified due date charge to working capital loan with "12 January 2026" due date and 25.0 transaction amount + When Admin sets the business date to "12 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId +# --- goodwill credit ignores the REPAYMENT (DUE_FEE first) rule and follows DEFAULT (DUE_PRINCIPAL first): all 100 goes to principal --- # + And Customer makes "GOODWILL_CREDIT" transaction on "12 January 2026" with 100.0 transaction amount on Working Capital loan + And Working capital loan account has the correct data: + | principal | totalPaidPrincipal | totalPaymentVolume | realizedIncome | unrealizedIncome | overpaymentAmount | + | 9000.0 | 100.0 | 100000.0 | 0.0 | 0.0 | 0.0 | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 15.0 | 15.0 | 0.0 | 25.0 | 25.0 | 0.0 | +# --- repayment on the same loan still follows the REPAYMENT (DUE_FEE first) rule: charges are settled, no principal --- # + When Admin sets the business date to "13 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Customer makes repayment on "13 January 2026" with 40.0 transaction amount on Working Capital loan + And Working capital loan account has the correct data: + | principal | totalPaidPrincipal | totalPaymentVolume | realizedIncome | unrealizedIncome | overpaymentAmount | + | 9000.0 | 100.0 | 100000.0 | 0.0 | 0.0 | 0.0 | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 15.0 | 0.0 | 15.0 | 25.0 | 0.0 | 25.0 | + And Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 12 January 2026 | Goodwill Credit | 100.0 | 100.0 | 0.0 | 0.0 | false | + | 13 January 2026 | Repayment | 40.0 | 0.0 | 15.0 | 25.0 | false | + When Admin sets the business date to "15 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + Then Admin closes the Working Capital loan with a full repayment on "15 January 2026" + + @TestRailId:C85527 + Scenario: Verify backdated Working Capital Goodwill Credit reprocessing replays each transaction with its own payment allocation rule - UC17 + Given Admin sets the business date to "01 January 2026" + And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_GOODWILL_CREDIT_ALLOCATION | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 | + When Admin sets the business date to "10 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "10 January 2026" due date and 100.0 transaction amount + When Admin sets the business date to "20 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId +# --- repayment follows the REPAYMENT (DUE_FEE first) rule: settles the fee --- # + And Customer makes repayment on "20 January 2026" with 100.0 transaction amount on Working Capital loan + Then Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 20 January 2026 | Repayment | 100.0 | 0.0 | 100.0 | 0.0 | false | +# --- backdated goodwill credit triggers reprocessing: goodwill keeps its own DUE_PRINCIPAL-first rule (principal 100), repayment keeps DUE_FEE-first (fee 100) --- # + And Customer makes "GOODWILL_CREDIT" transaction on "15 January 2026" with 100.0 transaction amount on Working Capital loan + Then Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 15 January 2026 | Goodwill Credit | 100.0 | 100.0 | 0.0 | 0.0 | false | + | 20 January 2026 | Repayment | 100.0 | 0.0 | 100.0 | 0.0 | false | + And Working capital loan account has the correct data: + | principal | totalPaidPrincipal | totalPaymentVolume | realizedIncome | unrealizedIncome | overpaymentAmount | + | 9000.0 | 100.0 | 100000.0 | 0.0 | 0.0 | 0.0 | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 100.0 | 0.0 | 100.0 | 0.0 | 0.0 | 0.0 | + When Admin sets the business date to "25 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + Then Admin closes the Working Capital loan with a full repayment on "25 January 2026" + + @TestRailId:C85528 + Scenario: Verify undo of Working Capital Goodwill Credit re-allocates the remaining transactions with their own payment allocation rules - UC18 + Given Admin sets the business date to "01 January 2026" + And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_GOODWILL_CREDIT_ALLOCATION | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 | + When Admin sets the business date to "10 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "12 January 2026" due date and 15.0 transaction amount + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_PENALTY" specified due date charge to working capital loan with "12 January 2026" due date and 25.0 transaction amount + When Admin sets the business date to "12 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Customer makes "GOODWILL_CREDIT" transaction on "12 January 2026" with 270.0 transaction amount on Working Capital loan + When Admin sets the business date to "15 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Customer makes repayment on "15 January 2026" with 40.0 transaction amount on Working Capital loan + Then Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 12 January 2026 | Goodwill Credit | 270.0 | 270.0 | 0.0 | 0.0 | false | + | 15 January 2026 | Repayment | 40.0 | 0.0 | 15.0 | 25.0 | false | +# --- undo the goodwill credit: the repayment is replayed with its own REPAYMENT (DUE_FEE first) rule and keeps its portions --- # + When Customer undo "1"th "GOODWILL_CREDIT" transaction made on "12 January 2026" on Working Capital loan + Then Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 12 January 2026 | Goodwill Credit | 270.0 | 270.0 | 0.0 | 0.0 | true | + | 15 January 2026 | Repayment | 40.0 | 0.0 | 15.0 | 25.0 | false | + And Working capital loan account has the correct data: + | principal | totalPaidPrincipal | totalPaymentVolume | realizedIncome | unrealizedIncome | overpaymentAmount | + | 9000.0 | 0.0 | 100000.0 | 0.0 | 0.0 | 0.0 | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 15.0 | 0.0 | 15.0 | 25.0 | 0.0 | 25.0 | + Then Admin closes the Working Capital loan with a full repayment on "15 January 2026" + + @TestRailId:C85529 + Scenario: Verify Working Capital Goodwill Credit allocates to in advance fee and penalty buckets of the GOODWILL_CREDIT specific rule - UC19 + Given Admin sets the business date to "01 January 2026" + And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_GOODWILL_CREDIT_ALLOCATION | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 | + When Admin sets the business date to "10 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "20 January 2026" due date and 15.0 transaction amount + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_PENALTY" specified due date charge to working capital loan with "20 January 2026" due date and 25.0 transaction amount + When Admin sets the business date to "15 January 2026" + And Admin runs inline COB job for Working Capital Loan by loanId +# --- charges are due only on 20 Jan: principal is settled first (DUE_PRINCIPAL), then IN_ADVANCE_FEE fully, then IN_ADVANCE_PENALTY partially --- # + And Customer makes "GOODWILL_CREDIT" transaction on "15 January 2026" with 9020.0 transaction amount on Working Capital loan + And Working capital loan account has the correct data: + | principal | totalPaidPrincipal | totalPaymentVolume | realizedIncome | unrealizedIncome | overpaymentAmount | + | 9000.0 | 9000.0 | 100000.0 | 0.0 | 0.0 | 0.0 | + And Working Capital Loan charge balances has the following data: + | Fee Amount | Fee Outstanding | Fee Paid | Penalty Amount | Penalty Outstanding | Penalty Paid | + | 15.0 | 0.0 | 15.0 | 25.0 | 20.0 | 5.0 | + And Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | + | 15 January 2026 | Goodwill Credit | 9020.0 | 9000.0 | 15.0 | 5.0 | false | diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanGoodwillCreditAccountingEntries.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanGoodwillCreditAccountingEntries.feature index 43edd9f35e0..83cbc5543c0 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanGoodwillCreditAccountingEntries.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanGoodwillCreditAccountingEntries.feature @@ -233,3 +233,21 @@ Feature: Working Capital Goodwill Credit Accounting Entries | ASSET | 112601 | Loans Receivable | 9000.0 | | | LIABILITY | 245000 | Other Credit Liability | 1000.0 | | + @TestRailId:C85530 + Scenario: Verify Working Capital loan Goodwill Credit transaction GL entries - UC9: GOODWILL_CREDIT specific rule (DUE_PRINCIPAL first) skips the due fee + When Admin sets the business date to "01 January 2026" + And Admin creates a client with random data + And Admin creates a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPaymentVolume | periodPaymentRate | discount | + | WCLP_GOODWILL_CREDIT_ALLOCATION | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 | + And Admin successfully approves the working capital loan on "01 January 2026" with "9000" amount and expected disbursement date on "01 January 2026" + And Admin successfully disburse the Working Capital loan on "01 January 2026" with "9000" EUR transaction amount + When Admin sets the business date to "10 January 2026" + And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "10 January 2026" due date and 50.0 transaction amount +# --- the goodwill credit follows the GOODWILL_CREDIT rule (DUE_PRINCIPAL first): the due fee is skipped, so no fee income/receivable legs are posted --- # + And Customer makes "GOODWILL_CREDIT" transaction on "10 January 2026" with 100.0 transaction amount on Working Capital loan + Then Working Capital Loan Transactions tab has a "GOODWILL_CREDIT" transaction with date "10 January 2026" which has the following Journal entries: + | Type | Account code | Account name | Debit | Credit | + | EXPENSE | 744003 | Goodwill Expense Account | 100.0 | | + | ASSET | 112601 | Loans Receivable | | 100.0 | + diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanProduct.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanProduct.feature index 1fcd36dc59b..49871ae1c65 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanProduct.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanProduct.feature @@ -123,6 +123,10 @@ Feature: WorkingCapitalLoanProduct Scenario: Verify Working capital Loan Product create with invalid data shall outcome with error - validation check with payment allocation rules - UC11 Then Admin failed to create a new Working Capital Loan Product with invalid value of payment allocation rules + @TestRailId:C85531 + Scenario: Verify Working capital Loan Product create with invalid data shall outcome with error - validation check with payment allocation rules missing DEFAULT transaction type + Then Admin failed to create a new Working Capital Loan Product with payment allocation rules missing DEFAULT transaction type + @TestRailId:C70219 Scenario: Verify Working capital Loan Product update with invalid data shall outcome with error - validation check with number of payment allocation rules - UC12 When Admin creates a new Working Capital Loan Product From 542da12e6011937650bd27f1a13f851da6c836a5 Mon Sep 17 00:00:00 2001 From: Adam Saghy Date: Mon, 13 Jul 2026 13:52:16 +0200 Subject: [PATCH 3/3] FINERACT-2455: Simplify allocation rule logic --- ...ngCapitalLoanAllocationRequestFactory.java | 46 +++++++------------ 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAllocationRequestFactory.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAllocationRequestFactory.java index 90104476971..63447512031 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAllocationRequestFactory.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAllocationRequestFactory.java @@ -28,7 +28,7 @@ import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanBalance; import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanCharge; import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanPaymentAllocationRule; -import org.apache.fineract.portfolio.workingcapitalloanproduct.domain.WorkingCapitalPaymentAllocationType; +import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; /** @@ -40,42 +40,28 @@ @Component public class WorkingCapitalLoanAllocationRequestFactory { - public WorkingCapitalLoanAllocationRequest build(final WorkingCapitalLoan loan, final WorkingCapitalLoanBalance balance, - final List charges, final LocalDate transactionDate, final BigDecimal amount, - final LoanTransactionType transactionType) { + @NonNull + public WorkingCapitalLoanAllocationRequest build(@NonNull final WorkingCapitalLoan loan, + @NonNull final WorkingCapitalLoanBalance balance, @NonNull final List charges, + @NonNull final LocalDate transactionDate, @NonNull final BigDecimal amount, + @NonNull final LoanTransactionType transactionType) { final List chargeBalances = charges.stream().map( charge -> new ChargeBalance(charge.getId(), charge.getAmountOutstanding(), charge.getDueDate(), charge.isPenaltyCharge())) .toList(); - return new WorkingCapitalLoanAllocationRequest(transactionDate, amount, resolveAllocationOrder(loan, transactionType), - balance.getPrincipalOutstanding(), chargeBalances); + return new WorkingCapitalLoanAllocationRequest(transactionDate, amount, + getAllocationRule(loan, transactionType).getAllocationTypes(), balance.getPrincipalOutstanding(), chargeBalances); } - private List resolveAllocationOrder(final WorkingCapitalLoan loan, - final LoanTransactionType transactionType) { - final WorkingCapitalLoanPaymentAllocationRule rule = transactionType == null ? getDefaultAllocationRule(loan) - : getAllocationRule(loan, transactionType); - if (rule != null && rule.getAllocationTypes() != null && !rule.getAllocationTypes().isEmpty()) { - return rule.getAllocationTypes(); - } - // No configured order: keep the legacy principal-only behaviour. - return List.of(WorkingCapitalPaymentAllocationType.DUE_PRINCIPAL); - } - - private WorkingCapitalLoanPaymentAllocationRule getAllocationRule(final WorkingCapitalLoan loan, - final LoanTransactionType transactionType) { - final List rules = loan.getPaymentAllocationRules(); - if (rules == null || rules.isEmpty()) { - return null; - } - return rules.stream().filter(rule -> transactionType.equals(rule.getTransactionType().getLoanTransactionType())).findFirst() + @NonNull + private WorkingCapitalLoanPaymentAllocationRule getAllocationRule(@NonNull final WorkingCapitalLoan loan, + @NonNull final LoanTransactionType transactionType) { + return loan.getPaymentAllocationRules().stream() + .filter(rule -> transactionType.equals(rule.getTransactionType().getLoanTransactionType())).findFirst() .orElseGet(() -> getDefaultAllocationRule(loan)); } - private WorkingCapitalLoanPaymentAllocationRule getDefaultAllocationRule(final WorkingCapitalLoan loan) { - final List rules = loan.getPaymentAllocationRules(); - if (rules == null || rules.isEmpty()) { - return null; - } - return rules.stream().filter(rule -> rule.getTransactionType().isDefault()).findFirst().orElse(null); + @NonNull + private WorkingCapitalLoanPaymentAllocationRule getDefaultAllocationRule(@NonNull final WorkingCapitalLoan loan) { + return loan.getPaymentAllocationRules().stream().filter(rule -> rule.getTransactionType().isDefault()).findFirst().get(); } }