From 9982407906de33ee9f028b23d8e1c2c5ff84b0a8 Mon Sep 17 00:00:00 2001 From: Jose Alberto Hernandez Date: Mon, 18 Mar 2024 11:35:02 -0600 Subject: [PATCH] Fix: Savings and Deposit undo transfer --- .../LoanProductDataValidator.java | 3 +- .../SavingsAccountDomainServiceJpa.java | 105 +++++++++++++++++- .../domain/SavingsAccountDomainService.java | 4 + 3 files changed, 106 insertions(+), 6 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java index bd50792bb7c..c59362c5deb 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java @@ -171,7 +171,8 @@ public final class LoanProductDataValidator { LoanProductConstants.ENABLE_DOWN_PAYMENT, LoanProductConstants.DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT, LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT, LoanProductConstants.REPAYMENT_START_DATE_TYPE, LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY, LoanProductConstants.LOAN_SCHEDULE_TYPE, - LoanProductConstants.LOAN_SCHEDULE_PROCESSING_TYPE, LoanProductConstants.FIXED_LENGTH, LoanProductConstants.ALLOW_ACCRUAL_POSTING_IN_ARREARS)); + LoanProductConstants.LOAN_SCHEDULE_PROCESSING_TYPE, LoanProductConstants.FIXED_LENGTH, + LoanProductConstants.ALLOW_ACCRUAL_POSTING_IN_ARREARS)); private static final String[] SUPPORTED_LOAN_CONFIGURABLE_ATTRIBUTES = { LoanProductConstants.amortizationTypeParamName, LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyCodeParamName, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java index 2558032a902..958788548bc 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java @@ -18,6 +18,8 @@ */ package org.apache.fineract.portfolio.savings.domain; +import static org.apache.fineract.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME; + import java.math.BigDecimal; import java.math.MathContext; import java.time.LocalDate; @@ -31,7 +33,11 @@ import lombok.extern.slf4j.Slf4j; import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; +import org.apache.fineract.infrastructure.core.data.ApiParameterError; +import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; import org.apache.fineract.infrastructure.core.domain.ExternalId; +import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; +import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.event.business.domain.savings.transaction.SavingsDepositBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.savings.transaction.SavingsWithdrawalBusinessEvent; @@ -40,10 +46,16 @@ import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.MoneyHelper; +import org.apache.fineract.portfolio.client.domain.Client; +import org.apache.fineract.portfolio.client.exception.ClientNotActiveException; +import org.apache.fineract.portfolio.group.domain.Group; +import org.apache.fineract.portfolio.group.exception.GroupNotActiveException; import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail; import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType; +import org.apache.fineract.portfolio.savings.SavingsApiConstants; import org.apache.fineract.portfolio.savings.SavingsTransactionBooleanValues; import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionDTO; +import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionDataValidator; import org.apache.fineract.portfolio.savings.domain.interest.PostingPeriod; import org.apache.fineract.portfolio.savings.exception.DepositAccountTransactionNotAllowedException; import org.springframework.stereotype.Service; @@ -57,6 +69,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi private final PlatformSecurityContext context; private final SavingsAccountRepositoryWrapper savingsAccountRepository; private final SavingsAccountTransactionRepository savingsAccountTransactionRepository; + private final SavingsAccountTransactionDataValidator savingsAccountTransactionDataValidator; private final JournalEntryWritePlatformService journalEntryWritePlatformService; private final ConfigurationDomainService configurationDomainService; private final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository; @@ -485,14 +498,96 @@ public void postInterest(SavingsAccount account, final MathContext mc, final Loc } @Override - public void reverseTransfer(SavingsAccountTransaction savingsTransaction, boolean backdatedTxnsAllowedTill) { - final SavingsAccount account = savingsTransaction.getSavingsAccount(); + public void reverseTransfer(SavingsAccountTransaction savingsAccountTransaction, boolean backdatedTxnsAllowedTill) { + final SavingsAccount account = savingsAccountTransaction.getSavingsAccount(); account.setHelpers(savingsAccountTransactionSummaryWrapper, savingsHelper); - List savingsAccountTransactions = new ArrayList<>(); - savingsAccountTransactions.add(savingsTransaction); + undoTransaction(account, savingsAccountTransaction); + } + + @Override + public void undoTransaction(SavingsAccount account, SavingsAccountTransaction savingsAccountTransaction) { + + final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService + .isSavingsInterestPostingAtCurrentPeriodEnd(); + final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); + final Set existingTransactionIds = new HashSet<>(); + final Set existingReversedTransactionIds = new HashSet<>(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); + + final Long savingsId = account.getId(); + final Long transactionId = savingsAccountTransaction.getId(); + + this.savingsAccountTransactionDataValidator.validateTransactionWithPivotDate(savingsAccountTransaction.getTransactionDate(), + account); + + if (!account.allowModify()) { + throw new PlatformServiceUnavailableException("error.msg.saving.account.transaction.update.not.allowed", + "Savings account transaction:" + transactionId + " update not allowed for this savings type", transactionId); + } + + final LocalDate today = DateUtils.getBusinessLocalDate(); + final MathContext mc = new MathContext(15, MoneyHelper.getRoundingMode()); + + if (account.isNotActive()) { + throwValidationExceptionForActiveStatus(SavingsApiConstants.undoTransactionAction); + } + account.undoTransaction(transactionId); + + // undoing transaction is withdrawal then undo withdrawal fee transaction if any + if (savingsAccountTransaction.isWithdrawal()) { + final SavingsAccountTransaction nextSavingsAccountTransaction = this.savingsAccountTransactionRepository + .findOneByIdAndSavingsAccountId(transactionId + 1, savingsId); + if (nextSavingsAccountTransaction != null && nextSavingsAccountTransaction.isWithdrawalFeeAndNotReversed()) { + account.undoTransaction(transactionId + 1); + } + } + boolean isInterestTransfer = false; + LocalDate postInterestOnDate = null; + boolean postReversals = false; + checkClientOrGroupActive(account); + if (savingsAccountTransaction.isPostInterestCalculationRequired() + && account.isBeforeLastPostingPeriod(savingsAccountTransaction.getTransactionDate(), false)) { + postInterest(account, mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, + postInterestOnDate, false, postReversals); + } else { + account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, + financialYearBeginningMonth, postInterestOnDate, false, postReversals); + } + List depositAccountOnHoldTransactions = null; + if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) > 0) { + depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository + .findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account); + } + account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction, depositAccountOnHoldTransactions, + false); + account.activateAccountBasedOnBalance(); + savingsAccountRepository.saveAndFlush(account); + + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds, false); + } - handleReversal(account, savingsAccountTransactions, backdatedTxnsAllowedTill); + private void throwValidationExceptionForActiveStatus(final String actionName) { + final List dataValidationErrors = new ArrayList<>(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(SAVINGS_ACCOUNT_RESOURCE_NAME + actionName); + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("account.is.not.active"); + throw new PlatformApiDataValidationException(dataValidationErrors); } + @Override + public void checkClientOrGroupActive(final SavingsAccount account) { + final Client client = account.getClient(); + if (client != null) { + if (client.isNotActive()) { + throw new ClientNotActiveException(client.getId()); + } + } + final Group group = account.group(); + if (group != null) { + if (group.isNotActive()) { + throw new GroupNotActiveException(group.getId()); + } + } + } } diff --git a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainService.java b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainService.java index e705e98422a..8a43542fd2b 100644 --- a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainService.java +++ b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainService.java @@ -54,4 +54,8 @@ void postInterest(SavingsAccount account, MathContext mc, LocalDate interestPost void reverseTransfer(SavingsAccountTransaction savingsTransaction, boolean backdatedTxnsAllowedTill); + void undoTransaction(SavingsAccount account, SavingsAccountTransaction savingsAccountTransaction); + + void checkClientOrGroupActive(SavingsAccount account); + }