This is an automated email from the ASF dual-hosted git repository.
adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push:
new 31a7e3644 FINERACT-1968: Adding Installment percentage fee Adv Payment
allocation
31a7e3644 is described below
commit 31a7e3644d80374fc9fc373bc7892476f8e5ab2f
Author: Ruchi Dhamankar <[email protected]>
AuthorDate: Thu Dec 7 18:50:14 2023 +0530
FINERACT-1968: Adding Installment percentage fee Adv Payment allocation
---
.../LoanChargeWritePlatformServiceImpl.java | 13 ++
...rHandlingWithAdvancedPaymentAllocationTest.java | 180 +++++++++++++++++++++
.../common/loans/LoanTransactionHelper.java | 6 +
3 files changed, 199 insertions(+)
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
index 5769ca3f4..5e96c43d9 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
@@ -72,6 +72,7 @@ import
org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatfo
import org.apache.fineract.portfolio.accountdetails.domain.AccountType;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
+import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
import
org.apache.fineract.portfolio.charge.exception.ChargeCannotBeAppliedToException;
import
org.apache.fineract.portfolio.charge.exception.ChargeCannotBeUpdatedException;
import
org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeAddedException;
@@ -183,6 +184,18 @@ public class LoanChargeWritePlatformServiceImpl implements
LoanChargeWritePlatfo
final Long chargeDefinitionId =
command.longValueOfParameterNamed("chargeId");
final Charge chargeDefinition =
this.chargeRepository.findOneWithNotFoundDetection(chargeDefinitionId);
+ /*
+ * TODO: remove this check once handling for Installment fee charges
is implemented for Advanced Payment
+ * strategy
+ */
+ if
(ChargeTimeType.fromInt(chargeDefinition.getChargeTimeType()).isInstalmentFee()
+ &&
AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY
+ .equals(loan.transactionProcessingStrategy())) {
+ final String errorMessageInstallmentChargeNotSupported = "Charge
with identifier " + chargeDefinition.getId()
+ + " cannot be applied: Installment fee charges are not
supported for Advanced payment allocation strategy";
+ throw new ChargeCannotBeAppliedToException("loan",
errorMessageInstallmentChargeNotSupported, chargeDefinition.getId());
+ }
+
if (loan.isDisbursed() && chargeDefinition.isDisbursementCharge()) {
// validates whether any pending disbursements are available to
// apply this charge
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeTypeInstallmentFeeErrorHandlingWithAdvancedPaymentAllocationTest.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeTypeInstallmentFeeErrorHandlingWithAdvancedPaymentAllocationTest.java
new file mode 100644
index 000000000..af8a09846
--- /dev/null
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeTypeInstallmentFeeErrorHandlingWithAdvancedPaymentAllocationTest.java
@@ -0,0 +1,180 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.time.LocalDate;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.fineract.client.models.AdvancedPaymentData;
+import org.apache.fineract.client.models.PaymentAllocationOrder;
+import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
+import org.apache.fineract.integrationtests.common.BusinessDateHelper;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.CommonConstants;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.accounting.Account;
+import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
+import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
+import
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
+import
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
+import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
+import
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
+import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+public class
LoanChargeTypeInstallmentFeeErrorHandlingWithAdvancedPaymentAllocationTest {
+
+ private static LoanTransactionHelper LOAN_TRANSACTION_HELPER;
+ private static ResponseSpecification RESPONSE_SPEC;
+ private static RequestSpecification REQUEST_SPEC;
+ private static ClientHelper CLIENT_HELPER;
+ private static AccountHelper ACCOUNT_HELPER;
+
+ @BeforeAll
+ public static void setupTests() {
+ Utils.initializeRESTAssured();
+ REQUEST_SPEC = new
RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ REQUEST_SPEC.header("Authorization", "Basic " +
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ RESPONSE_SPEC = new
ResponseSpecBuilder().expectStatusCode(200).build();
+ LOAN_TRANSACTION_HELPER = new LoanTransactionHelper(REQUEST_SPEC,
RESPONSE_SPEC);
+ CLIENT_HELPER = new ClientHelper(REQUEST_SPEC, RESPONSE_SPEC);
+ ACCOUNT_HELPER = new AccountHelper(REQUEST_SPEC, RESPONSE_SPEC);
+ }
+
+ /*
+ * TODO: To be disabled when Installment Fee Charges are handled for
Advanced Payment Allocation
+ */
+ @Test
+ public void
addingLoanChargeTypeInstallmentFeeForAdvancedPaymentAllocationGivesErrorTest() {
+ try {
+ // Set business date
+ LocalDate businessDate = LocalDate.of(2023, 3, 15);
+
+
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC,
RESPONSE_SPEC, Boolean.TRUE);
+ BusinessDateHelper.updateBusinessDate(REQUEST_SPEC, RESPONSE_SPEC,
BusinessDateType.BUSINESS_DATE, businessDate);
+
+ // Accounts oof periodic accrual
+ final Account assetAccount = ACCOUNT_HELPER.createAssetAccount();
+ final Account incomeAccount = ACCOUNT_HELPER.createIncomeAccount();
+ final Account expenseAccount =
ACCOUNT_HELPER.createExpenseAccount();
+ final Account overpaymentAccount =
ACCOUNT_HELPER.createLiabilityAccount();
+
+ final ResponseSpecification errorResponse = new
ResponseSpecBuilder().expectStatusCode(403).build();
+ final LoanTransactionHelper validationErrorHelper = new
LoanTransactionHelper(REQUEST_SPEC, errorResponse);
+
+ // Loan ExternalId
+ String loanExternalIdStr = UUID.randomUUID().toString();
+
+ final Integer clientId =
CLIENT_HELPER.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
+
+ final Integer loanProductId = createLoanProduct(assetAccount,
incomeAccount, expenseAccount, overpaymentAccount);
+
+ final Integer loanId = createLoanAccount(clientId, loanProductId,
loanExternalIdStr);
+
+ // disburse principal amount
+ LOAN_TRANSACTION_HELPER.disburseLoanWithTransactionAmount("15
February 2023", loanId, "1000");
+
+ // add loan charge
+ // apply Installment fee
+ Integer installmentFeeCharge =
ChargesHelper.createCharges(REQUEST_SPEC, RESPONSE_SPEC,
+
ChargesHelper.getLoanInstallmentJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
"50", false));
+
+ List<HashMap<String, Object>> loanChargeErrorData =
(List<HashMap<String, Object>>) validationErrorHelper
+ .addChargesForLoanWithError(loanId,
+
LoanTransactionHelper.getInstallmentChargesForLoanAsJSON(String.valueOf(installmentFeeCharge),
"50"),
+ CommonConstants.RESPONSE_ERROR);
+ assertNotNull(loanChargeErrorData);
+
+ assertEquals(
+ "Charge with identifier %d cannot be applied: Installment
fee charges are not supported for Advanced payment allocation strategy"
+ .formatted(installmentFeeCharge),
+ loanChargeErrorData.get(0).get("defaultUserMessage"));
+ assertEquals("error.msg.charge.cannot.be.applied.toloan",
+
loanChargeErrorData.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
+
+ } finally {
+
GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC,
RESPONSE_SPEC, Boolean.FALSE);
+ }
+ }
+
+ private Integer createLoanProduct(final Account... accounts) {
+ String futureInstallmentAllocationRule = "NEXT_INSTALLMENT";
+ AdvancedPaymentData defaultAllocation =
createDefaultPaymentAllocation(futureInstallmentAllocationRule);
+ String loanProductCreateJSON = new
LoanProductTestBuilder().withPrincipal("15,000.00").withNumberOfRepayments("4")
+
.withRepaymentAfterEvery("1").withRepaymentTypeAsMonth().withinterestRatePerPeriod("0")
+
.withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualInstallments().withInterestTypeAsDecliningBalance()
+
.withAccountingRulePeriodicAccrual(accounts).withInterestCalculationPeriodTypeAsRepaymentPeriod(true)
+
.addAdvancedPaymentAllocation(defaultAllocation).withLoanScheduleType(LoanScheduleType.PROGRESSIVE).withMultiDisburse()
+ .withDisallowExpectedDisbursements(true).build();
+ return LOAN_TRANSACTION_HELPER.getLoanProductId(loanProductCreateJSON);
+
+ }
+
+ private AdvancedPaymentData createDefaultPaymentAllocation(String
futureInstallmentAllocationRule) {
+ AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
+ advancedPaymentData.setTransactionType("DEFAULT");
+
advancedPaymentData.setFutureInstallmentAllocationRule(futureInstallmentAllocationRule);
+
+ List<PaymentAllocationOrder> paymentAllocationOrders =
getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
+ PaymentAllocationType.PAST_DUE_FEE,
PaymentAllocationType.PAST_DUE_PRINCIPAL,
PaymentAllocationType.PAST_DUE_INTEREST,
+ PaymentAllocationType.DUE_PENALTY,
PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
+ PaymentAllocationType.DUE_INTEREST,
PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
+ PaymentAllocationType.IN_ADVANCE_PRINCIPAL,
PaymentAllocationType.IN_ADVANCE_INTEREST);
+
+ advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
+ return advancedPaymentData;
+ }
+
+ private List<PaymentAllocationOrder>
getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
+ AtomicInteger integer = new AtomicInteger(1);
+ return Arrays.stream(paymentAllocationTypes).map(pat -> {
+ PaymentAllocationOrder paymentAllocationOrder = new
PaymentAllocationOrder();
+ paymentAllocationOrder.setPaymentAllocationRule(pat.name());
+ paymentAllocationOrder.setOrder(integer.getAndIncrement());
+ return paymentAllocationOrder;
+ }).toList();
+ }
+
+ private Integer createLoanAccount(final Integer clientID, final Integer
loanProductID, final String externalId) {
+
+ String loanApplicationJSON = new
LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("60")
+
.withLoanTermFrequencyAsDays().withNumberOfRepayments("4").withRepaymentEveryAfter("15").withRepaymentFrequencyTypeAsDays()
+
.withInterestRatePerPeriod("0").withInterestTypeAsFlatBalance().withAmortizationTypeAsEqualPrincipalPayments()
+
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod().withExpectedDisbursementDate("15
February 2023")
+ .withSubmittedOnDate("15 February
2023").withLoanType("individual").withExternalId(externalId)
+
.withRepaymentStrategy("advanced-payment-allocation-strategy").build(clientID.toString(),
loanProductID.toString(), null);
+
+ final Integer loanId =
LOAN_TRANSACTION_HELPER.getLoanId(loanApplicationJSON);
+ LOAN_TRANSACTION_HELPER.approveLoan("15 February 2023", "1000",
loanId, null);
+ return loanId;
+ }
+}
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
index 4794baf70..ba30cc35b 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
@@ -1965,4 +1965,10 @@ public class LoanTransactionHelper extends
IntegrationTest {
final PostLoansLoanIdTransactionsRequest request) {
return
ok(fineract().loanTransactions.executeLoanTransaction1(loanExternalId, request,
"writeoff"));
}
+
+ public Object addChargesForLoanWithError(final Integer loanId, final
String request, final String jsonAttributeToGetBack) {
+ log.info("--------------------------------- ADD CHARGES FOR LOAN
--------------------------------");
+ final String ADD_CHARGES_URL = LOAN_ACCOUNT_URL + "/" + loanId +
"/charges?" + Utils.TENANT_IDENTIFIER;
+ return Utils.performServerPost(requestSpec, responseSpec,
ADD_CHARGES_URL, request, jsonAttributeToGetBack);
+ }
}