reluxa commented on code in PR #3306:
URL: https://github.com/apache/fineract/pull/3306#discussion_r1263386404


##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanMultipleDisbursementRepaymentScheduleTest.java:
##########
@@ -0,0 +1,232 @@
+/**
+ * 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 static org.junit.jupiter.api.Assertions.assertTrue;
+
+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.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.UUID;
+import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
+import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
+import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
+import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
+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.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.SchedulerJobHelper;
+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.accounting.PeriodicAccrualAccountingHelper;
+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.LoanTestLifecycleExtension;
+import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(LoanTestLifecycleExtension.class)
+public class LoanMultipleDisbursementRepaymentScheduleTest {
+
+    private static final DateTimeFormatter DATE_FORMATTER = new 
DateTimeFormatterBuilder().appendPattern("dd MMMM yyyy").toFormatter();
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+    private LoanTransactionHelper loanTransactionHelper;
+    private ClientHelper clientHelper;
+    private PeriodicAccrualAccountingHelper periodicAccrualAccountingHelper;
+    private AccountHelper accountHelper;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+        this.requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        this.requestSpec.header("Authorization", "Basic " + 
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        this.responseSpec = new 
ResponseSpecBuilder().expectStatusCode(200).build();
+        this.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
+        this.clientHelper = new ClientHelper(this.requestSpec, 
this.responseSpec);
+        this.periodicAccrualAccountingHelper = new 
PeriodicAccrualAccountingHelper(this.requestSpec, this.responseSpec);
+        this.accountHelper = new AccountHelper(this.requestSpec, 
this.responseSpec);
+    }
+
+    @Test
+    public void loanNoDuplicateRepaymentScheduleWithMultipleDisbursementTest() 
{
+        try {
+            final SchedulerJobHelper schedulerJobHelper = new 
SchedulerJobHelper(requestSpec);
+            // Accounts oof periodic accrual
+            final Account assetAccount = 
this.accountHelper.createAssetAccount();
+            final Account incomeAccount = 
this.accountHelper.createIncomeAccount();
+            final Account expenseAccount = 
this.accountHelper.createExpenseAccount();
+            final Account overpaymentAccount = 
this.accountHelper.createLiabilityAccount();
+
+            // Set business date
+            LocalDate currentDate = LocalDate.of(2023, 07, 7);
+
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.TRUE);
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, currentDate);
+
+            // Loan ExternalId
+            String loanExternalIdStr = UUID.randomUUID().toString();
+
+            // Client and Loan account creation
+
+            final Integer clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
+            final GetLoanProductsProductIdResponse 
getLoanProductsProductResponse = createLoanProductMultipleDisbursements(
+                    loanTransactionHelper, assetAccount, incomeAccount, 
expenseAccount, overpaymentAccount);
+            assertNotNull(getLoanProductsProductResponse);
+
+            final Integer loanId = createLoanAccount(clientId, 
getLoanProductsProductResponse.getId().intValue(), loanExternalIdStr);
+
+            // Add Charge fee
+            Integer fee = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                    
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "5.15", false));
+
+            LocalDate targetDate = LocalDate.of(2023, 7, 11);
+            final String feeCharge1AddedDate = 
DATE_FORMATTER.format(targetDate);
+
+            Integer feeLoanChargeId = 
this.loanTransactionHelper.addChargesForLoan(loanId,
+                    
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(fee),
 feeCharge1AddedDate, "5.15"));
+
+            assertNotNull(feeLoanChargeId);
+
+            // run cob
+
+            currentDate = LocalDate.of(2023, 07, 12);
+
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, currentDate);
+
+            final String jobName = "Loan COB";
+            schedulerJobHelper.executeAndAwaitJob(jobName);
+
+            // verify accrual transaction created for charge due date
+            checkAccrualTransaction(targetDate, 0.0f, 5.15f, 0.0f, loanId);
+
+            // make Merchant Issued Refund
+
+            currentDate = LocalDate.of(2023, 07, 21);
+
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, currentDate);
+
+            final PostLoansLoanIdTransactionsResponse merchantIssuedRefund_1 = 
loanTransactionHelper.makeMerchantIssuedRefund((long) loanId,
+                    new PostLoansLoanIdTransactionsRequest().dateFormat("dd 
MMMM yyyy").transactionDate("21 July 2023").locale("en")
+                            .transactionAmount(167.4));
+
+            assertNotNull(merchantIssuedRefund_1);
+
+            // run cob
+            schedulerJobHelper.executeAndAwaitJob(jobName);
+
+            // make another disbursement
+            currentDate = LocalDate.of(2023, 07, 24);
+
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, currentDate);
+
+            loanTransactionHelper.disburseLoanWithTransactionAmount("24 July 
2023", loanId, "18");
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails((long) loanId);
+            assertTrue(loanDetails.getStatus().getActive());
+
+            List<GetLoansLoanIdRepaymentPeriod> periods = 
loanDetails.getRepaymentSchedule().getPeriods();
+
+            assertNotNull(periods);
+
+            // verify only one active period
+            long activePeriods = periods.stream().filter(p -> p.getPeriod() != 
null && p.getPeriod().equals(1)).count();
+
+            assertEquals(1, activePeriods);
+
+        } finally {
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.FALSE);
+        }
+
+    }
+
+    private void checkAccrualTransaction(final LocalDate transactionDate, 
final Float interestPortion, final Float feePortion,
+            final Float penaltyPortion, final Integer loanID) {
+
+        ArrayList<HashMap> transactions = (ArrayList<HashMap>) 
loanTransactionHelper.getLoanTransactions(this.requestSpec,
+                this.responseSpec, loanID);
+        boolean isTransactionFound = false;
+        for (int i = 0; i < transactions.size(); i++) {
+            HashMap transactionType = (HashMap) 
transactions.get(i).get("type");
+            boolean isAccrualTransaction = (Boolean) 
transactionType.get("accrual");
+
+            if (isAccrualTransaction) {
+                ArrayList<Integer> accrualEntryDateAsArray = 
(ArrayList<Integer>) transactions.get(i).get("date");
+                LocalDate accrualEntryDate = 
LocalDate.of(accrualEntryDateAsArray.get(0), accrualEntryDateAsArray.get(1),
+                        accrualEntryDateAsArray.get(2));
+
+                if (transactionDate.isEqual(accrualEntryDate)) {
+                    isTransactionFound = true;
+                    assertEquals(interestPortion, 
Float.valueOf(String.valueOf(transactions.get(i).get("interestPortion"))),
+                            "Mismatch in transaction amounts");
+                    assertEquals(feePortion, 
Float.valueOf(String.valueOf(transactions.get(i).get("feeChargesPortion"))),
+                            "Mismatch in transaction amounts");
+                    assertEquals(penaltyPortion, 
Float.valueOf(String.valueOf(transactions.get(i).get("penaltyChargesPortion"))),
+                            "Mismatch in transaction amounts");
+                    break;
+                }
+            }
+        }
+        assertTrue(isTransactionFound, "No Accrual entries are posted");
+    }
+
+    private GetLoanProductsProductIdResponse 
createLoanProductMultipleDisbursements(final LoanTransactionHelper 
loanTransactionHelper,
+            final Account... accounts) {
+
+        final String loanProductJSON = new 
LoanProductTestBuilder().withPrincipal("1000").withRepaymentTypeAsMonth()
+                
.withRepaymentAfterEvery("1").withNumberOfRepayments("1").withRepaymentTypeAsMonth().withinterestRatePerPeriod("0")
+                
.withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsDecliningBalance()
+                
.withAccountingRulePeriodicAccrual(accounts).withInterestCalculationPeriodTypeAsRepaymentPeriod(true).withDaysInMonth("30")
+                .withDaysInYear("365").withMoratorium("0", 
"0").withMultiDisburse().withDisallowExpectedDisbursements(true).build(null);
+        final Integer loanProductId = 
loanTransactionHelper.getLoanProductId(loanProductJSON);
+        return loanTransactionHelper.getLoanProduct(loanProductId);
+    }
+
+    private Integer createLoanAccount(final Integer clientID, final Integer 
loanProductID, final String externalId) {
+
+        String loanApplicationJSON = new 
LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("30")
+                
.withLoanTermFrequencyAsDays().withNumberOfRepayments("1").withRepaymentEveryAfter("30").withRepaymentFrequencyTypeAsDays()
+                
.withInterestRatePerPeriod("0").withInterestTypeAsFlatBalance().withAmortizationTypeAsEqualPrincipalPayments()
+                
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod().withExpectedDisbursementDate("07
 July 2023")
+                .withSubmittedOnDate("07 July 
2023").withLoanType("individual").withExternalId(externalId)
+                .build(clientID.toString(), loanProductID.toString(), null);
+
+        final Integer loanId = 
loanTransactionHelper.getLoanId(loanApplicationJSON);
+        loanTransactionHelper.approveLoan("07 July 2023", "1000", loanId, 
null);
+        loanTransactionHelper.disburseLoanWithTransactionAmount("07 July 
2023", loanId, "370.55");

Review Comment:
   I would move the disbursement out, or I would rename the method to 
createLoanAccountAndDisburse



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to