diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java index 73761fbcc..14b92f646 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java @@ -116,4 +116,6 @@ public interface ConfigurationDomainService { boolean isBusinessDateEnabled(); boolean isCOBDateAdjustmentEnabled(); + + boolean isReversalTransactionAllowed(); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java index 6dd1a1ab8..d77071f55 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java @@ -444,4 +444,11 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService public boolean isCOBDateAdjustmentEnabled() { return getGlobalConfigurationPropertyData(ENABLE_AUTOMATIC_COB_DATE_ADJUSTMENT).isEnabled(); } + + @Override + public boolean isReversalTransactionAllowed() { + final String propertyName = "enable-post-reversal-txns-for-reverse-transactions"; + final GlobalConfigurationPropertyData property = getGlobalConfigurationPropertyData(propertyName); + return property.isEnabled(); + } } 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 85f49ef40..1f1d09bc5 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 @@ -140,7 +140,6 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService .isSavingsInterestPostingAtCurrentPeriodEnd(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); - boolean isAccountTransfer = false; final boolean isPreMatureClosure = false; final MathContext mc = MathContext.DECIMAL64; @@ -348,7 +347,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService .isSavingsInterestPostingAtCurrentPeriodEnd(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); - + final boolean postReversals = false; boolean isRegularTransaction = false; boolean isAccountTransfer = false; final boolean isPreMatureClosure = false; @@ -364,7 +363,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale); final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName); Long savingsTransactionId = null; - account.postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, closedDate); + account.postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, closedDate, postReversals); final BigDecimal transactionAmount = account.getAccountBalance(); final Integer onAccountClosureId = command.integerValueOfParameterNamed(onAccountClosureIdParamName); final DepositAccountOnClosureType onClosureType = DepositAccountOnClosureType.fromInt(onAccountClosureId); @@ -385,7 +384,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi Integer frequency = CalendarUtils.getInterval(calendar.getRecurrence()); frequency = frequency == -1 ? 1 : frequency; reinvestedDeposit.generateSchedule(frequencyType, frequency, calendar); - reinvestedDeposit.processAccountUponActivation(fmt, user); + reinvestedDeposit.processAccountUponActivation(fmt, user, postReversals); reinvestedDeposit.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); this.savingsAccountRepository.save(reinvestedDeposit); @@ -520,7 +519,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService .isSavingsInterestPostingAtCurrentPeriodEnd(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); - + final boolean postReversals = false; boolean isAccountTransfer = false; final boolean isPreMatureClosure = true; boolean isRegularTransaction = false; @@ -537,7 +536,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi Long savingsTransactionId = null; // post interest account.postPreMaturityInterest(closedDate, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, - financialYearBeginningMonth); + financialYearBeginningMonth, postReversals); final Integer closureTypeValue = command.integerValueOfParameterNamed(DepositsApiConstants.onAccountClosureIdParamName); DepositAccountOnClosureType closureType = DepositAccountOnClosureType.fromInt(closureTypeValue); 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 a306b3a9b..60eec4dc7 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 @@ -39,6 +39,7 @@ import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.OneToOne; import javax.persistence.Transient; +import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.ApiParameterError; import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; @@ -77,6 +78,8 @@ public class FixedDepositAccount extends SavingsAccount { @Transient protected InterestRateChartAssembler chartAssembler; + @Transient + private ConfigurationDomainService configurationDomainService; protected FixedDepositAccount() { // 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 98e1a4823..7d59e1825 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 @@ -270,11 +270,11 @@ public class RecurringDepositAccount extends SavingsAccount { } } - public void updateMaturityStatus(final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) { + public void updateMaturityStatus(final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth, + final boolean postReversals) { final List dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.updateMaturityDetailsAction); - final SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); if (!SavingsAccountStatusType.ACTIVE.hasStateOf(currentStatus)) { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("not.in.active.state"); @@ -287,7 +287,7 @@ public class RecurringDepositAccount extends SavingsAccount { if (!this.maturityDate().isAfter(todayDate)) { // update account status this.status = SavingsAccountStatusType.MATURED.getValue(); - postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, todayDate); + postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, todayDate, postReversals); } } @@ -537,7 +537,7 @@ public class RecurringDepositAccount extends SavingsAccount { return Money.of(this.currency, this.minRequiredOpeningBalance); } - protected void processAccountUponActivation(final DateTimeFormatter fmt, final AppUser user) { + protected void processAccountUponActivation(final DateTimeFormatter fmt, final AppUser user, final boolean postReversals) { final Money minRequiredOpeningBalance = Money.of(this.currency, this.minRequiredOpeningBalance); final boolean backdatedTxnsAllowedTill = false; String refNo = null; @@ -549,7 +549,6 @@ public class RecurringDepositAccount extends SavingsAccount { // update existing transactions so derived balance fields are // correct. - boolean postReversals = false; recalculateDailyBalances(Money.zero(this.currency), DateUtils.getBusinessLocalDate(), backdatedTxnsAllowedTill, postReversals); } } @@ -635,7 +634,7 @@ public class RecurringDepositAccount extends SavingsAccount { } public void postMaturityInterest(final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth, - final LocalDate closeDate) { + final LocalDate closeDate, final boolean postReversals) { LocalDate interestPostingUpToDate = maturityDate(); if (interestPostingUpToDate == null) { interestPostingUpToDate = closeDate; @@ -645,7 +644,6 @@ public class RecurringDepositAccount extends SavingsAccount { boolean isInterestTransfer = false; LocalDate postInterestOnDate = null; final boolean backdatedTxnsAllowedTill = false; - boolean postReversals = false; final List postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate.minusDays(1), isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestOnDate, backdatedTxnsAllowedTill, postReversals); @@ -690,7 +688,7 @@ public class RecurringDepositAccount extends SavingsAccount { } public void postPreMaturityInterest(final LocalDate accountCloseDate, final boolean isPreMatureClosure, - final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) { + final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth, boolean postReversals) { final Money interestPostedToDate = totalInterestPosted(); // calculate interest before one day of closure date @@ -713,7 +711,6 @@ public class RecurringDepositAccount extends SavingsAccount { } applyWithholdTaxForDepositAccounts(accountCloseDate, recalucateDailyBalance, backdatedTxnsAllowedTill); - boolean postReversals = false; if (recalucateDailyBalance) { // update existing transactions so derived balance fields are // correct. @@ -755,11 +752,11 @@ public class RecurringDepositAccount extends SavingsAccount { return interestOnMaturity; } + @Override public void postInterest(final MathContext mc, final LocalDate postingDate, final boolean isInterestTransfer, final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth, - final LocalDate postInterestAson, final boolean backdatedTxnsAllowedTill) { + final LocalDate postInterestAson, final boolean backdatedTxnsAllowedTill, final boolean postReversals) { final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate); - boolean postReversals = false; super.postInterest(mc, interestPostingUpToDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestAson, backdatedTxnsAllowedTill, postReversals); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java index 90fbb967f..79cba8ee1 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java @@ -977,7 +977,6 @@ public class SavingsAccount extends AbstractPersistableCustom { protected void recalculateDailyBalances(final Money openingAccountBalance, final LocalDate interestPostingUpToDate, final boolean backdatedTxnsAllowedTill, boolean postReversals) { - Money runningBalance = openingAccountBalance.copy(); List accountTransactionsSorted = null; @@ -2775,6 +2774,7 @@ public class SavingsAccount extends AbstractPersistableCustom { private void payActivationCharges(final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth, final AppUser user, final boolean backdatedTxnsAllowedTill) { boolean isSavingsChargeApplied = false; + boolean postReversals = false; UUID refNo = UUID.randomUUID(); for (SavingsAccountCharge savingsAccountCharge : this.charges()) { if (savingsAccountCharge.isSavingsActivation()) { @@ -2788,7 +2788,6 @@ public class SavingsAccount extends AbstractPersistableCustom { final MathContext mc = MathContext.DECIMAL64; boolean isInterestTransfer = false; LocalDate postInterestAsOnDate = null; - boolean postReversals = false; if (this.isBeforeLastPostingPeriod(getActivationLocalDate(), backdatedTxnsAllowedTill)) { final LocalDate today = DateUtils.getBusinessLocalDate(); this.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, @@ -3551,12 +3550,12 @@ public class SavingsAccount extends AbstractPersistableCustom { this.closedOnDate = DateUtils.getBusinessLocalDate(); this.closedBy = appUser; boolean postInterestAsOnDate = false; + boolean postReversals = false; LocalDate transactionDate = DateUtils.getBusinessLocalDate(); if (this.getSummary().getAccountBalance(this.getCurrency()).isGreaterThanZero()) { SavingsAccountTransaction transaction = SavingsAccountTransaction.escheat(this, transactionDate, appUser, postInterestAsOnDate); this.transactions.add(transaction); } - boolean postReversals = false; recalculateDailyBalances(Money.zero(this.currency), transactionDate, false, postReversals); this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); } 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 555585145..45436ce42 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 @@ -92,6 +92,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService .isSavingsInterestPostingAtCurrentPeriodEnd(); final Long relaxingDaysConfigForPivotDate = this.configurationDomainService.retrieveRelaxingDaysConfigForPivotDate(); + final boolean postReversals = this.configurationDomainService.isReversalTransactionAllowed(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); if (transactionBooleanValues.isRegularTransaction() && !account.allowWithdrawal()) { throw new DepositAccountTransactionNotAllowedException(account.getId(), "withdraw", account.depositAccountType()); @@ -115,7 +116,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi final MathContext mc = MathContext.DECIMAL64; final LocalDate today = DateUtils.getBusinessLocalDate(); - boolean postReversals = false; + if (account.isBeforeLastPostingPeriod(transactionDate, backdatedTxnsAllowedTill)) { account.postInterest(mc, today, transactionBooleanValues.isInterestTransfer(), isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestOnDate, backdatedTxnsAllowedTill, postReversals); @@ -202,7 +203,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi final MathContext mc = MathContext.DECIMAL64; final LocalDate today = DateUtils.getBusinessLocalDate(); - boolean postReversals = false; + boolean postReversals = this.configurationDomainService.isReversalTransactionAllowed(); if (account.isBeforeLastPostingPeriod(transactionDate, backdatedTxnsAllowedTill)) { account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestOnDate, backdatedTxnsAllowedTill, postReversals); @@ -297,6 +298,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi .isSavingsInterestPostingAtCurrentPeriodEnd(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); final Long relaxingDaysConfigForPivotDate = this.configurationDomainService.retrieveRelaxingDaysConfigForPivotDate(); + final boolean postReversals = true; final Set existingTransactionIds = new HashSet<>(); final Set existingReversedTransactionIds = new HashSet<>(); @@ -314,7 +316,9 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi chargePaidBySet = savingsAccountTransaction.getSavingsAccountChargesPaid(); reversal.getSavingsAccountChargesPaid().addAll(chargePaidBySet); account.undoTransaction(savingsAccountTransaction); - newTransactions.add(reversal); + if (postReversals) { + newTransactions.add(reversal); + } } boolean isInterestTransfer = false; @@ -322,7 +326,6 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi final LocalDate today = DateUtils.getBusinessLocalDate(); final MathContext mc = new MathContext(15, MoneyHelper.getRoundingMode()); for (SavingsAccountTransaction savingsAccountTransaction : savingsAccountTransactions) { - boolean postReversals = true; if (savingsAccountTransaction.isPostInterestCalculationRequired() && account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate(), backdatedTxnsAllowedTill)) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java index 37cb3dd44..5d5904e10 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java @@ -151,7 +151,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService .isSavingsInterestPostingAtCurrentPeriodEnd(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); - + final boolean postReversals = false; this.depositAccountTransactionDataValidator.validateActivation(command); final MathContext mc = MathContext.DECIMAL64; final FixedDepositAccount account = (FixedDepositAccount) this.depositAccountAssembler.assembleFrom(savingsId, @@ -189,7 +189,6 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo } final boolean isInterestTransfer = false; final LocalDate postInterestOnDate = null; - final boolean postReversals = false; if (activationChargeAmount.isGreaterThanZero()) { payActivationCharge(account, user); } @@ -259,7 +258,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService .isSavingsInterestPostingAtCurrentPeriodEnd(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); - + final boolean postReversals = false; this.depositAccountTransactionDataValidator.validateActivation(command); final RecurringDepositAccount account = (RecurringDepositAccount) this.depositAccountAssembler.assembleFrom(savingsId, @@ -317,11 +316,10 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo account.updateOverduePayments(overdueUptoDate); final boolean isInterestTransfer = false; final LocalDate postInterestOnDate = null; - boolean postReversals = false; if (account.isBeforeLastPostingPeriod(account.getActivationLocalDate(), false)) { final LocalDate today = DateUtils.getBusinessLocalDate(); account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, - postInterestOnDate, false); + postInterestOnDate, false, postReversals); } else { final LocalDate today = DateUtils.getBusinessLocalDate(); account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, @@ -473,7 +471,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo checkClientOrGroupActive(account); final LocalDate today = DateUtils.getBusinessLocalDate(); - boolean postReversals = false; + final boolean postReversals = false; final MathContext mc = new MathContext(15, MoneyHelper.getRoundingMode()); boolean isInterestTransfer = false; LocalDate postInterestOnDate = null; @@ -521,7 +519,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo final MathContext mc = new MathContext(10, MoneyHelper.getRoundingMode()); boolean isInterestTransfer = false; LocalDate postInterestOnDate = null; - boolean postReversals = false; + final boolean postReversals = false; account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestOnDate, false, postReversals); this.savingAccountRepositoryWrapper.saveAndFlush(account); @@ -573,11 +571,11 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo boolean isInterestTransfer = false; LocalDate postInterestOnDate = null; checkClientOrGroupActive(account); - boolean postReversals = false; + final boolean postReversals = false; if (savingsAccountTransaction.isPostInterestCalculationRequired() && account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate(), false)) { account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, - postInterestOnDate, false); + postInterestOnDate, false, postReversals); } else { account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestOnDate, false, postReversals); @@ -686,11 +684,11 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo final Long newtransactionId = saveTransactionToGenerateTransactionId(transaction); boolean isInterestTransfer = false; final LocalDate postInterestOnDate = null; - boolean postReversals = false; + final boolean postReversals = false; if (account.isBeforeLastPostingPeriod(transactionDate, false) || account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate(), false)) { account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, - postInterestOnDate, false); + postInterestOnDate, false, postReversals); } else { account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestOnDate, false, postReversals); @@ -915,7 +913,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo savingsAccount.setStatus(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue()); final MathContext mc = MathContext.DECIMAL64; boolean isInterestTransfer = false; - boolean postReversals = false; + final boolean postReversals = false; savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestOnDate, false, postReversals); @@ -947,7 +945,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo savingsAccount.office(), transferDate, user); savingsAccount.addTransaction(withdrawtransferTransaction); savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue()); - boolean postReversals = false; + final boolean postReversals = false; final MathContext mc = MathContext.DECIMAL64; boolean isInterestTransfer = false; LocalDate postInterestOnDate = null; @@ -993,7 +991,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo savingsAccount.reassignSavingsOfficer(fieldOfficer, transferDate); } boolean isInterestTransfer = false; - boolean postReversals = false; + final boolean postReversals = false; LocalDate postInterestOnDate = null; final MathContext mc = MathContext.DECIMAL64; savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, @@ -1150,7 +1148,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo boolean isInterestTransfer = false; LocalDate postInterestOnDate = null; final MathContext mc = MathContext.DECIMAL64; - boolean postReversals = false; + final boolean postReversals = false; if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate(), false)) { final LocalDate today = DateUtils.getBusinessLocalDate(); account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, @@ -1290,7 +1288,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo boolean isInterestTransfer = false; LocalDate postInterestOnDate = null; final MathContext mc = MathContext.DECIMAL64; - boolean postReversals = false; + final boolean postReversals = false; if (account.isBeforeLastPostingPeriod(transactionDate, false)) { final LocalDate today = DateUtils.getBusinessLocalDate(); account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, @@ -1321,7 +1319,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService .isSavingsInterestPostingAtCurrentPeriodEnd(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); - + final boolean postReversals = false; final SavingsAccount account = this.depositAccountAssembler.assembleFrom(depositAccountId, depositAccountType); final Set existingTransactionIds = new HashSet<>(); final Set existingReversedTransactionIds = new HashSet<>(); @@ -1356,7 +1354,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo } } else if (depositAccountType.isRecurringDeposit()) { ((RecurringDepositAccount) account).updateMaturityStatus(isSavingsInterestPostingAtCurrentPeriodEnd, - financialYearBeginningMonth); + financialYearBeginningMonth, postReversals); } this.savingAccountRepositoryWrapper.saveAndFlush(account); postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml index 86ee3e172..88f8327e1 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml @@ -45,4 +45,5 @@ + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0026_reversals_for_reversed_transactions.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0026_reversals_for_reversed_transactions.xml new file mode 100644 index 000000000..1fdbf358d --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0026_reversals_for_reversed_transactions.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java index 552e17498..6e9338731 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java @@ -2990,6 +2990,70 @@ public class ClientSavingsIntegrationTest { assertEquals("error.msg.savings.transaction.is.not.allowed", error.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE)); } + @Test + public void testReversalEntriesAfterSystemReversingTransactionWithReversalConfigOn() { + this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); + this.savingsProductHelper = new SavingsProductHelper(); + this.scheduleJobHelper = new SchedulerJobHelper(requestSpec); + GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(this.requestSpec, this.responseSpec, "46", true); + LocalDate transactionDate = LocalDate.now(Utils.getZoneIdOfTenant()).minusDays(5); + LocalDate nextTransactionDate = transactionDate.plusDays(2); + final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd MMMM yyyy"); + String startDate = formatter.format(transactionDate); + String nxtTransaction = formatter.format(nextTransactionDate); + 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, "100", startDate, CommonConstants.RESPONSE_RESOURCE_ID); + this.scheduleJobHelper.executeAndAwaitJob(jobName); + this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, "100", nxtTransaction, CommonConstants.RESPONSE_RESOURCE_ID); + + List transactions = this.savingsAccountHelper.getSavingsTransactions(savingsId); + boolean reversalFlag = false; + for (int i = 0; i < transactions.size(); i++) { + boolean isReversal = (boolean) transactions.get(i).get("isReversal"); + if (isReversal) { + reversalFlag = true; + break; + } + } + Assertions.assertTrue(reversalFlag); + } + + @Test + public void testReversalEntriesAfterSystemReversingTransactionWithReversalConfigOff() { + this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); + this.savingsProductHelper = new SavingsProductHelper(); + this.scheduleJobHelper = new SchedulerJobHelper(requestSpec); + GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(this.requestSpec, this.responseSpec, "46", false); + LocalDate transactionDate = LocalDate.now(Utils.getZoneIdOfTenant()).minusDays(5); + LocalDate nextTransactionDate = transactionDate.plusDays(2); + final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd MMMM yyyy"); + String startDate = formatter.format(transactionDate); + String nxtTransaction = formatter.format(nextTransactionDate); + 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, "100", startDate, CommonConstants.RESPONSE_RESOURCE_ID); + this.scheduleJobHelper.executeAndAwaitJob(jobName); + this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, "100", nxtTransaction, CommonConstants.RESPONSE_RESOURCE_ID); + + List transactions = this.savingsAccountHelper.getSavingsTransactions(savingsId); + boolean reversalFlag = false; + for (int i = 0; i < transactions.size(); i++) { + boolean isReversal = (boolean) transactions.get(i).get("isReversal"); + if (isReversal) { + reversalFlag = true; + break; + } + } + Assertions.assertFalse(reversalFlag); + } + private Integer createSavingsAccountDailyPostingOverdraft(final Integer clientID, final String startDate) { final Integer savingsProductID = createSavingsProductDailyPostingOverdraft(); Assertions.assertNotNull(savingsProductID); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java index 12c3e4cc3..231168ce9 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java @@ -99,8 +99,8 @@ public class GlobalConfigurationHelper { ArrayList actualGlobalConfigurations = getAllGlobalConfigurations(requestSpec, responseSpec); // There are currently 37 global configurations. - Assertions.assertEquals(40, expectedGlobalConfigurations.size()); - Assertions.assertEquals(40, actualGlobalConfigurations.size()); + Assertions.assertEquals(41, expectedGlobalConfigurations.size()); + Assertions.assertEquals(41, actualGlobalConfigurations.size()); for (int i = 0; i < expectedGlobalConfigurations.size(); i++) { @@ -456,6 +456,14 @@ public class GlobalConfigurationHelper { isAutomaticCOBDateAdjustmentEnabled.put("trapDoor", false); defaults.add(isAutomaticCOBDateAdjustmentEnabled); + HashMap isReversalTransactionAllowed = new HashMap<>(); + isReversalTransactionAllowed.put("id", 46); + isReversalTransactionAllowed.put("name", "enable-post-reversal-txns-for-reverse-transactions"); + isReversalTransactionAllowed.put("value", 0); + isReversalTransactionAllowed.put("enabled", false); + isReversalTransactionAllowed.put("trapDoor", false); + defaults.add(isReversalTransactionAllowed); + return defaults; }