diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java index 9fbd578f1..8bcee6782 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java @@ -794,19 +794,8 @@ public final class LoanProductDataValidator { } } - String loanScheduleType = LoanScheduleType.CUMULATIVE.name(); if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.LOAN_SCHEDULE_TYPE, element)) { - loanScheduleType = this.fromApiJsonHelper.extractStringNamed(LoanProductConstants.LOAN_SCHEDULE_TYPE, element); - baseDataValidator.reset().parameter(LoanProductConstants.LOAN_SCHEDULE_TYPE).value(loanScheduleType) - .isOneOfEnumValues(LoanScheduleType.class); - } - - if (!LoanScheduleType.PROGRESSIVE.equals(LoanScheduleType.valueOf(loanScheduleType)) - && AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY - .equals(transactionProcessingStrategyCode)) { - baseDataValidator.reset().parameter(LoanProductConstants.LOAN_SCHEDULE_PROCESSING_TYPE).failWithCode( - "supported.only.for.progressive.loan.schedule.type", - "Progressive repayment schedule processing is only available with `Advanced payment allocation` strategy"); + validateLoanScheduleType(transactionProcessingStrategyCode, baseDataValidator, element); } String loanScheduleProcessingType = LoanScheduleProcessingType.HORIZONTAL.name(); @@ -1786,19 +1775,8 @@ public final class LoanProductDataValidator { } } - String loanScheduleType = loanProduct.getLoanProductRelatedDetail().getLoanScheduleType().name(); if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.LOAN_SCHEDULE_TYPE, element)) { - loanScheduleType = this.fromApiJsonHelper.extractStringNamed(LoanProductConstants.LOAN_SCHEDULE_TYPE, element); - baseDataValidator.reset().parameter(LoanProductConstants.LOAN_SCHEDULE_TYPE).value(loanScheduleType) - .isOneOfEnumValues(LoanScheduleType.class); - - if (!LoanScheduleType.PROGRESSIVE.equals(LoanScheduleType.valueOf(loanScheduleType)) - && AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY - .equals(transactionProcessingStrategyCode)) { - baseDataValidator.reset().parameter(LoanProductConstants.LOAN_SCHEDULE_PROCESSING_TYPE).failWithCode( - "supported.only.for.progressive.loan.schedule.type", - "Progressive repayment schedule processing is only available with `Advanced payment allocation` strategy"); - } + validateLoanScheduleType(transactionProcessingStrategyCode, baseDataValidator, element); } String loanScheduleProcessingType = loanProduct.getLoanProductRelatedDetail().getLoanScheduleProcessingType().name(); @@ -2463,4 +2441,28 @@ public final class LoanProductDataValidator { } } } + + private void validateLoanScheduleType(final String transactionProcessingStrategyCode, final DataValidatorBuilder baseDataValidator, + final JsonElement element) { + final String loanScheduleType = this.fromApiJsonHelper.extractStringNamed(LoanProductConstants.LOAN_SCHEDULE_TYPE, element); + baseDataValidator.reset().parameter(LoanProductConstants.LOAN_SCHEDULE_TYPE).value(loanScheduleType) + .isOneOfEnumValues(LoanScheduleType.class); + + if (!LoanScheduleType.PROGRESSIVE.equals(LoanScheduleType.valueOf(loanScheduleType)) + && AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY + .equals(transactionProcessingStrategyCode)) { + baseDataValidator.reset().parameter(LoanProductConstants.LOAN_SCHEDULE_PROCESSING_TYPE).failWithCode( + "supported.only.for.progressive.loan.schedule.type", + "Progressive repayment schedule processing is only available with `Advanced payment allocation` strategy"); + } + + if (LoanScheduleType.PROGRESSIVE.equals(LoanScheduleType.valueOf(loanScheduleType)) + && !AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY + .equals(transactionProcessingStrategyCode)) { + baseDataValidator.reset().parameter(LoanProductConstants.LOAN_SCHEDULE_TYPE).failWithCode( + "supported.only.with.advanced.payment.allocation.strategy", + loanScheduleType + " loan schedule type is not available with " + transactionProcessingStrategyCode + " strategy"); + } + } + } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java index c76f54b10..6f49446c0 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java @@ -4069,6 +4069,43 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan }); } + // UC137: Loan Progressive processing type only with Advanced payment allocation + // ADVANCED_PAYMENT_ALLOCATION_STRATEGY + // 1. Create a Loan product, Loan Progressive type and other transaction processing strategy code + // different than advanced-payment-allocation-strategy + // - > Expect validation error + @Test + public void uc137() { + runAt("22 February 2023", () -> { + + final Account assetAccount = accountHelper.createAssetAccount(); + final Account incomeAccount = accountHelper.createIncomeAccount(); + final Account expenseAccount = accountHelper.createExpenseAccount(); + final Account overpaymentAccount = accountHelper.createLiabilityAccount(); + AdvancedPaymentData defaultPaymentAllocation = createDefaultPaymentAllocationWithMixedGrouping(); + + // First scenario (Legacy): Loan product no Advanced Payment Allocation and Cumulative Loan Schedule type + PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct( + createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct().loanScheduleType(LoanScheduleType.CUMULATIVE.toString())); + assertNotNull(loanProductResponse.getResourceId()); + + // Second scenario: Loan product with Advanced Payment Allocation and Progressive Loan Schedule type + loanProductResponse = loanProductHelper + .createLoanProduct(createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation() + .loanScheduleType(LoanScheduleType.PROGRESSIVE.toString())); + assertNotNull(loanProductResponse.getResourceId()); + + // Third scenario: Loan product no Advanced Payment Allocation and Progressive Loan Schedule type, + // validation expected + CallFailedRuntimeException exception = assertThrows(CallFailedRuntimeException.class, + () -> loanProductHelper.createLoanProduct(createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct() + .loanScheduleType(LoanScheduleType.PROGRESSIVE.toString()) // + .loanScheduleProcessingType(LoanScheduleProcessingType.HORIZONTAL.toString()))); + assertEquals(400, exception.getResponse().code()); + assertTrue(exception.getMessage().contains("supported.only.with.advanced.payment.allocation.strategy")); + }); + } + private void createLoanForRoundingMethodValidation(boolean isHalfDown) { LOG.info("------------------------------ROUNDING HALF DOWN {} ---------------------------------------", isHalfDown); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductWithCreditAllocationsIntegrationTests.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductWithCreditAllocationsIntegrationTests.java index aca226fb7..1c7182d85 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductWithCreditAllocationsIntegrationTests.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductWithCreditAllocationsIntegrationTests.java @@ -182,7 +182,7 @@ public class LoanProductWithCreditAllocationsIntegrationTests { public void testCreditAllocationIsNotAllowedWhenPaymentStrategyIsNotAdvancedPaymentStrategy() { // given String loanProductJSON = baseLoanProduct().withRepaymentStrategy("mifos-standard-strategy") - .addCreditAllocations(createChargebackAllocation()).build(); + .withLoanScheduleType(LoanScheduleType.CUMULATIVE).addCreditAllocations(createChargebackAllocation()).build(); ResponseSpecification errorResponse = new ResponseSpecBuilder().expectStatusCode(400).build(); LoanTransactionHelper validationErrorHelper = new LoanTransactionHelper(REQUEST_SPEC, errorResponse);