This is an automated email from the ASF dual-hosted git repository.

manojvm 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 a97a3d040 FINERACT-1603- Post Interest Job Fix
     new 3827417a6 Merge pull request #2322 from apurbraj/develop
a97a3d040 is described below

commit a97a3d0402aff5cc5c25a3b0c67c078c63b89158
Author: apurbraj <[email protected]>
AuthorDate: Tue May 10 21:18:30 2022 +0530

    FINERACT-1603- Post Interest Job Fix
---
 .../portfolio/savings/data/SavingsAccountData.java |   6 +-
 .../savings/data/SavingsAccountSummaryData.java    |   2 +-
 .../SavingsAccountInterestPostingServiceImpl.java  |   9 +-
 .../SavingsAccountReadPlatformServiceImpl.java     |   3 +-
 .../service/SavingsSchedularInterestPoster.java    |   2 +-
 .../SavingsInterestPostingJobIntegrationTest.java  | 171 +++++++++++++++++++++
 .../common/savings/SavingsAccountHelper.java       |   2 +-
 .../common/savings/SavingsProductHelper.java       |   9 ++
 8 files changed, 194 insertions(+), 10 deletions(-)

diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountData.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountData.java
index dbc354737..83617b084 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountData.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountData.java
@@ -180,7 +180,7 @@ public final class SavingsAccountData implements 
Serializable {
         this.reasonForBlock = null;
         this.timeline = null;
         this.currency = null;
-        this.nominalAnnualInterestRate = nominalAnnualInterestRate;
+        this.nominalAnnualInterestRate = nominalAnnualInterestRate != null ? 
nominalAnnualInterestRate : BigDecimal.ZERO;
         this.interestCompoundingPeriodType = interestCompoundingPeriodType;
         this.interestPostingPeriodType = interestPostingPeriodType;
         this.interestCalculationType = interestCalculationType;
@@ -537,7 +537,7 @@ public final class SavingsAccountData implements 
Serializable {
         this.reasonForBlock = null;
         this.timeline = null;
         this.currency = null;
-        this.nominalAnnualInterestRate = nominalAnnualInterestRate;
+        this.nominalAnnualInterestRate = nominalAnnualInterestRate == null ? 
BigDecimal.ZERO : nominalAnnualInterestRate;
         this.interestCompoundingPeriodType = interestCompoundingPeriodType;
         this.interestPostingPeriodType = interestPostingPeriodType;
         this.interestCalculationType = interestCalculationType;
@@ -959,7 +959,7 @@ public final class SavingsAccountData implements 
Serializable {
         this.reasonForBlock = reasonForBlock;
         this.timeline = timeline;
         this.currency = currency;
-        this.nominalAnnualInterestRate = nominalAnnualInterestRate;
+        this.nominalAnnualInterestRate = nominalAnnualInterestRate == null ? 
BigDecimal.ZERO : nominalAnnualInterestRate;
         this.interestCompoundingPeriodType = interestPeriodType;
         this.interestPostingPeriodType = interestPostingPeriodType;
         this.interestCalculationType = interestCalculationType;
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountSummaryData.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountSummaryData.java
index fbbe5cccb..2cb810b15 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountSummaryData.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountSummaryData.java
@@ -78,7 +78,7 @@ public class SavingsAccountSummaryData implements 
Serializable {
         this.interestNotPosted = interestNotPosted;
         this.lastInterestCalculationDate = lastInterestCalculationDate;
         this.availableBalance = availableBalance;
-        this.interestPostedTillDate = interestPostedTillDate;
+        this.interestPostedTillDate = interestPostedTillDate == null ? 
lastInterestCalculationDate : interestPostedTillDate;
     }
 
     public void setPrevInterestPostedTillDate(LocalDate 
interestPostedTillDate) {
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountInterestPostingServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountInterestPostingServiceImpl.java
index dd660ffad..74a19d350 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountInterestPostingServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountInterestPostingServiceImpl.java
@@ -108,7 +108,7 @@ public class SavingsAccountInterestPostingServiceImpl 
implements SavingsAccountI
                         newPostingTransaction = 
SavingsAccountTransactionData.interestPosting(savingsAccountData,
                                 interestPostingTransactionDate, 
interestEarnedToBePostedForPeriod, interestPostingPeriod.isUserPosting());
                     } else {
-                        newPostingTransaction = 
SavingsAccountTransactionData.interestPosting(savingsAccountData,
+                        newPostingTransaction = 
SavingsAccountTransactionData.overdraftInterest(savingsAccountData,
                                 interestPostingTransactionDate, 
interestEarnedToBePostedForPeriod.negated(),
                                 interestPostingPeriod.isUserPosting());
                     }
@@ -250,7 +250,8 @@ public class SavingsAccountInterestPostingServiceImpl 
implements SavingsAccountI
         final List<PostingPeriod> allPostingPeriods = new ArrayList<>();
 
         Money periodStartingBalance;
-        if (savingsAccountData.getStartInterestCalculationDate() != null) {
+        if (savingsAccountData.getStartInterestCalculationDate() != null
+                && 
!savingsAccountData.getStartInterestCalculationDate().equals(savingsAccountData.getActivationLocalDate()))
 {
             final SavingsAccountTransactionData transaction = 
retrieveLastTransactions(savingsAccountData);
 
             if (transaction == null) {
@@ -361,7 +362,9 @@ public class SavingsAccountInterestPostingServiceImpl 
implements SavingsAccountI
     }
 
     private BigDecimal getEffectiveOverdraftInterestRateAsFraction(MathContext 
mc, final SavingsAccountData savingsAccountData) {
-        return 
savingsAccountData.getNominalAnnualInterestRateOverdraft().divide(BigDecimal.valueOf(100L),
 mc);
+        return savingsAccountData.getNominalAnnualInterestRateOverdraft() != 
null
+                ? 
savingsAccountData.getNominalAnnualInterestRateOverdraft().divide(BigDecimal.valueOf(100L),
 mc)
+                : BigDecimal.ZERO;
     }
 
     @SuppressWarnings("unused")
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java
index f75dc60aa..7e98984e9 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java
@@ -274,7 +274,8 @@ public class SavingsAccountReadPlatformServiceImpl 
implements SavingsAccountRead
                     + "where (CASE WHEN sa.interest_posted_till_date is not 
null THEN tr.transaction_date >= sa.interest_posted_till_date ELSE 
tr.transaction_date >= sa.activatedon_date END) ";
         }
 
-        sql = sql + "and apm.product_type=2 and sa.interest_posted_till_date < 
'" + java.sql.Date.valueOf(currentDate) + "'";
+        sql = sql + " and (sa.interest_posted_till_date is null or 
sa.interest_posted_till_date < '" + java.sql.Date.valueOf(currentDate)
+                + "') ";
         sql = sql + " order by sa.id, tr.transaction_date, tr.created_date, 
tr.id";
 
         List<SavingsAccountData> savingsAccountDataList = 
this.jdbcTemplate.query(sql, this.savingAccountMapperForInterestPosting, // 
NOSONAR
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularInterestPoster.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularInterestPoster.java
index b78a39bfe..ae896230f 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularInterestPoster.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularInterestPoster.java
@@ -359,7 +359,7 @@ public class SavingsSchedularInterestPoster implements 
Callable<Void> {
         query.append("transaction_type_enum, transaction_date, amount, 
balance_end_date_derived,");
         query.append("balance_number_of_days_derived, running_balance_derived, 
cumulative_balance_derived,");
         query.append("created_date, appuser_id, is_manual, 
is_loan_disbursement, ref_no) VALUES ");
-        query.append("(?, ?, 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?)");
+        query.append("(?, ?, false, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, false, ?)");
         return query.toString();
 
     }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsInterestPostingJobIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsInterestPostingJobIntegrationTest.java
new file mode 100644
index 000000000..d92213c06
--- /dev/null
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsInterestPostingJobIntegrationTest.java
@@ -0,0 +1,171 @@
+/**
+ * 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 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.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+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.SchedulerJobHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import 
org.apache.fineract.integrationtests.common.savings.SavingsAccountHelper;
+import 
org.apache.fineract.integrationtests.common.savings.SavingsProductHelper;
+import 
org.apache.fineract.integrationtests.common.savings.SavingsStatusChecker;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SavingsInterestPostingJobIntegrationTest {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(SavingsInterestPostingJobIntegrationTest.class);
+    public static final String ACCOUNT_TYPE_INDIVIDUAL = "INDIVIDUAL";
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+    private SavingsProductHelper savingsProductHelper;
+    private SavingsAccountHelper savingsAccountHelper;
+    private SchedulerJobHelper scheduleJobHelper;
+
+    @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.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, 
this.responseSpec);
+        this.savingsProductHelper = new SavingsProductHelper();
+        this.scheduleJobHelper = new SchedulerJobHelper(requestSpec);
+    }
+
+    @Test
+    public void testSavingsDailyInterestPostingJob() {
+        // client activation, savings activation and 1st transaction date
+        final String startDate = "10 April 2022";
+        final String jobName = "Post Interest For Savings";
+        final Integer clientID = ClientHelper.createClient(this.requestSpec, 
this.responseSpec, startDate);
+        Assertions.assertNotNull(clientID);
+
+        final Integer savingsId = createSavingsAccountDailyPosting(clientID, 
startDate);
+
+        this.savingsAccountHelper.depositToSavingsAccount(savingsId, "10000", 
startDate, CommonConstants.RESPONSE_RESOURCE_ID);
+
+        /***
+         * Runs Post interest posting job and verify the new account created 
with accounting configuration set as none
+         * is picked up by job
+         */
+        this.scheduleJobHelper.executeAndAwaitJob(jobName);
+        Object transactionObj = 
this.savingsAccountHelper.getSavingsDetails(savingsId, "transactions");
+        ArrayList<HashMap<String, Object>> transactions = 
(ArrayList<HashMap<String, Object>>) transactionObj;
+        HashMap<String, Object> interestPostingTransaction = 
transactions.get(transactions.size() - 3);
+        for (Map.Entry<String, Object> entry : 
interestPostingTransaction.entrySet()) {
+            LOG.info("{} - {}", entry.getKey(), entry.getValue().toString());
+        }
+        assertEquals("2.7405", 
interestPostingTransaction.get("amount").toString(), "Equality check for 
interest posted amount");
+        assertEquals("[2022, 4, 12]", 
interestPostingTransaction.get("date").toString(), "Date check for Interest 
Posting transaction");
+    }
+
+    @Test
+    public void testSavingsDailyOverdraftInterestPostingJob() {
+        // client activation, savings activation and 1st transaction date
+        final String startDate = "10 April 2022";
+        final String jobName = "Post Interest For Savings";
+        final Integer clientID = ClientHelper.createClient(this.requestSpec, 
this.responseSpec, startDate);
+        Assertions.assertNotNull(clientID);
+
+        final Integer savingsId = 
createSavingsAccountDailyPostingOverdraft(clientID, startDate);
+
+        this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, 
"10000", startDate, CommonConstants.RESPONSE_RESOURCE_ID);
+
+        /***
+         * Runs Post interest posting job and verify the new account created 
with Overdraft is posting negative interest
+         */
+        this.scheduleJobHelper.executeAndAwaitJob(jobName);
+        Object transactionObj = 
this.savingsAccountHelper.getSavingsDetails(savingsId, "transactions");
+        ArrayList<HashMap<String, Object>> transactions = 
(ArrayList<HashMap<String, Object>>) transactionObj;
+        HashMap<String, Object> interestPostingTransaction = 
transactions.get(transactions.size() - 2);
+        for (Map.Entry<String, Object> entry : 
interestPostingTransaction.entrySet()) {
+            LOG.info("{} - {}", entry.getKey(), entry.getValue().toString());
+        }
+        assertEquals("2.7397", 
interestPostingTransaction.get("amount").toString(), "Equality check for 
overdatft interest posted amount");
+        assertEquals("[2022, 4, 11]", 
interestPostingTransaction.get("date").toString(),
+                "Date check for overdraft Interest Posting transaction");
+
+    }
+
+    private Integer createSavingsAccountDailyPosting(final Integer clientID, 
final String startDate) {
+        final Integer savingsProductID = createSavingsProductDailyPosting();
+        Assertions.assertNotNull(savingsProductID);
+        final Integer savingsId = 
this.savingsAccountHelper.applyForSavingsApplicationOnDate(clientID, 
savingsProductID,
+                ACCOUNT_TYPE_INDIVIDUAL, startDate);
+        Assertions.assertNotNull(savingsId);
+        HashMap savingsStatusHashMap = 
this.savingsAccountHelper.approveSavingsOnDate(savingsId, startDate);
+        SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
+        savingsStatusHashMap = 
this.savingsAccountHelper.activateSavingsAccount(savingsId, startDate);
+        SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
+        return savingsId;
+    }
+
+    private Integer createSavingsAccountDailyPostingOverdraft(final Integer 
clientID, final String startDate) {
+        final Integer savingsProductID = 
createSavingsProductDailyPostingOverdraft();
+        Assertions.assertNotNull(savingsProductID);
+        final Integer savingsId = 
this.savingsAccountHelper.applyForSavingsApplicationOnDate(clientID, 
savingsProductID,
+                ACCOUNT_TYPE_INDIVIDUAL, startDate);
+        Assertions.assertNotNull(savingsId);
+        HashMap savingsStatusHashMap = 
this.savingsAccountHelper.approveSavingsOnDate(savingsId, startDate);
+        SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
+        savingsStatusHashMap = 
this.savingsAccountHelper.activateSavingsAccount(savingsId, startDate);
+        SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
+        return savingsId;
+    }
+
+    private Integer createSavingsProductDailyPosting() {
+        final String savingsProductJSON = 
this.savingsProductHelper.withInterestCompoundingPeriodTypeAsDaily()
+                
.withInterestPostingPeriodTypeAsDaily().withInterestCalculationPeriodTypeAsDailyBalance().build();
+        return SavingsProductHelper.createSavingsProduct(savingsProductJSON, 
requestSpec, responseSpec);
+    }
+
+    private Integer createSavingsProductDailyPostingOverdraft() {
+        final String overDraftLimit = "10000.0";
+        final String nominalAnnualInterestRateOverdraft = "10";
+        final String savingsProductJSON = 
this.savingsProductHelper.withInterestCompoundingPeriodTypeAsDaily()
+                
.withInterestPostingPeriodTypeAsDaily().withInterestCalculationPeriodTypeAsDailyBalance()
+                .withOverDraftRate(overDraftLimit, 
nominalAnnualInterestRateOverdraft).build();
+        return SavingsProductHelper.createSavingsProduct(savingsProductJSON, 
requestSpec, responseSpec);
+    }
+
+    // Reset configuration fields
+    @AfterEach
+    public void tearDown() {
+        
GlobalConfigurationHelper.resetAllDefaultGlobalConfigurations(this.requestSpec, 
this.responseSpec);
+        
GlobalConfigurationHelper.verifyAllDefaultGlobalConfigurations(this.requestSpec,
 this.responseSpec);
+    }
+
+}
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsAccountHelper.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsAccountHelper.java
index 6ec624203..90127462e 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsAccountHelper.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsAccountHelper.java
@@ -644,7 +644,7 @@ public class SavingsAccountHelper {
     }
 
     public Object getSavingsDetails(final Integer savingsID, final String 
returnAttribute) {
-        final String URL = SAVINGS_ACCOUNT_URL + "/" + savingsID + "?" + 
Utils.TENANT_IDENTIFIER;
+        final String URL = SAVINGS_ACCOUNT_URL + "/" + savingsID + 
"?associations=all&" + Utils.TENANT_IDENTIFIER;
         final Object response = Utils.performServerGet(requestSpec, 
responseSpec, URL, returnAttribute);
         return response;
     }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsProductHelper.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsProductHelper.java
index 4b1b66b84..6ede76cbd 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsProductHelper.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsProductHelper.java
@@ -96,6 +96,7 @@ public class SavingsProductHelper {
     private String daysToEscheat = null;
     private Boolean withgsimID = null;
     private Integer gsimID = null;
+    private String nominalAnnualInterestRateOverdraft = null;
 
     public String build() {
         final HashMap<String, String> map = new HashMap<>();
@@ -134,6 +135,7 @@ public class SavingsProductHelper {
         map.put("lienAllowed", this.lienAllowed);
         map.put("maxAllowedLienLimit", this.maxAllowedLienLimit);
         map.put("withHoldTax", this.withHoldTax.toString());
+        map.put("nominalAnnualInterestRateOverdraft", 
this.nominalAnnualInterestRateOverdraft);
 
         if (withHoldTax) {
             map.put("taxGroupId", taxGroupId);
@@ -242,6 +244,13 @@ public class SavingsProductHelper {
         return this;
     }
 
+    public SavingsProductHelper withOverDraftRate(final String overdraftLimit, 
String nominalAnnualInterestRateOverdraft) {
+        this.allowOverdraft = "true";
+        this.overdraftLimit = overdraftLimit;
+        this.nominalAnnualInterestRateOverdraft = 
nominalAnnualInterestRateOverdraft;
+        return this;
+    }
+
     public SavingsProductHelper withWithHoldTax(final String taxGroupId) {
         if (taxGroupId != null) {
             this.withHoldTax = true;

Reply via email to