Skip to content

Commit

Permalink
fix: Savings accrual when overdraft
Browse files Browse the repository at this point in the history
  • Loading branch information
Jose Alberto Hernandez authored and alberto-art3ch committed Jun 27, 2024
1 parent 5afc83b commit 3ffcb03
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3419,6 +3419,15 @@ public CommandWrapperBuilder unblockSavingsAccount(final Long accountId) {
return this;
}

public CommandWrapperBuilder addAccrualsToSavingsAccount(final Long accountId) {
this.actionName = "ADD_ACCRUALS";
this.entityName = "SAVINGSACCOUNT";
this.savingsId = accountId;
this.entityId = null;
this.href = "/savingsaccounts/" + accountId + "?command=addAccrualTransactions";
return this;
}

public CommandWrapperBuilder disableAdHoc(Long adHocId) {
this.actionName = "DISABLE";
this.entityName = "ADHOC";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public class SavingsApiConstants {
public static final String COMMAND_BLOCK_DEBIT = "blockDebit";
public static final String COMMAND_UNBLOCK_DEBIT = "unblockDebit";
public static final String COMMAND_UNBLOCK_CREDIT = "unblockCredit";
public static final String COMMAND_ADD_ACCRUAL_TRANSACTION = "addAccrualTransactions";

// general
public static final String localeParamName = "locale";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,9 @@ private String handleCommands(Long accountId, String externalId, String commandP
} else if (CommandParameterUtil.is(commandParam, SavingsApiConstants.COMMAND_UNBLOCK_ACCOUNT)) {
final CommandWrapper commandRequest = builder.unblockSavingsAccount(accountId).build();
result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (CommandParameterUtil.is(commandParam, SavingsApiConstants.COMMAND_ADD_ACCRUAL_TRANSACTION)) {
final CommandWrapper commandRequest = builder.addAccrualsToSavingsAccount(accountId).build();
result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
}

if (result == null) {
Expand All @@ -595,7 +598,8 @@ private String handleCommands(Long accountId, String externalId, String commandP
"postInterest", "close", "assignSavingsOfficer", "unassignSavingsOfficer",
SavingsApiConstants.COMMAND_BLOCK_DEBIT, SavingsApiConstants.COMMAND_UNBLOCK_DEBIT,
SavingsApiConstants.COMMAND_BLOCK_CREDIT, SavingsApiConstants.COMMAND_UNBLOCK_CREDIT,
SavingsApiConstants.COMMAND_BLOCK_ACCOUNT, SavingsApiConstants.COMMAND_UNBLOCK_ACCOUNT });
SavingsApiConstants.COMMAND_BLOCK_ACCOUNT, SavingsApiConstants.COMMAND_UNBLOCK_ACCOUNT,
SavingsApiConstants.COMMAND_ADD_ACCRUAL_TRANSACTION });
}

return toApiJsonSerializer.serialize(result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,8 @@ public void postInterest(SavingsAccount account, final MathContext mc, final Loc
account.addTransaction(reversal);
}
}
if (account.savingsProduct().isAccrualBasedAccountingEnabled() && MathUtil.isGreaterThanZero(interestEarnedToBePostedForPeriod)) {
if (account.savingsProduct().isAccrualBasedAccountingEnabled()
&& MathUtil.isGreaterThanZero(interestEarnedToBePostedForPeriod)) {
log.info("TX2: {}", interestEarnedToBePostedForPeriod.getAmount());
SavingsAccountTransaction accrualTransaction = SavingsAccountTransaction.accrual(account, account.office(),
interestPostingTransactionDate, interestEarnedToBePostedForPeriod,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* 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.portfolio.savings.handler;

import lombok.RequiredArgsConstructor;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.savings.service.SavingsAccrualWritePlatformService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@CommandType(entity = "SAVINGSACCOUNT", action = "ADD_ACCRUALS")
@RequiredArgsConstructor
public class AddAccrualTransactionsToSavingsAccountCommandHandler implements NewCommandSourceHandler {

private final SavingsAccrualWritePlatformService savingsAccrualWritePlatformService;

@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {

return savingsAccrualWritePlatformService.addAccrualEntries(command.getSavingsId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import java.time.LocalDate;
import java.util.Collection;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.exception.MultiException;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountCharge;
Expand All @@ -34,4 +35,6 @@ public interface SavingsAccrualWritePlatformService {
SavingsAccountTransaction addSavingsChargeAccrualTransaction(SavingsAccount savingsAccount, SavingsAccountCharge savingsAccountCharge,
LocalDate transactionDate);

CommandProcessingResult addAccrualEntries(Long savingsAccountId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
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;
Expand Down Expand Up @@ -92,6 +95,37 @@ public void addAccrualEntries(LocalDate tillDate) throws JobExecutionException {
}
}

@Transactional
@Override
public CommandProcessingResult addAccrualEntries(Long savingsAccountId) {
SavingsAccount savingsAccount = savingsAccountAssembler.assembleFrom(savingsAccountId, false);
final LocalDate tillDate = DateUtils.getBusinessLocalDate();
final Collection<SavingsAccrualData> savingsAccrualData = savingsAccountReadPlatformService.retrievePeriodicAccrualData(tillDate,
savingsAccount);
final Integer financialYearBeginningMonth = configurationDomainService.retrieveFinancialYearBeginningMonth();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final MathContext mc = MoneyHelper.getMathContext();

List<Throwable> errors = new ArrayList<>();
for (SavingsAccrualData savingsAccrual : savingsAccrualData) {
try {
LocalDate fromDate = savingsAccrual.getAccruedTill();
if (fromDate == null) {
fromDate = savingsAccount.getActivationDate();
}
log.debug("Processing savings account {} from date {} till date {}", savingsAccrual.getAccountNo(), fromDate, tillDate);
addAccrualTransactions(savingsAccount, fromDate, tillDate, financialYearBeginningMonth,
isSavingsInterestPostingAtCurrentPeriodEnd, mc);
} catch (Exception e) {
log.error("Failed to add accrual transaction for savings {} : {}", savingsAccrual.getAccountNo(), e.getMessage());
errors.add(e.getCause());
}
}

return CommandProcessingResult.empty();
}

@Override
public boolean isChargeToBeRecognizedAsAccrual(final Collection<Long> chargeIds, final SavingsAccountCharge savingsAccountCharge) {
if (chargeIds.isEmpty()) {
Expand Down Expand Up @@ -164,12 +198,10 @@ private void addAccrualTransactions(SavingsAccount savingsAccount, final LocalDa
.map(transaction -> transaction.getTransactionDate()).toList();

LocalDate accruedTillDate = fromDate;
final boolean accountInOverdraft = (savingsAccount.getSummary().getAccountBalance().compareTo(BigDecimal.ZERO) < 0);
log.info("ACT {} : {} {}", savingsAccount.getAccountNumber(), savingsAccount.getSummary().getAccountBalance(), accountInOverdraft);
for (PostingPeriod period : allPostingPeriods) {
if (!accountInOverdraft) {
if (MathUtil.isGreaterThanZero(period.closingBalance())) {
period.calculateInterest(compoundInterestValues);
log.info(" period {} {} : {}", period.getPeriodInterval().startDate(), period.getPeriodInterval().endDate(),
log.debug(" period {} {} : {}", period.getPeriodInterval().startDate(), period.getPeriodInterval().endDate(),
period.getInterestEarned());
if (!accrualTransactionDates.contains(period.getPeriodInterval().endDate())) {
SavingsAccountTransaction savingsAccountTransaction = SavingsAccountTransaction.accrual(savingsAccount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ public class SavingsProductDataValidator {
nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName,
SavingsApiConstants.minRequiredBalanceParamName, SavingsApiConstants.enforceMinRequiredBalanceParamName,
SavingsApiConstants.maxAllowedLienLimitParamName, SavingsApiConstants.lienAllowedParamName,
minBalanceForInterestCalculationParamName, withHoldTaxParamName, taxGroupIdParamName, SavingsApiConstants.accrualChargesParamName));
minBalanceForInterestCalculationParamName, withHoldTaxParamName, taxGroupIdParamName,
SavingsApiConstants.accrualChargesParamName));

public void validateForCreate(final String json) {

Expand Down

0 comments on commit 3ffcb03

Please sign in to comment.