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 ff2e6cf4a FINERACT-1642:Bulk reversal of related Transactions
new 74e43ab62 Merge pull request #2372 from logoutdhaval/fineract-1642
ff2e6cf4a is described below
commit ff2e6cf4af04e401735c083141413e15cb89351a
Author: Dhaval Maniyar <[email protected]>
AuthorDate: Mon Jun 20 14:09:01 2022 +0530
FINERACT-1642:Bulk reversal of related Transactions
---
.../api/SavingsAccountTransactionsApiResource.java | 8 +++
...vingsAccountTransactionsApiResourceSwagger.java | 9 +++
.../data/SavingsAccountTransactionData.java | 5 ++
.../savings/domain/FixedDepositAccount.java | 4 +-
.../savings/domain/RecurringDepositAccount.java | 4 +-
.../portfolio/savings/domain/SavingsAccount.java | 65 ++++++++++-------
.../domain/SavingsAccountDomainService.java | 3 +-
.../domain/SavingsAccountDomainServiceJpa.java | 57 ++++++++-------
.../savings/domain/SavingsAccountSummary.java | 6 +-
.../savings/domain/SavingsAccountTransaction.java | 75 ++++++++++++-------
.../SavingsAccountTransactionRepository.java | 2 +
.../SavingsAccountTransactionSummaryWrapper.java | 42 +++++------
...rseTransactionSavingsAccountCommandHandler.java | 2 +-
...countWritePlatformServiceJpaRepositoryImpl.java | 17 +++--
.../SavingsAccountWritePlatformService.java | 3 +-
...countWritePlatformServiceJpaRepositoryImpl.java | 46 +++++++-----
.../db/changelog/tenant/changelog-tenant.xml | 1 +
.../0016_changed_unique_constraint_of_ref_no.xml | 31 ++++++++
.../ClientSavingsIntegrationTest.java | 84 ++++++++++++++++++++++
.../common/savings/SavingsAccountHelper.java | 18 +++++
20 files changed, 355 insertions(+), 127 deletions(-)
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/SavingsAccountTransactionsApiResource.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/SavingsAccountTransactionsApiResource.java
index a9c8161a2..582837aa3 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/SavingsAccountTransactionsApiResource.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/SavingsAccountTransactionsApiResource.java
@@ -18,6 +18,8 @@
*/
package org.apache.fineract.portfolio.savings.api;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
@@ -182,6 +184,12 @@ public class SavingsAccountTransactionsApiResource {
@Path("{transactionId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Undo/Reverse/Modify/Release Amount transaction API",
description = "Undo/Reverse/Modify/Release Amount transaction API\n\n"
+ + "Example Requests:\n" + "\n" + "\n" +
"savingsaccounts/{savingsId}/transactions/{transactionId}?command=reverse\n" +
"\n"
+ + "Accepted command = undo, reverse, modify, releaseAmount")
+ @RequestBody(required = true, content = @Content(schema =
@Schema(implementation =
SavingsAccountTransactionsApiResourceSwagger.PostSavingsAccountBulkReversalTransactionsRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content =
@Content(array = @ArraySchema(schema = @Schema(implementation =
SavingsAccountTransactionsApiResourceSwagger.PostSavingsAccountBulkReversalTransactionsRequest.class))))
})
public String adjustTransaction(@PathParam("savingsId") final Long
savingsId, @PathParam("transactionId") final Long transactionId,
@QueryParam("command") final String commandParam, final String
apiRequestBodyAsJson) {
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/SavingsAccountTransactionsApiResourceSwagger.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/SavingsAccountTransactionsApiResourceSwagger.java
index 96e2a5347..a2ab98316 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/SavingsAccountTransactionsApiResourceSwagger.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/api/SavingsAccountTransactionsApiResourceSwagger.java
@@ -57,4 +57,13 @@ final class SavingsAccountTransactionsApiResourceSwagger {
@Schema(example = "1")
public Integer resourceId;
}
+
+ @Schema(description = "PostSavingsAccountBulkReversalTransactionsRequest")
+ public static final class
PostSavingsAccountBulkReversalTransactionsRequest {
+
+ private PostSavingsAccountBulkReversalTransactionsRequest() {}
+
+ @Schema(example = "true")
+ public String isBulk;
+ }
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionData.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionData.java
index 5afb3fb71..c96077aa5 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionData.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionData.java
@@ -948,4 +948,9 @@ public final class SavingsAccountTransactionData implements
Serializable {
this.id = id;
this.modifiedId = id;
}
+
+ public boolean isReversalTransaction() {
+ return Boolean.TRUE.equals(this.isReversal);
+ }
+
}
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 c8385ca0e..d0b43b28a 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
@@ -639,13 +639,13 @@ public class FixedDepositAccount extends SavingsAccount {
return interestOnMaturity;
}
- @Override
public void postInterest(final MathContext mc, final LocalDate
postingDate, boolean isInterestTransfer,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final
Integer financialYearBeginningMonth,
final LocalDate postInterestOnDate, final boolean
backdatedTxnsAllowedTill) {
final LocalDate interestPostingUpToDate =
interestPostingUpToDate(postingDate);
+ boolean postReversals = false;
super.postInterest(mc, interestPostingUpToDate, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
- financialYearBeginningMonth, postInterestOnDate,
backdatedTxnsAllowedTill);
+ financialYearBeginningMonth, postInterestOnDate,
backdatedTxnsAllowedTill, postReversals);
}
@Override
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 90d078ae2..b228fef3b 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
@@ -752,13 +752,13 @@ 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 interestPostingUpToDate =
interestPostingUpToDate(postingDate);
+ boolean postReversals = false;
super.postInterest(mc, interestPostingUpToDate, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
- financialYearBeginningMonth, postInterestAson,
backdatedTxnsAllowedTill);
+ financialYearBeginningMonth, postInterestAson,
backdatedTxnsAllowedTill, postReversals);
}
@Override
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 ee20f96d2..ce6ece216 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
@@ -53,6 +53,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.TimeUnit;
import javax.persistence.CascadeType;
import javax.persistence.Column;
@@ -511,7 +512,7 @@ public class SavingsAccount extends
AbstractPersistableCustom {
public void postInterest(final MathContext mc, final LocalDate
interestPostingUpToDate, final boolean isInterestTransfer,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final
Integer financialYearBeginningMonth,
- final LocalDate postInterestOnDate, final boolean
backdatedTxnsAllowedTill) {
+ final LocalDate postInterestOnDate, final boolean
backdatedTxnsAllowedTill, final boolean postReversals) {
final List<PostingPeriod> postingPeriods = calculateInterestUsing(mc,
interestPostingUpToDate, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth, postInterestOnDate, backdatedTxnsAllowedTill);
@@ -570,6 +571,10 @@ public class SavingsAccount extends
AbstractPersistableCustom {
if (correctionRequired) {
boolean applyWithHoldTaxForOldTransaction = false;
postingTransaction.reverse();
+ SavingsAccountTransaction reversal = null;
+ if (postReversals) {
+ reversal =
SavingsAccountTransaction.reversal(postingTransaction);
+ }
final SavingsAccountTransaction withholdTransaction =
findTransactionFor(interestPostingTransactionDate,
withholdTransactions);
if (withholdTransaction != null) {
@@ -588,8 +593,14 @@ public class SavingsAccount extends
AbstractPersistableCustom {
}
if (backdatedTxnsAllowedTill) {
addTransactionToExisting(newPostingTransaction);
+ if (reversal != null) {
+ addTransactionToExisting(reversal);
+ }
} else {
addTransaction(newPostingTransaction);
+ if (reversal != null) {
+ addTransaction(reversal);
+ }
}
if (applyWithHoldTaxForOldTransaction) {
createWithHoldTransaction(interestEarnedToBePostedForPeriod.getAmount(),
interestPostingTransactionDate,
@@ -1166,7 +1177,7 @@ public class SavingsAccount extends
AbstractPersistableCustom {
}
public SavingsAccountTransaction withdraw(final
SavingsAccountTransactionDTO transactionDTO, final boolean applyWithdrawFee,
- final boolean backdatedTxnsAllowedTill) {
+ final boolean backdatedTxnsAllowedTill, String refNo) {
if (!isTransactionsAllowed()) {
@@ -1222,13 +1233,13 @@ public class SavingsAccount extends
AbstractPersistableCustom {
if (applyWithdrawFee) {
// auto pay withdrawal fee
payWithdrawalFee(transactionDTO.getTransactionAmount(),
transactionDTO.getTransactionDate(), transactionDTO.getAppUser(),
- transactionDTO.getPaymentDetail(),
backdatedTxnsAllowedTill);
+ transactionDTO.getPaymentDetail(),
backdatedTxnsAllowedTill, refNo);
}
final Money transactionAmountMoney = Money.of(this.currency,
transactionDTO.getTransactionAmount());
final SavingsAccountTransaction transaction =
SavingsAccountTransaction.withdrawal(this, office(),
transactionDTO.getPaymentDetail(),
transactionDTO.getTransactionDate(), transactionAmountMoney,
- transactionDTO.getCreatedDate(), transactionDTO.getAppUser());
+ transactionDTO.getCreatedDate(), transactionDTO.getAppUser(),
refNo);
if (backdatedTxnsAllowedTill) {
addTransactionToExisting(transaction);
@@ -1256,7 +1267,7 @@ public class SavingsAccount extends
AbstractPersistableCustom {
}
private void payWithdrawalFee(final BigDecimal transactionAmount, final
LocalDate transactionDate, final AppUser user,
- final PaymentDetail paymentDetail, final boolean
backdatedTxnsAllowedTill) {
+ final PaymentDetail paymentDetail, final boolean
backdatedTxnsAllowedTill, final String refNo) {
for (SavingsAccountCharge charge : this.charges()) {
if (charge.isWithdrawalFee() && charge.isActive()) {
@@ -1268,23 +1279,23 @@ public class SavingsAccount extends
AbstractPersistableCustom {
if (charge.isEnablePaymentType() &&
charge.isEnableFreeWithdrawal()) { // discount transaction to
// specific paymentType
if
(paymentDetail.getPaymentType().getPaymentName().equals(charge.getCharge().getPaymentType().getPaymentName()))
{
- resetFreeChargeDaysCount(charge, transactionAmount,
transactionDate, user);
+ resetFreeChargeDaysCount(charge, transactionAmount,
transactionDate, user, refNo);
}
} else if (charge.isEnablePaymentType()) { // normal
charge-transaction to specific paymentType
if
(paymentDetail.getPaymentType().getPaymentName().equals(charge.getCharge().getPaymentType().getPaymentName()))
{
charge.updateWithdralFeeAmount(transactionAmount);
this.payCharge(charge,
charge.getAmountOutstanding(this.getCurrency()), transactionDate, user,
- backdatedTxnsAllowedTill);
+ backdatedTxnsAllowedTill, refNo);
}
} else if (!charge.isEnablePaymentType() &&
charge.isEnableFreeWithdrawal()) { // discount transaction
// irrespective of
// PaymentTypes.
- resetFreeChargeDaysCount(charge, transactionAmount,
transactionDate, user);
+ resetFreeChargeDaysCount(charge, transactionAmount,
transactionDate, user, refNo);
} else { // normal-withdraw
charge.updateWithdralFeeAmount(transactionAmount);
- this.payCharge(charge,
charge.getAmountOutstanding(this.getCurrency()), transactionDate, user,
- backdatedTxnsAllowedTill);
+ this.payCharge(charge,
charge.getAmountOutstanding(this.getCurrency()), transactionDate, user,
backdatedTxnsAllowedTill,
+ refNo);
}
}
@@ -1293,7 +1304,7 @@ public class SavingsAccount extends
AbstractPersistableCustom {
}
private void resetFreeChargeDaysCount(SavingsAccountCharge charge, final
BigDecimal transactionAmount, final LocalDate transactionDate,
- final AppUser user) {
+ final AppUser user, final String refNo) {
Date resetDate = charge.getResetChargeDate();
Integer restartPeriod = charge.getRestartFrequency();
@@ -1319,7 +1330,7 @@ public class SavingsAccount extends
AbstractPersistableCustom {
YearMonth gapYearMonth = YearMonth.from(gapIntervalMonth);
YearMonth localYearMonth = YearMonth.from(localDate);
if (localYearMonth.isBefore(gapYearMonth)) {
- countValidation(charge, transactionAmount, transactionDate,
user);
+ countValidation(charge, transactionAmount, transactionDate,
user, refNo);
} else {
discountCharge(1, charge);
}
@@ -1338,7 +1349,7 @@ public class SavingsAccount extends
AbstractPersistableCustom {
Integer totalDays = days.intValue();
if (totalDays < restartPeriod) {
- countValidation(charge, transactionAmount, transactionDate,
user);
+ countValidation(charge, transactionAmount, transactionDate,
user, refNo);
} else {
discountCharge(1, charge);
}
@@ -1346,7 +1357,7 @@ public class SavingsAccount extends
AbstractPersistableCustom {
}
private void countValidation(SavingsAccountCharge charge, final BigDecimal
transactionAmount, final LocalDate transactionDate,
- final AppUser user) {
+ final AppUser user, final String refNo) {
boolean backdatedTxnsAllowedTill = false;
if (charge.getFreeWithdrawalCount() <
charge.getFrequencyFreeWithdrawalCharge()) {
final Integer count = charge.getFreeWithdrawalCount() + 1;
@@ -1354,7 +1365,7 @@ public class SavingsAccount extends
AbstractPersistableCustom {
charge.updateNoWithdrawalFee();
} else {
charge.updateWithdralFeeAmount(transactionAmount);
- this.payCharge(charge,
charge.getAmountOutstanding(this.getCurrency()), transactionDate, user,
backdatedTxnsAllowedTill);
+ this.payCharge(charge,
charge.getAmountOutstanding(this.getCurrency()), transactionDate, user,
backdatedTxnsAllowedTill, refNo);
}
}
@@ -2743,11 +2754,12 @@ public class SavingsAccount extends
AbstractPersistableCustom {
private void payActivationCharges(final boolean
isSavingsInterestPostingAtCurrentPeriodEnd, final Integer
financialYearBeginningMonth,
final AppUser user, final boolean backdatedTxnsAllowedTill) {
boolean isSavingsChargeApplied = false;
+ UUID refNo = UUID.randomUUID();
for (SavingsAccountCharge savingsAccountCharge : this.charges()) {
if (savingsAccountCharge.isSavingsActivation()) {
isSavingsChargeApplied = true;
payCharge(savingsAccountCharge,
savingsAccountCharge.getAmountOutstanding(getCurrency()),
getActivationLocalDate(), user,
- backdatedTxnsAllowedTill);
+ backdatedTxnsAllowedTill, refNo.toString());
}
}
@@ -2757,8 +2769,9 @@ public class SavingsAccount extends
AbstractPersistableCustom {
LocalDate postInterestAsOnDate = null;
if (this.isBeforeLastPostingPeriod(getActivationLocalDate(),
backdatedTxnsAllowedTill)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
+ boolean postReversals = false;
this.postInterest(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
- postInterestAsOnDate, backdatedTxnsAllowedTill);
+ postInterestAsOnDate, backdatedTxnsAllowedTill,
postReversals);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
this.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
@@ -3106,8 +3119,8 @@ public class SavingsAccount extends
AbstractPersistableCustom {
}
public SavingsAccountTransaction payCharge(final SavingsAccountCharge
savingsAccountCharge, final BigDecimal amountPaid,
- final LocalDate transactionDate, final DateTimeFormatter
formatter, final AppUser user,
- final boolean backdatedTxnsAllowedTill) {
+ final LocalDate transactionDate, final DateTimeFormatter
formatter, final AppUser user, final boolean backdatedTxnsAllowedTill,
+ final String refNo) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new
DataValidatorBuilder(dataValidationErrors)
@@ -3195,21 +3208,21 @@ public class SavingsAccount extends
AbstractPersistableCustom {
}
}
- return this.payCharge(savingsAccountCharge, chargePaid,
transactionDate, user, backdatedTxnsAllowedTill);
+ return this.payCharge(savingsAccountCharge, chargePaid,
transactionDate, user, backdatedTxnsAllowedTill, refNo);
}
public SavingsAccountTransaction payCharge(final SavingsAccountCharge
savingsAccountCharge, final Money amountPaid,
- final LocalDate transactionDate, final AppUser user, final boolean
backdatedTxnsAllowedTill) {
+ final LocalDate transactionDate, final AppUser user, final boolean
backdatedTxnsAllowedTill, String refNo) {
savingsAccountCharge.pay(getCurrency(), amountPaid);
- return handlePayChargeTransactions(savingsAccountCharge, amountPaid,
transactionDate, user, backdatedTxnsAllowedTill);
+ return handlePayChargeTransactions(savingsAccountCharge, amountPaid,
transactionDate, user, backdatedTxnsAllowedTill, refNo);
}
private SavingsAccountTransaction
handlePayChargeTransactions(SavingsAccountCharge savingsAccountCharge, Money
transactionAmount,
- final LocalDate transactionDate, final AppUser user, final boolean
backdatedTxnsAllowedTill) {
+ final LocalDate transactionDate, final AppUser user, final boolean
backdatedTxnsAllowedTill, final String refNo) {
SavingsAccountTransaction chargeTransaction = null;
if (savingsAccountCharge.isWithdrawalFee()) {
- chargeTransaction = SavingsAccountTransaction.withdrawalFee(this,
office(), transactionDate, transactionAmount, user);
+ chargeTransaction = SavingsAccountTransaction.withdrawalFee(this,
office(), transactionDate, transactionAmount, user, refNo);
} else if (savingsAccountCharge.isAnnualFee()) {
chargeTransaction = SavingsAccountTransaction.annualFee(this,
office(), transactionDate, transactionAmount, user);
} else {
@@ -3502,7 +3515,9 @@ public class SavingsAccount extends
AbstractPersistableCustom {
for (SavingsAccountCharge charge : this.charges()) {
if (charge.isSavingsNoActivity() && charge.isActive()) {
charge.updateWithdralFeeAmount(this.getAccountBalance());
- this.payCharge(charge,
charge.getAmountOutstanding(this.getCurrency()), transactionDate, appUser,
backdatedTxnsAllowedTill);
+ UUID refNo = UUID.randomUUID();
+ this.payCharge(charge,
charge.getAmountOutstanding(this.getCurrency()), transactionDate, appUser,
backdatedTxnsAllowedTill,
+ refNo.toString());
}
}
recalculateDailyBalances(Money.zero(this.currency), transactionDate,
backdatedTxnsAllowedTill);
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainService.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainService.java
index 58e9ff875..06f0db995 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainService.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainService.java
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.savings.domain;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
+import java.util.List;
import java.util.Set;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
import org.apache.fineract.portfolio.savings.SavingsTransactionBooleanValues;
@@ -42,7 +43,7 @@ public interface SavingsAccountDomainService {
SavingsAccountTransaction handleDividendPayout(SavingsAccount account,
LocalDate transactionDate, BigDecimal transactionAmount,
boolean backdatedTxnsAllowedTill);
- SavingsAccountTransaction handleReversal(SavingsAccount account,
SavingsAccountTransaction savingsAccountTransaction,
+ SavingsAccountTransaction handleReversal(SavingsAccount account,
List<SavingsAccountTransaction> savingsAccountTransactions,
boolean backdatedTxnsAllowedTill);
SavingsAccountTransaction handleHold(SavingsAccount account, AppUser
createdUser, BigDecimal amount, LocalDate transactionDate,
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 d25b94566..17f979c62 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
@@ -28,6 +28,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.UUID;
import
org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
import
org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.service.DateUtils;
@@ -108,14 +109,16 @@ public class SavingsAccountDomainServiceJpa implements
SavingsAccountDomainServi
Integer accountType = null;
final SavingsAccountTransactionDTO transactionDTO = new
SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
paymentDetail, new Date(), user, accountType);
+ UUID refNo = UUID.randomUUID();
final SavingsAccountTransaction withdrawal =
account.withdraw(transactionDTO, transactionBooleanValues.isApplyWithdrawFee(),
- backdatedTxnsAllowedTill);
+ backdatedTxnsAllowedTill, refNo.toString());
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(transactionDate,
backdatedTxnsAllowedTill)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
+ boolean postReversals = false;
account.postInterest(mc, today,
transactionBooleanValues.isInterestTransfer(),
isSavingsInterestPostingAtCurrentPeriodEnd,
- financialYearBeginningMonth, postInterestOnDate,
backdatedTxnsAllowedTill);
+ financialYearBeginningMonth, postInterestOnDate,
backdatedTxnsAllowedTill, postReversals);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today,
transactionBooleanValues.isInterestTransfer(),
@@ -195,8 +198,9 @@ public class SavingsAccountDomainServiceJpa implements
SavingsAccountDomainServi
if (account.isBeforeLastPostingPeriod(transactionDate,
backdatedTxnsAllowedTill)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
+ boolean postReversals = false;
account.postInterest(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
- postInterestOnDate, backdatedTxnsAllowedTill);
+ postInterestOnDate, backdatedTxnsAllowedTill,
postReversals);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
@@ -283,15 +287,12 @@ public class SavingsAccountDomainServiceJpa implements
SavingsAccountDomainServi
}
@Override
- public SavingsAccountTransaction handleReversal(SavingsAccount account,
SavingsAccountTransaction savingsAccountTransaction,
+ public SavingsAccountTransaction handleReversal(SavingsAccount account,
List<SavingsAccountTransaction> savingsAccountTransactions,
boolean backdatedTxnsAllowedTill) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd =
this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth =
this.configurationDomainService.retrieveFinancialYearBeginningMonth();
-
- final Set<SavingsAccountChargePaidBy> chargePaidBySet =
savingsAccountTransaction.getSavingsAccountChargesPaid();
-
final Set<Long> existingTransactionIds = new HashSet<>();
final Set<Long> existingReversedTransactionIds = new HashSet<>();
@@ -300,30 +301,36 @@ public class SavingsAccountDomainServiceJpa implements
SavingsAccountDomainServi
} else {
updateExistingTransactionsDetails(account, existingTransactionIds,
existingReversedTransactionIds);
}
-
- SavingsAccountTransaction reversal =
SavingsAccountTransaction.reversal(savingsAccountTransaction);
- reversal.getSavingsAccountChargesPaid().addAll(chargePaidBySet);
- account.undoTransaction(savingsAccountTransaction);
+ List<SavingsAccountTransaction> newTransactions = new ArrayList<>();
+ SavingsAccountTransaction reversal = null;
+
+ Set<SavingsAccountChargePaidBy> chargePaidBySet = null;
+ for (SavingsAccountTransaction savingsAccountTransaction :
savingsAccountTransactions) {
+ reversal =
SavingsAccountTransaction.reversal(savingsAccountTransaction);
+ chargePaidBySet =
savingsAccountTransaction.getSavingsAccountChargesPaid();
+ reversal.getSavingsAccountChargesPaid().addAll(chargePaidBySet);
+ account.undoTransaction(savingsAccountTransaction);
+ newTransactions.add(reversal);
+ }
boolean isInterestTransfer = false;
LocalDate postInterestOnDate = null;
final LocalDate today = DateUtils.getLocalDateOfTenant();
final MathContext mc = new MathContext(15,
MoneyHelper.getRoundingMode());
-
- if (savingsAccountTransaction.isPostInterestCalculationRequired()
- &&
account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate(),
backdatedTxnsAllowedTill)) {
- account.postInterest(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
- postInterestOnDate, backdatedTxnsAllowedTill);
- } else {
- account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
- financialYearBeginningMonth, postInterestOnDate,
backdatedTxnsAllowedTill);
+ for (SavingsAccountTransaction savingsAccountTransaction :
savingsAccountTransactions) {
+ if (savingsAccountTransaction.isPostInterestCalculationRequired()
+ &&
account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate(),
backdatedTxnsAllowedTill)) {
+ boolean postReversals = true;
+ account.postInterest(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
+ postInterestOnDate, backdatedTxnsAllowedTill,
postReversals);
+ } else {
+ account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
+ financialYearBeginningMonth, postInterestOnDate,
backdatedTxnsAllowedTill);
+ }
+
+
account.validateAccountBalanceDoesNotBecomeNegativeMinimal(savingsAccountTransaction.getAmount(),
false);
+ account.activateAccountBasedOnBalance();
}
-
- List<SavingsAccountTransaction> newTransactions = new ArrayList<>();
- newTransactions.add(reversal);
-
-
account.validateAccountBalanceDoesNotBecomeNegativeMinimal(savingsAccountTransaction.getAmount(),
false);
- account.activateAccountBasedOnBalance();
this.savingsAccountRepository.save(account);
this.savingsAccountTransactionRepository.saveAll(newTransactions);
postJournalEntries(account, existingTransactionIds,
existingReversedTransactionIds, false, backdatedTxnsAllowedTill);
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountSummary.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountSummary.java
index 8eb54625c..a32afb6b7 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountSummary.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountSummary.java
@@ -211,7 +211,8 @@ public final class SavingsAccountSummary {
HashMap<String, Money> map = new HashMap<>();
for (int i = savingsAccountTransactions.size() - 1; i >= 0; i--) {
final SavingsAccountTransaction savingsAccountTransaction =
savingsAccountTransactions.get(i);
- if (savingsAccountTransaction.isInterestPostingAndNotReversed() &&
savingsAccountTransaction.isNotReversed() && !isUpdated) {
+ if (savingsAccountTransaction.isInterestPostingAndNotReversed() &&
savingsAccountTransaction.isNotReversed()
+ && !savingsAccountTransaction.isReversalTransaction() &&
!isUpdated) {
setRunningBalanceOnPivotDate(savingsAccountTransaction.getRunningBalance(currency).getAmount());
setInterestPostedTillDate(savingsAccountTransaction.getLastTransactionDate());
isUpdated = true;
@@ -220,7 +221,8 @@ public final class SavingsAccountSummary {
}
}
if (backdatedTxnsAllowedTill) {
- if
(savingsAccountTransaction.isInterestPostingAndNotReversed() &&
savingsAccountTransaction.isNotReversed()) {
+ if
(savingsAccountTransaction.isInterestPostingAndNotReversed() &&
savingsAccountTransaction.isNotReversed()
+ && !savingsAccountTransaction.isReversalTransaction())
{
interestTotal =
interestTotal.plus(savingsAccountTransaction.getAmount(currency));
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java
index b6e420fe8..3362d4ebe 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java
@@ -143,6 +143,9 @@ public final class SavingsAccountTransaction extends
AbstractPersistableCustom {
@Column(name = "is_lien_transaction")
private Boolean lienTransaction;
+ @Column(name = "ref_no", nullable = true)
+ private String refNo;
+
SavingsAccountTransaction() {
this.dateOf = null;
this.typeOf = null;
@@ -154,8 +157,9 @@ public final class SavingsAccountTransaction extends
AbstractPersistableCustom {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
+ final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office,
paymentDetail, SavingsAccountTransactionType.DEPOSIT.getValue(), date,
- createdDate, amount, isReversed, appUser, isManualTransaction,
lienTransaction);
+ createdDate, amount, isReversed, appUser, isManualTransaction,
lienTransaction, refNo);
}
public static SavingsAccountTransaction deposit(final SavingsAccount
savingsAccount, final Office office,
@@ -164,42 +168,46 @@ public final class SavingsAccountTransaction extends
AbstractPersistableCustom {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
+ final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office,
paymentDetail, savingsAccountTransactionType.getValue(), date,
- createdDate, amount, isReversed, appUser, isManualTransaction,
lienTransaction);
+ createdDate, amount, isReversed, appUser, isManualTransaction,
lienTransaction, refNo);
}
public static SavingsAccountTransaction withdrawal(final SavingsAccount
savingsAccount, final Office office,
- final PaymentDetail paymentDetail, final LocalDate date, final
Money amount, Date createdDate, final AppUser appUser) {
+ final PaymentDetail paymentDetail, final LocalDate date, final
Money amount, Date createdDate, final AppUser appUser,
+ final String refNo) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office,
paymentDetail, SavingsAccountTransactionType.WITHDRAWAL.getValue(),
- date, createdDate, amount, isReversed, appUser,
isManualTransaction, lienTransaction);
+ date, createdDate, amount, isReversed, appUser,
isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction interestPosting(final
SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final boolean isManualTransaction) {
final boolean isReversed = false;
final Boolean lienTransaction = false;
+ final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office,
SavingsAccountTransactionType.INTEREST_POSTING.getValue(), date,
- amount, isReversed, null, isManualTransaction,
lienTransaction);
+ amount, isReversed, null, isManualTransaction,
lienTransaction, refNo);
}
public static SavingsAccountTransaction overdraftInterest(final
SavingsAccount savingsAccount, final Office office,
final LocalDate date, final Money amount, final boolean
isManualTransaction) {
final boolean isReversed = false;
final Boolean lienTransaction = false;
+ final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office,
SavingsAccountTransactionType.OVERDRAFT_INTEREST.getValue(), date,
- amount, isReversed, null, isManualTransaction,
lienTransaction);
+ amount, isReversed, null, isManualTransaction,
lienTransaction, refNo);
}
public static SavingsAccountTransaction withdrawalFee(final SavingsAccount
savingsAccount, final Office office, final LocalDate date,
- final Money amount, final AppUser appUser) {
+ final Money amount, final AppUser appUser, final String refNo) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office,
SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue(), date, amount,
- isReversed, appUser, isManualTransaction, lienTransaction);
+ isReversed, appUser, isManualTransaction, lienTransaction,
refNo);
}
public static SavingsAccountTransaction annualFee(final SavingsAccount
savingsAccount, final Office office, final LocalDate date,
@@ -207,8 +215,9 @@ public final class SavingsAccountTransaction extends
AbstractPersistableCustom {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
+ final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office,
SavingsAccountTransactionType.ANNUAL_FEE.getValue(), date, amount,
- isReversed, appUser, isManualTransaction, lienTransaction);
+ isReversed, appUser, isManualTransaction, lienTransaction,
refNo);
}
public static SavingsAccountTransaction charge(final SavingsAccount
savingsAccount, final Office office, final LocalDate date,
@@ -216,8 +225,9 @@ public final class SavingsAccountTransaction extends
AbstractPersistableCustom {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
+ final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office,
SavingsAccountTransactionType.PAY_CHARGE.getValue(), date, amount,
- isReversed, appUser, isManualTransaction, lienTransaction);
+ isReversed, appUser, isManualTransaction, lienTransaction,
refNo);
}
public static SavingsAccountTransaction from(final Integer
transactionTypeEnum, final LocalDate transactionDate,
@@ -233,8 +243,9 @@ public final class SavingsAccountTransaction extends
AbstractPersistableCustom {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
+ final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office,
SavingsAccountTransactionType.WAIVE_CHARGES.getValue(), date, amount,
- isReversed, appUser, isManualTransaction, lienTransaction);
+ isReversed, appUser, isManualTransaction, lienTransaction,
refNo);
}
public static SavingsAccountTransaction initiateTransfer(final
SavingsAccount savingsAccount, final Office office, final LocalDate date,
@@ -243,9 +254,10 @@ public final class SavingsAccountTransaction extends
AbstractPersistableCustom {
final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false;
+ final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office,
paymentDetail,
SavingsAccountTransactionType.INITIATE_TRANSFER.getValue(),
date, new Date(),
- savingsAccount.getSummary().getAccountBalance(), isReversed,
appUser, isManualTransaction, lienTransaction);
+ savingsAccount.getSummary().getAccountBalance(), isReversed,
appUser, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction approveTransfer(final
SavingsAccount savingsAccount, final Office office, final LocalDate date,
@@ -254,9 +266,10 @@ public final class SavingsAccountTransaction extends
AbstractPersistableCustom {
final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false;
+ final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office,
paymentDetail,
SavingsAccountTransactionType.APPROVE_TRANSFER.getValue(),
date, new Date(),
- savingsAccount.getSummary().getAccountBalance(), isReversed,
appUser, isManualTransaction, lienTransaction);
+ savingsAccount.getSummary().getAccountBalance(), isReversed,
appUser, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction withdrawTransfer(final
SavingsAccount savingsAccount, final Office office, final LocalDate date,
@@ -265,9 +278,10 @@ public final class SavingsAccountTransaction extends
AbstractPersistableCustom {
final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false;
+ final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office,
paymentDetail,
SavingsAccountTransactionType.WITHDRAW_TRANSFER.getValue(),
date, new Date(),
- savingsAccount.getSummary().getAccountBalance(), isReversed,
appUser, isManualTransaction, lienTransaction);
+ savingsAccount.getSummary().getAccountBalance(), isReversed,
appUser, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction withHoldTax(final SavingsAccount
savingsAccount, final Office office, final LocalDate date,
@@ -275,9 +289,10 @@ public final class SavingsAccountTransaction extends
AbstractPersistableCustom {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
+ final String refNo = null;
SavingsAccountTransaction accountTransaction = new
SavingsAccountTransaction(savingsAccount, office,
- SavingsAccountTransactionType.WITHHOLD_TAX.getValue(), date,
amount, isReversed, null, isManualTransaction,
- lienTransaction);
+ SavingsAccountTransactionType.WITHHOLD_TAX.getValue(), date,
amount, isReversed, null, isManualTransaction, lienTransaction,
+ refNo);
updateTaxDetails(taxDetails, accountTransaction);
return accountTransaction;
}
@@ -287,9 +302,10 @@ public final class SavingsAccountTransaction extends
AbstractPersistableCustom {
final boolean isReversed = false;
final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false;
+ final String refNo = null;
return new SavingsAccountTransaction(savingsAccount,
savingsAccount.office(), paymentDetail,
SavingsAccountTransactionType.ESCHEAT.getValue(), date, new
Date(), savingsAccount.getSummary().getAccountBalance(),
- isReversed, appUser, accountTransaction, lienTransaction);
+ isReversed, appUser, accountTransaction, lienTransaction,
refNo);
}
public static void updateTaxDetails(final Map<TaxComponent, BigDecimal>
taxDetails,
@@ -306,26 +322,28 @@ public final class SavingsAccountTransaction extends
AbstractPersistableCustom {
return new
SavingsAccountTransaction(accountTransaction.savingsAccount,
accountTransaction.office, accountTransaction.paymentDetail,
accountTransaction.typeOf,
accountTransaction.transactionLocalDate(), DateUtils.getDateOfTenant(),
accountTransaction.amount, accountTransaction.reversed,
accountTransaction.appUser, accountTransaction.isManualTransaction,
- accountTransaction.lienTransaction);
+ accountTransaction.lienTransaction, accountTransaction.refNo);
}
private SavingsAccountTransaction(final SavingsAccount savingsAccount,
final Office office, final Integer typeOf,
final LocalDate transactionLocalDate, final Money amount, final
boolean isReversed, final AppUser appUser,
- final boolean isManualTransaction, final Boolean lienTransaction) {
+ final boolean isManualTransaction, final Boolean lienTransaction,
final String refNo) {
this(savingsAccount, office, null, typeOf, transactionLocalDate, new
Date(), amount, isReversed, appUser, isManualTransaction,
- lienTransaction);
+ lienTransaction, refNo);
}
private SavingsAccountTransaction(final SavingsAccount savingsAccount,
final Office office, final PaymentDetail paymentDetail,
final Integer typeOf, final LocalDate transactionLocalDate, final
Date createdDate, final Money amount,
- final boolean isReversed, final AppUser appUser, final boolean
isManualTransaction, final Boolean lienTransaction) {
+ final boolean isReversed, final AppUser appUser, final boolean
isManualTransaction, final Boolean lienTransaction,
+ final String refNo) {
this(savingsAccount, office, paymentDetail, typeOf,
transactionLocalDate, createdDate, amount.getAmount(), isReversed, appUser,
- isManualTransaction, lienTransaction);
+ isManualTransaction, lienTransaction, refNo);
}
private SavingsAccountTransaction(final SavingsAccount savingsAccount,
final Office office, final PaymentDetail paymentDetail,
final Integer typeOf, final LocalDate transactionLocalDate, final
Date createdDate, final BigDecimal amount,
- final boolean isReversed, final AppUser appUser, final boolean
isManualTransaction, final Boolean lienTransaction) {
+ final boolean isReversed, final AppUser appUser, final boolean
isManualTransaction, final Boolean lienTransaction,
+ final String refNo) {
this.savingsAccount = savingsAccount;
this.office = office;
this.typeOf = typeOf;
@@ -337,6 +355,7 @@ public final class SavingsAccountTransaction extends
AbstractPersistableCustom {
this.appUser = appUser;
this.isManualTransaction = isManualTransaction;
this.lienTransaction = lienTransaction;
+ this.refNo = refNo;
}
private SavingsAccountTransaction(final Integer transactionTypeEnum, final
LocalDate transactionDate, final BigDecimal amount,
@@ -362,15 +381,17 @@ public final class SavingsAccountTransaction extends
AbstractPersistableCustom {
final Boolean lienTransaction) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
+ final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office,
paymentDetail, SavingsAccountTransactionType.AMOUNT_HOLD.getValue(),
- date, createdDate, amount, isReversed, appUser,
isManualTransaction, lienTransaction);
+ date, createdDate, amount, isReversed, appUser,
isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction
releaseAmount(SavingsAccountTransaction accountTransaction, LocalDate
transactionDate,
Date createdDate, final AppUser appUser) {
return new
SavingsAccountTransaction(accountTransaction.savingsAccount,
accountTransaction.office, accountTransaction.paymentDetail,
SavingsAccountTransactionType.AMOUNT_RELEASE.getValue(),
transactionDate, createdDate, accountTransaction.amount,
- accountTransaction.reversed, appUser,
accountTransaction.isManualTransaction, accountTransaction.lienTransaction);
+ accountTransaction.reversed, appUser,
accountTransaction.isManualTransaction, accountTransaction.lienTransaction,
+ accountTransaction.refNo);
}
public static SavingsAccountTransaction reversal(SavingsAccountTransaction
accountTransaction) {
@@ -860,6 +881,10 @@ public final class SavingsAccountTransaction extends
AbstractPersistableCustom {
return this.dateOf;
}
+ public String getRefNo() {
+ return this.refNo;
+ }
+
public PaymentDetail getPaymentDetail() {
return this.paymentDetail;
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionRepository.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionRepository.java
index cdb1d9337..a7e1b3bfc 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionRepository.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionRepository.java
@@ -42,4 +42,6 @@ public interface SavingsAccountTransactionRepository
@Lock(LockModeType.PESSIMISTIC_WRITE)
List<SavingsAccountTransaction>
findBySavingsAccount(@Param("savingsAccount") SavingsAccount savingsAccount);
+ @Query("select sat from SavingsAccountTransaction sat where sat.refNo =
:refNo")
+ List<SavingsAccountTransaction> findAllTransactionByRefNo(@Param("refNo")
String refNo);
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionSummaryWrapper.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionSummaryWrapper.java
index 1498c92cc..82e9a2de5 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionSummaryWrapper.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransactionSummaryWrapper.java
@@ -67,7 +67,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalWithdrawals(final CurrencyData currency,
final List<SavingsAccountTransactionData> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransactionData transaction : transactions) {
- if (transaction.isWithdrawal() && transaction.isNotReversed()) {
+ if (transaction.isWithdrawal() && transaction.isNotReversed() &&
!transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount());
}
}
@@ -77,7 +77,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalInterestPosted(final MonetaryCurrency
currency, final List<SavingsAccountTransaction> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransaction transaction : transactions) {
- if (transaction.isInterestPostingAndNotReversed() &&
transaction.isNotReversed()) {
+ if (transaction.isInterestPostingAndNotReversed() &&
transaction.isNotReversed() && !transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount(currency));
}
}
@@ -87,7 +87,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalInterestPosted(final CurrencyData
currency, final List<SavingsAccountTransactionData> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransactionData transaction : transactions) {
- if (transaction.isInterestPostingAndNotReversed() &&
transaction.isNotReversed()) {
+ if (transaction.isInterestPostingAndNotReversed() &&
transaction.isNotReversed() && !transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount());
}
}
@@ -98,7 +98,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
final List<SavingsAccountTransaction> savingsAccountTransactions) {
Money total = Money.of(currency, currentInterestPosted);
for (final SavingsAccountTransaction transaction :
savingsAccountTransactions) {
- if (transaction.isInterestPostingAndNotReversed() &&
transaction.isNotReversed()) {
+ if (transaction.isInterestPostingAndNotReversed() &&
transaction.isNotReversed() && !transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount(currency));
}
}
@@ -108,7 +108,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalWithdrawalFees(final MonetaryCurrency
currency, final List<SavingsAccountTransaction> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransaction transaction : transactions) {
- if (transaction.isWithdrawalFeeAndNotReversed() &&
transaction.isNotReversed()) {
+ if (transaction.isWithdrawalFeeAndNotReversed() &&
transaction.isNotReversed() && !transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount(currency));
}
}
@@ -118,7 +118,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalWithdrawalFees(final CurrencyData
currency, final List<SavingsAccountTransactionData> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransactionData transaction : transactions) {
- if (transaction.isWithdrawalFeeAndNotReversed() &&
transaction.isNotReversed()) {
+ if (transaction.isWithdrawalFeeAndNotReversed() &&
transaction.isNotReversed() && !transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount());
}
}
@@ -128,7 +128,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalAnnualFees(final MonetaryCurrency
currency, final List<SavingsAccountTransaction> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransaction transaction : transactions) {
- if (transaction.isAnnualFeeAndNotReversed() &&
transaction.isNotReversed()) {
+ if (transaction.isAnnualFeeAndNotReversed() &&
transaction.isNotReversed() && !transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount(currency));
}
}
@@ -138,7 +138,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalAnnualFees(final CurrencyData currency,
final List<SavingsAccountTransactionData> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransactionData transaction : transactions) {
- if (transaction.isAnnualFeeAndNotReversed() &&
transaction.isNotReversed()) {
+ if (transaction.isAnnualFeeAndNotReversed() &&
transaction.isNotReversed() && !transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount());
}
}
@@ -148,7 +148,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalFeesCharge(final MonetaryCurrency
currency, final List<SavingsAccountTransaction> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransaction transaction : transactions) {
- if (transaction.isFeeChargeAndNotReversed()) {
+ if (transaction.isFeeChargeAndNotReversed() &&
!transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount(currency));
}
}
@@ -158,7 +158,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalFeesCharge(final CurrencyData currency,
final List<SavingsAccountTransactionData> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransactionData transaction : transactions) {
- if (transaction.isFeeChargeAndNotReversed()) {
+ if (transaction.isFeeChargeAndNotReversed() &&
!transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount());
}
}
@@ -168,7 +168,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalFeesChargeWaived(final MonetaryCurrency
currency, final List<SavingsAccountTransaction> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransaction transaction : transactions) {
- if (transaction.isWaiveFeeChargeAndNotReversed()) {
+ if (transaction.isWaiveFeeChargeAndNotReversed() &&
!transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount(currency));
}
}
@@ -178,7 +178,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalFeesChargeWaived(final CurrencyData
currency, final List<SavingsAccountTransactionData> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransactionData transaction : transactions) {
- if (transaction.isWaiveFeeChargeAndNotReversed()) {
+ if (transaction.isWaiveFeeChargeAndNotReversed() &&
!transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount());
}
}
@@ -188,7 +188,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalPenaltyCharge(final MonetaryCurrency
currency, final List<SavingsAccountTransaction> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransaction transaction : transactions) {
- if (transaction.isPenaltyChargeAndNotReversed()) {
+ if (transaction.isPenaltyChargeAndNotReversed() &&
!transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount(currency));
}
}
@@ -198,7 +198,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalPenaltyCharge(final CurrencyData currency,
final List<SavingsAccountTransactionData> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransactionData transaction : transactions) {
- if (transaction.isPenaltyChargeAndNotReversed()) {
+ if (transaction.isPenaltyChargeAndNotReversed() &&
!transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount());
}
}
@@ -209,7 +209,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
final List<SavingsAccountTransaction> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransaction transaction : transactions) {
- if (transaction.isWaivePenaltyChargeAndNotReversed()) {
+ if (transaction.isWaivePenaltyChargeAndNotReversed() &&
!transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount(currency));
}
}
@@ -220,7 +220,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
final List<SavingsAccountTransactionData> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransactionData transaction : transactions) {
- if (transaction.isWaivePenaltyChargeAndNotReversed()) {
+ if (transaction.isWaivePenaltyChargeAndNotReversed() &&
!transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount());
}
}
@@ -231,7 +231,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
List<SavingsAccountTransaction> transactions) {
Money total = Money.of(currency, overdraftPosted);
for (final SavingsAccountTransaction transaction : transactions) {
- if (transaction.isOverdraftInterestAndNotReversed()) {
+ if (transaction.isOverdraftInterestAndNotReversed() &&
!transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount(currency));
}
}
@@ -241,7 +241,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalOverdraftInterest(MonetaryCurrency
currency, List<SavingsAccountTransaction> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransaction transaction : transactions) {
- if (transaction.isOverdraftInterestAndNotReversed()) {
+ if (transaction.isOverdraftInterestAndNotReversed() &&
!transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount(currency));
}
}
@@ -251,7 +251,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalOverdraftInterest(CurrencyData currency,
List<SavingsAccountTransactionData> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransactionData transaction : transactions) {
- if (transaction.isOverdraftInterestAndNotReversed()) {
+ if (transaction.isOverdraftInterestAndNotReversed() &&
!transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount());
}
}
@@ -261,7 +261,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalWithholdTaxWithdrawal(MonetaryCurrency
currency, List<SavingsAccountTransaction> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransaction transaction : transactions) {
- if (transaction.isWithHoldTaxAndNotReversed()) {
+ if (transaction.isWithHoldTaxAndNotReversed() &&
!transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount(currency));
}
}
@@ -271,7 +271,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
public BigDecimal calculateTotalWithholdTaxWithdrawal(CurrencyData
currency, List<SavingsAccountTransactionData> transactions) {
Money total = Money.zero(currency);
for (final SavingsAccountTransactionData transaction : transactions) {
- if (transaction.isWithHoldTaxAndNotReversed()) {
+ if (transaction.isWithHoldTaxAndNotReversed() &&
!transaction.isReversalTransaction()) {
total = total.plus(transaction.getAmount());
}
}
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/handler/ReverseTransactionSavingsAccountCommandHandler.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/handler/ReverseTransactionSavingsAccountCommandHandler.java
index 1932791ee..ec09d9804 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/handler/ReverseTransactionSavingsAccountCommandHandler.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/handler/ReverseTransactionSavingsAccountCommandHandler.java
@@ -45,6 +45,6 @@ public class ReverseTransactionSavingsAccountCommandHandler
implements NewComman
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
final Long transactionId = Long.valueOf(command.getTransactionId());
- return
this.writePlatformService.reverseTransaction(command.getSavingsId(),
transactionId, false);
+ return
this.writePlatformService.reverseTransaction(command.getSavingsId(),
transactionId, false, command);
}
}
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 75dafe97b..be46494e3 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
@@ -37,6 +37,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import
org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
import
org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
@@ -296,7 +297,7 @@ public class
DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
for (SavingsAccountCharge savingsAccountCharge : account.charges()) {
if (savingsAccountCharge.isSavingsActivation()) {
account.payCharge(savingsAccountCharge,
savingsAccountCharge.getAmount(account.getCurrency()),
- account.getActivationLocalDate(), user, false);
+ account.getActivationLocalDate(), user, false, null);
}
}
}
@@ -571,8 +572,9 @@ public class
DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final MathContext mc = new MathContext(10,
MoneyHelper.getRoundingMode());
boolean isInterestTransfer = false;
LocalDate postInterestOnDate = null;
+ boolean postReversals = false;
account.postInterest(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
- postInterestOnDate, false);
+ postInterestOnDate, false, postReversals);
this.savingAccountRepositoryWrapper.saveAndFlush(account);
postJournalEntries(account, existingTransactionIds,
existingReversedTransactionIds);
@@ -744,6 +746,7 @@ public class
DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
SavingsAccountTransaction transaction = null;
Integer accountType = null;
+ UUID refNo = UUID.randomUUID();
if (savingsAccountTransaction.isDeposit()) {
final SavingsAccountTransactionDTO transactionDTO = new
SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
paymentDetail, savingsAccountTransaction.createdDate(),
user, accountType);
@@ -751,7 +754,7 @@ public class
DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
} else {
final SavingsAccountTransactionDTO transactionDTO = new
SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
paymentDetail, savingsAccountTransaction.createdDate(),
user, accountType);
- transaction = account.withdraw(transactionDTO, true, false);
+ transaction = account.withdraw(transactionDTO, true, false,
refNo.toString());
}
final Long newtransactionId =
saveTransactionToGenerateTransactionId(transaction);
boolean isInterestTransfer = false;
@@ -1218,8 +1221,9 @@ public class
DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final MathContext mc = MathContext.DECIMAL64;
if
(account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate(),
false)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
+ boolean postReversals = false;
account.postInterest(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
- postInterestOnDate, false);
+ postInterestOnDate, false, postReversals);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
@@ -1351,14 +1355,15 @@ public class
DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final Set<Long> existingTransactionIds = new HashSet<>();
final Set<Long> existingReversedTransactionIds = new HashSet<>();
updateExistingTransactionsDetails(account, existingTransactionIds,
existingReversedTransactionIds);
- account.payCharge(savingsAccountCharge, amountPaid, transactionDate,
formatter, user, false);
+ account.payCharge(savingsAccountCharge, amountPaid, transactionDate,
formatter, user, false, null);
boolean isInterestTransfer = false;
LocalDate postInterestOnDate = null;
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(transactionDate, false)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
+ boolean postReversals = false;
account.postInterest(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
- postInterestOnDate, false);
+ postInterestOnDate, false, postReversals);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformService.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformService.java
index dab91c282..8b2979591 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformService.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformService.java
@@ -41,7 +41,8 @@ public interface SavingsAccountWritePlatformService {
CommandProcessingResult calculateInterest(Long savingsId);
- CommandProcessingResult reverseTransaction(Long savingsId, Long
transactionId, boolean allowAccountTransferModification);
+ CommandProcessingResult reverseTransaction(Long savingsId, Long
transactionId, boolean allowAccountTransferModification,
+ JsonCommand command);
CommandProcessingResult undoTransaction(Long savingsId, Long
transactionId, boolean allowAccountTransferModification);
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
index 1bd5291e4..1f700a748 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java
@@ -38,6 +38,7 @@ import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -46,6 +47,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import
org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
import
org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
@@ -614,9 +616,9 @@ public class
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
if (postInterestAs) {
postInterestOnDate = transactionDate;
}
-
+ boolean postReversals = false;
account.postInterest(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
- postInterestOnDate, backdatedTxnsAllowedTill);
+ postInterestOnDate, backdatedTxnsAllowedTill,
postReversals);
if (!backdatedTxnsAllowedTill) {
List<SavingsAccountTransaction> transactions =
account.getTransactions();
@@ -685,9 +687,10 @@ public class
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Override
public CommandProcessingResult reverseTransaction(final Long savingsId,
final Long transactionId,
- final boolean allowAccountTransferModification) {
+ final boolean allowAccountTransferModification, final JsonCommand
command) {
final boolean backdatedTxnsAllowedTill =
this.savingAccountAssembler.getPivotConfigStatus();
+ final boolean isBulk =
command.booleanPrimitiveValueOfParameterNamed("isBulk");
final SavingsAccount account =
this.savingAccountAssembler.assembleFrom(savingsId, backdatedTxnsAllowedTill);
final SavingsAccountTransaction savingsAccountTransaction =
this.savingsAccountTransactionRepository
@@ -707,18 +710,24 @@ public class
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
throw new
PlatformServiceUnavailableException("error.msg.saving.account.transaction.update.not.allowed",
"Savings account transaction:" + transactionId + " update
not allowed for this savings type", transactionId);
}
-
if (account.isNotActive()) {
throwValidationForActiveStatus(SavingsApiConstants.undoTransactionAction);
}
-
+ SavingsAccountTransaction reversal = null;
+ Long reversalId = null;
checkClientOrGroupActive(account);
-
- final SavingsAccountTransaction reversal =
this.savingsAccountDomainService.handleReversal(account,
savingsAccountTransaction,
- backdatedTxnsAllowedTill);
-
+ List<SavingsAccountTransaction> savingsAccountTransactions = null;
+ if (isBulk) {
+ String transactionRefNo = savingsAccountTransaction.getRefNo();
+ savingsAccountTransactions =
this.savingsAccountTransactionRepository.findAllTransactionByRefNo(transactionRefNo);
+ reversal =
this.savingsAccountDomainService.handleReversal(account,
savingsAccountTransactions, backdatedTxnsAllowedTill);
+ } else {
+ reversal =
this.savingsAccountDomainService.handleReversal(account,
Collections.singletonList(savingsAccountTransaction),
+ backdatedTxnsAllowedTill);
+ }
+ reversalId = reversal.getId();
return new CommandProcessingResultBuilder() //
- .withEntityId(reversal.getId()) //
+ .withEntityId(reversalId) //
.withOfficeId(account.officeId()) //
.withClientId(account.clientId()) //
.withGroupId(account.groupId()) //
@@ -781,8 +790,9 @@ public class
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
checkClientOrGroupActive(account);
if (savingsAccountTransaction.isPostInterestCalculationRequired()
&&
account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate(),
false)) {
+ boolean postReversals = false;
account.postInterest(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
- postInterestOnDate, false);
+ postInterestOnDate, false, postReversals);
} else {
account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth, postInterestOnDate, false);
@@ -875,14 +885,16 @@ public class
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
if (savingsAccountTransaction.isDeposit()) {
transaction = account.deposit(transactionDTO, false);
} else {
- transaction = account.withdraw(transactionDTO, true, false);
+ UUID refNo = UUID.randomUUID();
+ transaction = account.withdraw(transactionDTO, true, false,
refNo.toString());
}
final Long newtransactionId =
saveTransactionToGenerateTransactionId(transaction);
final LocalDate postInterestOnDate = null;
if (account.isBeforeLastPostingPeriod(transactionDate, false)
||
account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate(),
false)) {
+ boolean postReversals = false;
account.postInterest(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
- postInterestOnDate, false);
+ postInterestOnDate, false, postReversals);
} else {
account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth, postInterestOnDate, false);
@@ -1312,8 +1324,9 @@ public class
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final MathContext mc = MathContext.DECIMAL64;
if
(account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate(),
backdatedTxnsAllowedTill)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
+ boolean postReversals = false;
account.postInterest(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
- postInterestOnDate, backdatedTxnsAllowedTill);
+ postInterestOnDate, backdatedTxnsAllowedTill,
postReversals);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
@@ -1458,14 +1471,15 @@ public class
SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final Set<Long> existingReversedTransactionIds = new HashSet<>();
updateExistingTransactionsDetails(account, existingTransactionIds,
existingReversedTransactionIds);
SavingsAccountTransaction chargeTransaction =
account.payCharge(savingsAccountCharge, amountPaid, transactionDate, formatter,
user,
- backdatedTxnsAllowedTill);
+ backdatedTxnsAllowedTill, null);
boolean isInterestTransfer = false;
LocalDate postInterestOnDate = null;
final MathContext mc = MathContext.DECIMAL64;
if (account.isBeforeLastPostingPeriod(transactionDate,
backdatedTxnsAllowedTill)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
+ boolean postReversals = false;
account.postInterest(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
- postInterestOnDate, isInterestTransfer);
+ postInterestOnDate, isInterestTransfer, postReversals);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(mc, today, isInterestTransfer,
isSavingsInterestPostingAtCurrentPeriodEnd,
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 00372f771..c5d7ef7f5 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
@@ -35,4 +35,5 @@
<include file="parts/0013_remove_topics.xml"
relativeToChangelogFile="true"/>
<include file="parts/0014_remove_unused_jobs.xml"
relativeToChangelogFile="true"/>
<include file="parts/0015_add_business_date.xml"
relativeToChangelogFile="true"/>
+ <include file="parts/0016_changed_unique_constraint_of_ref_no.xml"
relativeToChangelogFile="true"/>
</databaseChangeLog>
diff --git
a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0016_changed_unique_constraint_of_ref_no.xml
b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0016_changed_unique_constraint_of_ref_no.xml
new file mode 100644
index 000000000..ec83a13c7
--- /dev/null
+++
b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0016_changed_unique_constraint_of_ref_no.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+ <changeSet author="fineract" id="1" context="postgresql">
+ <dropUniqueConstraint tableName="m_savings_account_transaction"
constraintName="m_savings_account_transaction_ref_no_key"/>
+ </changeSet>
+ <changeSet author="fineract" id="1" context="mysql">
+ <dropUniqueConstraint tableName="m_savings_account_transaction"
constraintName="ref_no"/>
+ </changeSet>
+</databaseChangeLog>
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 32a7484d2..9cc08a415 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
@@ -2731,4 +2731,88 @@ public class ClientSavingsIntegrationTest {
assertEquals(balance, summary.get("accountBalance"), "Verifying
opening Balance is 500");
}
+
+ @Test
+ public void testReversalWhenIsBulkIsTrue() {
+ this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec,
this.responseSpec);
+
+ final Integer clientID = ClientHelper.createClient(this.requestSpec,
this.responseSpec);
+ ClientHelper.verifyClientCreatedOnServer(this.requestSpec,
this.responseSpec, clientID);
+ final String minBalanceForInterestCalculation = null;
+ final String minRequiredBalance = "0";
+ final String enforceMinRequiredBalance = "false";
+ final boolean allowOverdraft = false;
+ final boolean isBulk = true;
+
+ final Integer savingsProductID =
createSavingsProduct(this.requestSpec, this.responseSpec,
MINIMUM_OPENING_BALANCE,
+ minBalanceForInterestCalculation, minRequiredBalance,
enforceMinRequiredBalance, allowOverdraft);
+ Assertions.assertNotNull(savingsProductID);
+
+ final Integer savingsId =
this.savingsAccountHelper.applyForSavingsApplication(clientID,
savingsProductID, ACCOUNT_TYPE_INDIVIDUAL);
+ Assertions.assertNotNull(savingsId);
+ final Integer withdrawalChargeId =
ChargesHelper.createCharges(this.requestSpec, this.responseSpec,
+ ChargesHelper.getSavingsWithdrawalFeeJSON());
+ Assertions.assertNotNull(withdrawalChargeId);
+
+ this.savingsAccountHelper.addChargesForSavings(savingsId,
withdrawalChargeId, false);
+
+ HashMap savingsStatusHashMap =
this.savingsAccountHelper.approveSavings(savingsId);
+ SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
+
+ savingsStatusHashMap =
this.savingsAccountHelper.activateSavings(savingsId);
+ SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
+
+ Integer withdrawalTransactionId = (Integer)
this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, "500",
+ SavingsAccountHelper.TRANSACTION_DATE,
CommonConstants.RESPONSE_RESOURCE_ID);
+ HashMap summary =
this.savingsAccountHelper.getSavingsSummary(savingsId);
+ Float balance = Float.parseFloat("400.0");
+ assertEquals(balance, summary.get("accountBalance"), "Verifying
account balance is 400");
+ LOG.info("------------------------When Bulk transaction is
true------------------------");
+ this.savingsAccountHelper.reverseSavingsAccountTransaction(savingsId,
withdrawalTransactionId, isBulk);
+ summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
+ balance = Float.parseFloat("1000.0");
+ assertEquals(balance, summary.get("accountBalance"), "Verifying
account balance is 1000");
+ }
+
+ @Test
+ public void testReversalWhenIsBulkIsFalse() {
+ this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec,
this.responseSpec);
+
+ final Integer clientID = ClientHelper.createClient(this.requestSpec,
this.responseSpec);
+ ClientHelper.verifyClientCreatedOnServer(this.requestSpec,
this.responseSpec, clientID);
+ final String minBalanceForInterestCalculation = null;
+ final String minRequiredBalance = "0";
+ final String enforceMinRequiredBalance = "false";
+ final boolean allowOverdraft = false;
+ final boolean isBulk = false;
+
+ final Integer savingsProductID =
createSavingsProduct(this.requestSpec, this.responseSpec,
MINIMUM_OPENING_BALANCE,
+ minBalanceForInterestCalculation, minRequiredBalance,
enforceMinRequiredBalance, allowOverdraft);
+ Assertions.assertNotNull(savingsProductID);
+
+ final Integer savingsId =
this.savingsAccountHelper.applyForSavingsApplication(clientID,
savingsProductID, ACCOUNT_TYPE_INDIVIDUAL);
+ Assertions.assertNotNull(savingsId);
+ final Integer withdrawalChargeId =
ChargesHelper.createCharges(this.requestSpec, this.responseSpec,
+ ChargesHelper.getSavingsWithdrawalFeeJSON());
+ Assertions.assertNotNull(withdrawalChargeId);
+
+ this.savingsAccountHelper.addChargesForSavings(savingsId,
withdrawalChargeId, false);
+
+ HashMap savingsStatusHashMap =
this.savingsAccountHelper.approveSavings(savingsId);
+ SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
+
+ savingsStatusHashMap =
this.savingsAccountHelper.activateSavings(savingsId);
+ SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
+
+ Integer withdrawalTransactionId = (Integer)
this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, "500",
+ SavingsAccountHelper.TRANSACTION_DATE,
CommonConstants.RESPONSE_RESOURCE_ID);
+ HashMap summary =
this.savingsAccountHelper.getSavingsSummary(savingsId);
+ Float balance = Float.parseFloat("400.0");
+ assertEquals(balance, summary.get("accountBalance"), "Verifying
account balance is 400");
+ LOG.info("------------------------When Bulk transaction is
false------------------------");
+ this.savingsAccountHelper.reverseSavingsAccountTransaction(savingsId,
withdrawalTransactionId, isBulk);
+ summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
+ balance = Float.parseFloat("900.0");
+ assertEquals(balance, summary.get("accountBalance"), "Verifying
account balance is 900");
+ }
}
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 90127462e..5f3016e2b 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
@@ -280,6 +280,12 @@ public class SavingsAccountHelper {
getSavingsTransactionJSON("0", LAST_TRANSACTION_DATE),
CommonConstants.RESPONSE_RESOURCE_ID);
}
+ public Integer reverseSavingsAccountTransaction(final Integer savingsId,
final Integer transactionId, final boolean isBulk) {
+ LOG.info("\n--------------------------------- REVERSE SAVINGS
TRANSACTION --------------------------------");
+ return (Integer)
performSavingActions(createAdjustTransactionURL(REVERSE_TRASACTION_COMMAND,
savingsId, transactionId),
+ getSavingsTransactionJSON("0", LAST_TRANSACTION_DATE, isBulk),
CommonConstants.RESPONSE_RESOURCE_ID);
+ }
+
public void calculateInterestForSavings(final Integer savingsId) {
LOG.info("--------------------------------- CALCULATING INTEREST FOR
SAVINGS --------------------------------");
performSavingActions(createSavingsCalculateInterestURL(CALCULATE_INTEREST_SAVINGS_COMMAND,
savingsId),
@@ -460,6 +466,18 @@ public class SavingsAccountHelper {
return savingsAccountWithdrawalJson;
}
+ private String getSavingsTransactionJSON(final String amount, final String
transactionDate, final boolean isBulk) {
+ final HashMap<String, String> map = new HashMap<>();
+ map.put("locale", CommonConstants.LOCALE);
+ map.put("dateFormat", CommonConstants.DATE_FORMAT);
+ map.put("transactionDate", transactionDate);
+ map.put("transactionAmount", amount);
+ map.put("isBulk", String.valueOf(isBulk));
+ String savingsAccountWithdrawalJson = new Gson().toJson(map);
+ LOG.info(savingsAccountWithdrawalJson);
+ return savingsAccountWithdrawalJson;
+ }
+
private String getLienSavingsTransactionJSON(final String amount, final
String transactionDate, final Boolean lienAllowed) {
final HashMap<String, Object> map = new HashMap<>();
map.put("locale", CommonConstants.LOCALE);