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..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 @@ -36,7 +36,9 @@ 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, // + 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 aacdfe57c2a..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 @@ -345,6 +345,8 @@ 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 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 bcad8aff284..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 @@ -225,6 +225,48 @@ 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)) + .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 new file mode 100644 index 00000000000..8f22cca33d2 --- /dev/null +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalGoodwillCreditPaymentAllocation.feature @@ -0,0 +1,993 @@ +@WorkingCapital +@WorkingCapitalGoodwillCreditPaymentAllocationFeature +Feature: Working Capital Goodwill Credit Payment Allocation + + @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 | + | 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" + + @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: + | 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 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 | 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 | 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 | 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: + | 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" + + @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: + | 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" + + @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: + | 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" + + @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: + | 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 | + + @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: + | 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 | + + @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: + | 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" + + @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: + | 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" + + @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: + | 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" + + @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: + | 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" + + @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: + | 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" + + @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: + | 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" + + @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 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..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 @@ -21,50 +21,47 @@ 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; 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; /** * 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) { + @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), - balance.getPrincipalOutstanding(), chargeBalances); + return new WorkingCapitalLoanAllocationRequest(transactionDate, amount, + getAllocationRule(loan, transactionType).getAllocationTypes(), 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(); - } - } - // No configured order: keep the legacy principal-only behaviour. - return List.of(WorkingCapitalPaymentAllocationType.DUE_PRINCIPAL); + @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 findRule(final List rules, - final PaymentAllocationTransactionType transactionType) { - return rules.stream().filter(rule -> transactionType.equals(rule.getTransactionType())).findFirst().orElse(null); + @NonNull + private WorkingCapitalLoanPaymentAllocationRule getDefaultAllocationRule(@NonNull final WorkingCapitalLoan loan) { + return loan.getPaymentAllocationRules().stream().filter(rule -> rule.getTransactionType().isDefault()).findFirst().get(); } } 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