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 d782e65fc2 FINERACT-2376: Unable to re-invest maturity amount or
principal for fixed deposit at closing
d782e65fc2 is described below
commit d782e65fc2c2217c295f7da60cb3fddc5acd9642
Author: sayhaed <[email protected]>
AuthorDate: Fri Sep 12 19:07:59 2025 -0600
FINERACT-2376: Unable to re-invest maturity amount or principal for fixed
deposit at closing
---
.../domain/DepositAccountDomainServiceJpa.java | 5 +-
.../savings/domain/FixedDepositAccount.java | 3 +
.../savings/domain/RecurringDepositAccount.java | 4 +-
.../SavingsAccrualWritePlatformServiceImpl.java | 5 +-
.../portfolio/savings/domain/SavingsAccount.java | 2 +-
.../service/SavingsSchedularInterestPoster.java | 5 +-
.../integrationtests/FixedDepositTest.java | 166 +++++++++++++++++++++
.../SavingsInterestPostingTest.java | 86 +++++++++++
.../fixeddeposit/FixedDepositAccountHelper.java | 11 ++
.../fixeddeposit/FixedDepositProductHelper.java | 44 ++++++
10 files changed, 324 insertions(+), 7 deletions(-)
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountDomainServiceJpa.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountDomainServiceJpa.java
index 7fe875bedd..961d9a7480 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountDomainServiceJpa.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountDomainServiceJpa.java
@@ -212,6 +212,7 @@ public class DepositAccountDomainServiceJpa implements
DepositAccountDomainServi
final LocalDate closedDate =
command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName);
Long savingsTransactionId = null;
account.postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
+ account.setClosedOnDate(closedDate);
final Integer onAccountClosureId =
command.integerValueOfParameterNamed(onAccountClosureIdParamName);
final DepositAccountOnClosureType onClosureType =
DepositAccountOnClosureType.fromInt(onAccountClosureId);
if (onClosureType.isReinvest()) {
@@ -272,6 +273,7 @@ public class DepositAccountDomainServiceJpa implements
DepositAccountDomainServi
final MathContext mc = MathContext.DECIMAL64;
Long savingsTransactionId = null;
account.postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
+ account.setClosedOnDate(closedDate);
final DepositAccountOnClosureType onClosureType =
DepositAccountOnClosureType.fromInt(onAccountClosureId);
if (onClosureType.isReinvest()) {
BigDecimal reInvestAmount;
@@ -367,10 +369,11 @@ public class DepositAccountDomainServiceJpa implements
DepositAccountDomainServi
this.calendarInstanceRepository.save(calendarInstance);
final Calendar calendar = calendarInstance.getCalendar();
final PeriodFrequencyType frequencyType =
CalendarFrequencyType.from(CalendarUtils.getFrequency(calendar.getRecurrence()));
+ final Long relaxingDaysConfigForPivotDate =
this.configurationDomainService.retrieveRelaxingDaysConfigForPivotDate();
Integer frequency =
CalendarUtils.getInterval(calendar.getRecurrence());
frequency = frequency == -1 ? 1 : frequency;
reinvestedDeposit.generateSchedule(frequencyType, frequency,
calendar);
- reinvestedDeposit.processAccountUponActivation(fmt, postReversals);
+ reinvestedDeposit.processAccountUponActivation(fmt, postReversals,
relaxingDaysConfigForPivotDate);
reinvestedDeposit.updateMaturityDateAndAmount(mc,
isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth);
this.savingsAccountRepository.save(reinvestedDeposit);
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositAccount.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositAccount.java
index 2855bc1e56..207d7bd55e 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositAccount.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositAccount.java
@@ -882,4 +882,7 @@ public class FixedDepositAccount extends SavingsAccount {
return SavingsAccountStatusType.MATURED.getValue().equals(this.status);
}
+ public void setClosedOnDate(final LocalDate closedOnDate) {
+ this.closedOnDate = closedOnDate;
+ }
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/RecurringDepositAccount.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/RecurringDepositAccount.java
index 5eea23621b..fb064ab0e8 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/RecurringDepositAccount.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/RecurringDepositAccount.java
@@ -536,11 +536,11 @@ public class RecurringDepositAccount extends
SavingsAccount {
return Money.of(this.currency, this.minRequiredOpeningBalance);
}
- protected void processAccountUponActivation(final DateTimeFormatter fmt,
final boolean postReversals) {
+ protected void processAccountUponActivation(final DateTimeFormatter fmt,
final boolean postReversals,
+ final Long relaxingDaysConfigForPivotDate) {
final Money minRequiredOpeningBalance = Money.of(this.currency,
this.minRequiredOpeningBalance);
final boolean backdatedTxnsAllowedTill = false;
String refNo = null;
- final Long relaxingDaysConfigForPivotDate =
this.configurationDomainService.retrieveRelaxingDaysConfigForPivotDate();
if (minRequiredOpeningBalance.isGreaterThanZero()) {
final SavingsAccountTransactionDTO transactionDTO = new
SavingsAccountTransactionDTO(fmt, getActivationDate(),
minRequiredOpeningBalance.getAmount(), null, null,
accountType);
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccrualWritePlatformServiceImpl.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccrualWritePlatformServiceImpl.java
index 8ee466cdd3..01309e69b3 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccrualWritePlatformServiceImpl.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccrualWritePlatformServiceImpl.java
@@ -32,6 +32,7 @@ import lombok.extern.slf4j.Slf4j;
import
org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.domain.LocalDateInterval;
import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.infrastructure.core.service.MathUtil;
import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
@@ -177,7 +178,9 @@ public class SavingsAccrualWritePlatformServiceImpl
implements SavingsAccrualWri
savingsAccount.office(),
period.getPeriodInterval().endDate(), period.getInterestEarned().abs(), false,
refNo);
savingsAccountTransaction.setRunningBalance(period.getClosingBalance());
savingsAccountTransaction.setOverdraftAmount(period.getInterestEarned());
- savingsAccount.addTransaction(savingsAccountTransaction);
+ if (!MathUtil.isZero(savingsAccountTransaction.getAmount())) {
+ savingsAccount.addTransaction(savingsAccountTransaction);
+ }
}
}
diff --git
a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java
b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java
index 50b2db3987..eb574a051a 100644
---
a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java
+++
b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java
@@ -1042,7 +1042,7 @@ public class SavingsAccount extends
AbstractAuditableWithUTCDateTimeCustom<Long>
if (MathUtil.isEmpty(overdraftAmount) &&
runningBalance.isLessThanZero() && !transaction.isAmountOnHold()) {
overdraftAmount = runningBalance.negated();
}
- if (!calculateInterest || transaction.getId() == null) {
+ if (!calculateInterest || transaction.getId() == null ||
transaction.getOverdraftAmount(this.currency).isZero()) {
transaction.setOverdraftAmount(overdraftAmount);
} else if (!MathUtil.isEqualTo(overdraftAmount,
transaction.getOverdraftAmount(this.currency))
&& !transaction.isAccrual()) {
diff --git
a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularInterestPoster.java
b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularInterestPoster.java
index 913fbdb31e..6928f730b6 100644
---
a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularInterestPoster.java
+++
b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularInterestPoster.java
@@ -36,6 +36,7 @@ import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.accounting.journalentry.domain.JournalEntryType;
import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.infrastructure.core.service.MathUtil;
import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
import
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
@@ -106,7 +107,7 @@ public class SavingsSchedularInterestPoster {
List<SavingsAccountTransactionData>
savingsAccountTransactionDataList =
savingsAccountData.getSavingsAccountTransactionData();
for (SavingsAccountTransactionData savingsAccountTransactionData :
savingsAccountTransactionDataList) {
- if (savingsAccountTransactionData.getId() == null) {
+ if (savingsAccountTransactionData.getId() == null &&
!MathUtil.isZero(savingsAccountTransactionData.getAmount())) {
final String key =
savingsAccountTransactionData.getRefNo();
final Boolean isOverdraft =
savingsAccountTransactionData.getIsOverdraft();
final SavingsAccountTransactionData dataFromFetch =
savingsAccountTransactionDataHashMap.get(key);
@@ -183,7 +184,7 @@ public class SavingsSchedularInterestPoster {
auditTime, userId, savingsAccountData.getId() });
List<SavingsAccountTransactionData>
savingsAccountTransactionDataList =
savingsAccountData.getSavingsAccountTransactionData();
for (SavingsAccountTransactionData savingsAccountTransactionData :
savingsAccountTransactionDataList) {
- if (savingsAccountTransactionData.getId() == null) {
+ if (savingsAccountTransactionData.getId() == null &&
!MathUtil.isZero(savingsAccountTransactionData.getAmount())) {
UUID uuid = UUID.randomUUID();
savingsAccountTransactionData.setRefNo(uuid.toString());
transRefNo.add(uuid.toString());
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/FixedDepositTest.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/FixedDepositTest.java
index 3162faff4c..a55e312cc5 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/FixedDepositTest.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/FixedDepositTest.java
@@ -48,12 +48,17 @@ import java.util.Locale;
import java.util.TimeZone;
import lombok.extern.slf4j.Slf4j;
import
org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity;
+import org.apache.fineract.client.models.PutGlobalConfigurationsRequest;
+import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
+import
org.apache.fineract.infrastructure.configuration.api.GlobalConfigurationConstants;
import org.apache.fineract.infrastructure.core.api.JsonQuery;
import
org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.integrationtests.client.IntegrationTest;
+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.SchedulerJobHelper;
import org.apache.fineract.integrationtests.common.TaxComponentHelper;
import org.apache.fineract.integrationtests.common.TaxGroupHelper;
@@ -92,6 +97,7 @@ public class FixedDepositTest extends IntegrationTest {
private SavingsAccountHelper savingsAccountHelper;
private JournalEntryHelper journalEntryHelper;
private FinancialActivityAccountHelper financialActivityAccountHelper;
+ private GlobalConfigurationHelper globalConfigurationHelper;
private FixedDepositAccountInterestCalculationServiceImpl
fixedDepositAccountInterestCalculationServiceImpl;
@@ -108,12 +114,14 @@ public class FixedDepositTest extends IntegrationTest {
private static final String NONE = "1";
private static final String CASH_BASED = "2";
+ private static final String ACCRUAL = "3";
public static final String MINIMUM_OPENING_BALANCE = "1000.0";
public static final String ACCOUNT_TYPE_INDIVIDUAL = "INDIVIDUAL";
public static final String CLOSURE_TYPE_WITHDRAW_DEPOSIT = "100";
public static final String CLOSURE_TYPE_TRANSFER_TO_SAVINGS = "200";
public static final String CLOSURE_TYPE_REINVEST = "300";
+ public static final String CLOSURE_TYPE_REINVEST_PRINCIPAL_ONLY = "400";
public static final Integer DAILY_COMPOUNDING_INTERVAL = 0;
public static final Integer MONTHLY_INTERVAL = 1;
public static final Integer QUARTERLY_INTERVAL = 3;
@@ -130,6 +138,7 @@ public class FixedDepositTest extends IntegrationTest {
public static final Float THRESHOLD = 1.0f;
private MockedStatic<MoneyHelper> moneyHelperStatic;
+ private SchedulerJobHelper schedulerJobHelper;
@BeforeEach
public void setup() {
@@ -138,8 +147,11 @@ public class FixedDepositTest extends IntegrationTest {
this.requestSpec.header("Authorization", "Basic " +
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
this.requestSpec.header("Fineract-Platform-TenantId", "default");
this.responseSpec = new
ResponseSpecBuilder().expectStatusCode(200).build();
+ this.accountHelper = new AccountHelper(this.requestSpec,
this.responseSpec);
+ this.schedulerJobHelper = new SchedulerJobHelper(this.requestSpec);
this.journalEntryHelper = new JournalEntryHelper(this.requestSpec,
this.responseSpec);
this.financialActivityAccountHelper = new
FinancialActivityAccountHelper(this.requestSpec);
+ this.globalConfigurationHelper = new GlobalConfigurationHelper();
TimeZone.setDefault(TimeZone.getTimeZone(Utils.TENANT_TIME_ZONE));
}
@@ -2598,6 +2610,158 @@ public class FixedDepositTest extends IntegrationTest {
FixedDepositAccountStatusChecker.verifyFixedDepositIsActive(fixedDepositAccountStatusHashMap);
}
+ @Test
+ public void
testCloseFixedDepositForCLOSURE_TYPE_REINVEST_withConfigurationMaturityInstruction()
{
+ try {
+ final Account assetAccount =
this.accountHelper.createAssetAccount();
+ final Account liabilityAccount =
this.accountHelper.createLiabilityAccount();
+ final Account incomeAccount =
this.accountHelper.createIncomeAccount();
+ final Account expenseAccount =
this.accountHelper.createExpenseAccount();
+
+ this.fixedDepositProductHelper = new
FixedDepositProductHelper(this.requestSpec, this.responseSpec);
+ this.fixedDepositAccountHelper = new
FixedDepositAccountHelper(this.requestSpec, this.responseSpec);
+
+ DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy",
Locale.US);
+ DateFormat monthDayFormat = new SimpleDateFormat("dd MMM",
Locale.US);
+ DateFormat currentDateFormat = new SimpleDateFormat("dd");
+
+ Calendar todaysDate = Calendar.getInstance();
+ int currentYear = todaysDate.get(Calendar.YEAR);
+ todaysDate.set(currentYear, Calendar.JANUARY, 1);
+ final String VALID_FROM = dateFormat.format(todaysDate.getTime());
+ todaysDate.set(currentYear + 1, Calendar.MARCH, 1);
+ final String VALID_TO = dateFormat.format(todaysDate.getTime());
+
+ todaysDate = Calendar.getInstance();
+ Integer currentDate =
Integer.valueOf(currentDateFormat.format(todaysDate.getTime()));
+ todaysDate.set(currentYear, Calendar.JANUARY, 1);
+ final String SUBMITTED_ON_DATE =
dateFormat.format(todaysDate.getTime());
+ final String APPROVED_ON_DATE =
dateFormat.format(todaysDate.getTime());
+ dateFormat.format(todaysDate.getTime());
+ monthDayFormat.format(todaysDate.getTime());
+
+
globalConfigurationHelper.updateGlobalConfiguration(GlobalConfigurationConstants.ENABLE_BUSINESS_DATE,
+ new PutGlobalConfigurationsRequest().enabled(true));
+
+ LocalDate marchDate = LocalDate.of(currentYear + 1, 3, 1);
+ BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec,
BusinessDateType.BUSINESS_DATE, marchDate);
+
+ log.info("Submitted Date: {}", SUBMITTED_ON_DATE);
+
+ final String accountingRule = ACCRUAL;
+ Integer fixedDepositProductId =
createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule, assetAccount,
liabilityAccount,
+ incomeAccount, expenseAccount);
+ Assertions.assertNotNull(fixedDepositProductId);
+
+ Integer clientId = ClientHelper.createClient(this.requestSpec,
this.responseSpec);
+ Assertions.assertNotNull(clientId);
+
+ Integer fixedDepositAccountId =
applyForFixedDepositApplication(clientId.toString(),
fixedDepositProductId.toString(),
+ SUBMITTED_ON_DATE, WHOLE_TERM,
Integer.valueOf(CLOSURE_TYPE_REINVEST));
+ Assertions.assertNotNull(fixedDepositAccountId);
+
+
this.fixedDepositAccountHelper.approveFixedDeposit(fixedDepositAccountId,
APPROVED_ON_DATE);
+
this.fixedDepositAccountHelper.activateFixedDeposit(fixedDepositAccountId,
APPROVED_ON_DATE);
+
+ schedulerJobHelper.executeAndAwaitJob("Update Deposit Accounts
Maturity details");
+
+ HashMap fixedDepositAccountStatusHashMap =
FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec,
+ this.responseSpec, fixedDepositAccountId.toString());
+
+
FixedDepositAccountStatusChecker.verifyFixedDepositAccountIsClosed(fixedDepositAccountStatusHashMap);
+ } finally {
+
globalConfigurationHelper.updateGlobalConfiguration(GlobalConfigurationConstants.ENABLE_BUSINESS_DATE,
+ new PutGlobalConfigurationsRequest().enabled(false));
+ }
+
+ }
+
+ @Test
+ public void testCloseFixedDepositForCLOSURE_TYPE_REINVEST() {
+ testClosureTypeReinvestVariants(CLOSURE_TYPE_REINVEST);
+ }
+
+ @Test
+ public void testCloseFixedDepositForCLOSURE_TYPE_REINVEST_PRINCIPAL_ONLY()
{
+ testClosureTypeReinvestVariants(CLOSURE_TYPE_REINVEST_PRINCIPAL_ONLY);
+ }
+
+ public void testClosureTypeReinvestVariants(String reInvest) {
+ try {
+ final Account assetAccount =
this.accountHelper.createAssetAccount();
+ final Account liabilityAccount =
this.accountHelper.createLiabilityAccount();
+ final Account incomeAccount =
this.accountHelper.createIncomeAccount();
+ final Account expenseAccount =
this.accountHelper.createExpenseAccount();
+
+ this.fixedDepositProductHelper = new
FixedDepositProductHelper(this.requestSpec, this.responseSpec);
+ this.fixedDepositAccountHelper = new
FixedDepositAccountHelper(this.requestSpec, this.responseSpec);
+
+ DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy",
Locale.US);
+ DateFormat monthDayFormat = new SimpleDateFormat("dd MMM",
Locale.US);
+ DateFormat currentDateFormat = new SimpleDateFormat("dd");
+
+ Calendar todaysDate = Calendar.getInstance();
+ int currentYear = todaysDate.get(Calendar.YEAR);
+ todaysDate.set(currentYear, Calendar.JANUARY, 1);
+ final String VALID_FROM = dateFormat.format(todaysDate.getTime());
+ todaysDate.set(currentYear + 1, Calendar.JANUARY, 1);
+ final String VALID_TO = dateFormat.format(todaysDate.getTime());
+
+ todaysDate = Calendar.getInstance();
+ Integer currentDate =
Integer.valueOf(currentDateFormat.format(todaysDate.getTime()));
+ todaysDate.set(currentYear, Calendar.JANUARY, 1);
+ final String SUBMITTED_ON_DATE =
dateFormat.format(todaysDate.getTime());
+ final String APPROVED_ON_DATE =
dateFormat.format(todaysDate.getTime());
+ dateFormat.format(todaysDate.getTime());
+ monthDayFormat.format(todaysDate.getTime());
+
+
globalConfigurationHelper.updateGlobalConfiguration(GlobalConfigurationConstants.ENABLE_BUSINESS_DATE,
+ new PutGlobalConfigurationsRequest().enabled(true));
+
+ LocalDate marchDate = LocalDate.of(currentYear + 1, 1, 1);
+ BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec,
BusinessDateType.BUSINESS_DATE, marchDate);
+
+ log.info("Submitted Date: {}", SUBMITTED_ON_DATE);
+
+ final String accountingRule = ACCRUAL;
+ Integer fixedDepositProductId =
createFixedDepositProduct(VALID_FROM, VALID_TO, accountingRule, assetAccount,
liabilityAccount,
+ incomeAccount, expenseAccount);
+ Assertions.assertNotNull(fixedDepositProductId);
+
+ Integer clientId = ClientHelper.createClient(this.requestSpec,
this.responseSpec);
+ Assertions.assertNotNull(clientId);
+
+ Integer fixedDepositAccountId =
applyForFixedDepositApplication(clientId.toString(),
fixedDepositProductId.toString(),
+ SUBMITTED_ON_DATE, WHOLE_TERM, "10000", "12");
+ Assertions.assertNotNull(fixedDepositAccountId);
+
+
this.fixedDepositAccountHelper.approveFixedDeposit(fixedDepositAccountId,
APPROVED_ON_DATE);
+
this.fixedDepositAccountHelper.activateFixedDeposit(fixedDepositAccountId,
APPROVED_ON_DATE);
+
+ schedulerJobHelper.executeAndAwaitJob("Update Deposit Accounts
Maturity details");
+
+ HashMap fixedDepositAccountStatusHashMap =
FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec,
+ this.responseSpec, fixedDepositAccountId.toString());
+
+
FixedDepositAccountStatusChecker.verifyFixedDepositAccountIsMatured(fixedDepositAccountStatusHashMap);
+
+ todaysDate.set(currentYear + 1, Calendar.JANUARY, 1);
+ final String CLOSED_ON_DATE =
dateFormat.format(todaysDate.getTime());
+
+ Integer prematureClosureTransactionId = (Integer)
this.fixedDepositAccountHelper.closeForFixedDeposit(fixedDepositAccountId,
+ CLOSED_ON_DATE, reInvest, null,
CommonConstants.RESPONSE_RESOURCE_ID);
+
+ fixedDepositAccountStatusHashMap =
FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec,
+ this.responseSpec, fixedDepositAccountId.toString());
+
+
FixedDepositAccountStatusChecker.verifyFixedDepositAccountIsClosed(fixedDepositAccountStatusHashMap);
+ } finally {
+
globalConfigurationHelper.updateGlobalConfiguration(GlobalConfigurationConstants.ENABLE_BUSINESS_DATE,
+ new PutGlobalConfigurationsRequest().enabled(false));
+ }
+
+ }
+
private Integer createFixedDepositProduct(final String validFrom, final
String validTo, final String accountingRule,
Account... accounts) {
log.info("------------------------------CREATING NEW FIXED DEPOSIT
PRODUCT ---------------------------------------");
@@ -2606,6 +2770,8 @@ public class FixedDepositTest extends IntegrationTest {
fixedDepositProductHelper =
fixedDepositProductHelper.withAccountingRuleAsCashBased(accounts);
} else if (accountingRule.equals(NONE)) {
fixedDepositProductHelper =
fixedDepositProductHelper.withAccountingRuleAsNone();
+ } else if (accountingRule.equals(ACCRUAL)) {
+ fixedDepositProductHelper =
fixedDepositProductHelper.withAccountingRuleAsAccrual(accounts);
}
final String fixedDepositProductJSON =
fixedDepositProductHelper.withPeriodRangeChart() //
.build(validFrom, validTo, true);
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsInterestPostingTest.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsInterestPostingTest.java
index efdc69d2bf..a8e7a15e53 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsInterestPostingTest.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsInterestPostingTest.java
@@ -340,6 +340,92 @@ public class SavingsInterestPostingTest {
}
}
+ @Test
+ public void testPostInterestNotZero() {
+ try {
+ final String amountDeposit = "1000";
+ final String amountWithdrawal = "1000";
+
+ final Account assetAccount = accountHelper.createAssetAccount();
+ final Account incomeAccount = accountHelper.createIncomeAccount();
+ final Account expenseAccount =
accountHelper.createExpenseAccount();
+ final Account liabilityAccount =
accountHelper.createLiabilityAccount();
+ final Account interestReceivableAccount =
accountHelper.createAssetAccount("interestReceivableAccount");
+ final Account savingsControlAccount =
accountHelper.createLiabilityAccount("Savings Control");
+ final Account interestPayableAccount =
accountHelper.createLiabilityAccount("Interest Payable");
+
+ final Integer productId =
createSavingsProductWithAccrualAccountingWithOutOverdraftAllowed(
+ interestPayableAccount.getAccountID().toString(),
savingsControlAccount.getAccountID().toString(),
+ interestReceivableAccount.getAccountID().toString(),
assetAccount, incomeAccount, expenseAccount, liabilityAccount);
+
+ final Integer clientId = ClientHelper.createClient(requestSpec,
responseSpec, "01 January 2025");
+ final LocalDate startDate =
LocalDate.of(LocalDate.now(Utils.getZoneIdOfTenant()).getYear(), 1, 1);
+ final String startStr = DateTimeFormatter.ofPattern("dd MMMM
yyyy", Locale.US).format(startDate);
+
+ final Integer accountId =
savingsAccountHelper.applyForSavingsApplicationOnDate(clientId, productId,
+ SavingsAccountHelper.ACCOUNT_TYPE_INDIVIDUAL, startStr);
+ savingsAccountHelper.approveSavingsOnDate(accountId, startStr);
+ savingsAccountHelper.activateSavings(accountId, startStr);
+ savingsAccountHelper.depositToSavingsAccount(accountId,
amountDeposit, startStr, CommonConstants.RESPONSE_RESOURCE_ID);
+
+
globalConfigurationHelper.updateGlobalConfiguration(GlobalConfigurationConstants.ENABLE_BUSINESS_DATE,
+ new PutGlobalConfigurationsRequest().enabled(true));
+ LocalDate februaryDate = LocalDate.of(startDate.getYear(), 2, 1);
+ BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec,
BusinessDateType.BUSINESS_DATE, februaryDate);
+
+ schedulerJobHelper.executeAndAwaitJob(POST_INTEREST_JOB_NAME);
+
+ List<HashMap> txsFebruary = getInterestTransactions(accountId); //
OBTENER EL POSTEO DEL INTEREST
+
+ long daysFebruary = ChronoUnit.DAYS.between(startDate,
februaryDate);
+ BigDecimal expectedFebruary = calcInterestPosting(productHelper,
amountDeposit, daysFebruary);
+ Assertions.assertEquals(expectedFebruary,
BigDecimal.valueOf(((Double) txsFebruary.get(0).get("amount"))));
+
+ final LocalDate withdrawalDate = LocalDate.of(startDate.getYear(),
2, 1);
+ final String withdrawal = DateTimeFormatter.ofPattern("dd MMMM
yyyy", Locale.US).format(withdrawalDate);
+
+ BigDecimal runningBalance = new
BigDecimal(txsFebruary.get(0).get("runningBalance").toString());
+ String withdrawalRunning = runningBalance.setScale(2,
RoundingMode.HALF_UP).toString();
+
+ savingsAccountHelper.withdrawalFromSavingsAccount(accountId,
withdrawalRunning, withdrawal,
+ CommonConstants.RESPONSE_RESOURCE_ID);
+ savingsAccountHelper.withdrawalFromSavingsAccount(accountId,
amountWithdrawal, withdrawal,
+ CommonConstants.RESPONSE_RESOURCE_ID);
+
+ LocalDate marchDate = LocalDate.of(startDate.getYear(), 3, 1);
+ BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec,
BusinessDateType.BUSINESS_DATE, marchDate);
+
+ schedulerJobHelper.executeAndAwaitJob(POST_INTEREST_JOB_NAME);
+
+ List<HashMap> txs = getInterestTransactions(accountId); // CON
ESTE DEBEMOS DE VALIDAR QUE EL DIA DE MARZO
+ // NO SE
TENGA POSTEO EN CERO
+ for (HashMap tx : txs) {
+ BigDecimal amt = BigDecimal.valueOf(((Double)
tx.get("amount")));
+ @SuppressWarnings("unchecked")
+ Map<String, Object> typeMap = (Map<String, Object>)
tx.get("transactionType");
+ SavingsAccountTransactionType type =
SavingsAccountTransactionType.fromInt(((Double) typeMap.get("id")).intValue());
+ if (type.isOverDraftInterestPosting()) {
+ long days = ChronoUnit.DAYS.between(withdrawalDate,
marchDate);
+ BigDecimal decimalsss = new
BigDecimal(txsFebruary.get(0).get("runningBalance").toString())
+ .subtract(runningBalance.setScale(2,
RoundingMode.HALF_UP));
+ BigDecimal withdraw = new BigDecimal(amountWithdrawal);
+ BigDecimal res = withdraw.subtract(decimalsss);
+ BigDecimal expected = calcOverdraftPosting(productHelper,
res.toString(), days);
+ Assertions.assertEquals(expected, amt);
+ }
+ }
+
+ Assertions.assertEquals(0L, countInterestOnDate(accountId,
marchDate), "Expected exactly one INTEREST posting on posting date");
+ Assertions.assertEquals(1L, countOverdraftOnDate(accountId,
marchDate),
+ "Expected exactly one OVERDRAFT posting on posting date");
+
+ assertNoAccrualReversals(accountId);
+ } finally {
+
globalConfigurationHelper.updateGlobalConfiguration(GlobalConfigurationConstants.ENABLE_BUSINESS_DATE,
+ new PutGlobalConfigurationsRequest().enabled(false));
+ }
+ }
+
private List<HashMap> getInterestTransactions(Integer savingsAccountId) {
List<HashMap> all =
savingsAccountHelper.getSavingsTransactions(savingsAccountId);
List<HashMap> filtered = new ArrayList<>();
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositAccountHelper.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositAccountHelper.java
index 68a29fc49c..9c84b4f645 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositAccountHelper.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositAccountHelper.java
@@ -402,6 +402,17 @@ public class FixedDepositAccountHelper {
getPrematureCloseForFixedDepositAccountAsJSON(closedOnDate,
closureType, toSavingsId), jsonAttributeToGetBack);
}
+ // TODO: Rewrite to use fineract-client instead!
+ // Example:
org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long,
+ // org.apache.fineract.client.models.PostLoansLoanIdRequest)
+ @Deprecated(forRemoval = true)
+ public Object closeForFixedDeposit(final Integer fixedDepositAccountId,
final String closedOnDate, final String closureType,
+ final Integer toSavingsId, final String jsonAttributeToGetBack) {
+ LOG.info("--------------------- CLOSE FOR FIXED DEPOSIT
----------------------------");
+ return
performFixedDepositActions(createFixedDepositCalculateInterestURL(CLOSE_FIXED_DEPOSIT_COMMAND,
fixedDepositAccountId),
+ getPrematureCloseForFixedDepositAccountAsJSON(closedOnDate,
closureType, toSavingsId), jsonAttributeToGetBack);
+ }
+
// TODO: Rewrite to use fineract-client instead!
// Example:
org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long,
// org.apache.fineract.client.models.PostLoansLoanIdRequest)
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositProductHelper.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositProductHelper.java
index 20cf1a91ea..3d7af6583a 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositProductHelper.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/fixeddeposit/FixedDepositProductHelper.java
@@ -163,6 +163,9 @@ public class FixedDepositProductHelper {
if (this.accountingRule.equals(CASH_BASED)) {
map.putAll(getAccountMappingForCashBased());
}
+ if (this.accountingRule.equals(ACCRUAL_PERIODIC)) {
+ map.putAll(getAccountMappingForAccrualBased());
+ }
String FixedDepositProductCreateJson = new Gson().toJson(map);
LOG.info("{}", FixedDepositProductCreateJson);
@@ -397,6 +400,12 @@ public class FixedDepositProductHelper {
return this;
}
+ public FixedDepositProductHelper withAccountingRuleAsAccrual(final
Account[] account_list) {
+ this.accountingRule = ACCRUAL_PERIODIC;
+ this.accountList = account_list;
+ return this;
+ }
+
public FixedDepositProductHelper withPeriodRangeChart() {
this.chartSlabs = constructChartSlabWithPeriodRange();
return this;
@@ -456,6 +465,41 @@ public class FixedDepositProductHelper {
return map;
}
+ // TODO: Rewrite to use fineract-client instead!
+ // Example:
org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long,
+ // org.apache.fineract.client.models.PostLoansLoanIdRequest)
+ @Deprecated(forRemoval = true)
+ private Map<String, String> getAccountMappingForAccrualBased() {
+ final Map<String, String> map = new HashMap<>();
+ if (accountList != null) {
+ for (int i = 0; i < this.accountList.length; i++) {
+ if
(this.accountList[i].getAccountType().equals(Account.AccountType.ASSET)) {
+ final String ID =
this.accountList[i].getAccountID().toString();
+ map.put("savingsReferenceAccountId", ID);
+ map.put("feesReceivableAccountId", ID);
+ map.put("penaltiesReceivableAccountId", ID);
+ }
+ if
(this.accountList[i].getAccountType().equals(Account.AccountType.LIABILITY)) {
+
+ final String ID =
this.accountList[i].getAccountID().toString();
+ map.put("savingsControlAccountId", ID);
+ map.put("transfersInSuspenseAccountId", ID);
+ map.put("interestPayableAccountId", ID);
+ }
+ if
(this.accountList[i].getAccountType().equals(Account.AccountType.EXPENSE)) {
+ final String ID =
this.accountList[i].getAccountID().toString();
+ map.put("interestOnSavingsAccountId", ID);
+ }
+ if
(this.accountList[i].getAccountType().equals(Account.AccountType.INCOME)) {
+ final String ID =
this.accountList[i].getAccountID().toString();
+ map.put("incomeFromFeeAccountId", ID);
+ map.put("incomeFromPenaltyAccountId", ID);
+ }
+ }
+ }
+ return map;
+ }
+
// TODO: Rewrite to use fineract-client instead!
// Example:
org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long,
// org.apache.fineract.client.models.PostLoansLoanIdRequest)