FINERACT-1510-reversal-transaction-posting (#2046)
This commit is contained in:
+11
@@ -1466,6 +1466,17 @@ public class CommandWrapperBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder reverseSavingsAccountTransaction(final Long accountId, final Long transactionId) {
|
||||||
|
this.actionName = "REVERSETRANSACTION";
|
||||||
|
this.entityName = "SAVINGSACCOUNT";
|
||||||
|
this.savingsId = accountId;
|
||||||
|
this.entityId = accountId;
|
||||||
|
this.subentityId = transactionId;
|
||||||
|
this.transactionId = transactionId.toString();
|
||||||
|
this.href = "/savingsaccounts/" + accountId + "/transactions/" + transactionId + "?command=reverse";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public CommandWrapperBuilder adjustSavingsAccountTransaction(final Long accountId, final Long transactionId) {
|
public CommandWrapperBuilder adjustSavingsAccountTransaction(final Long accountId, final Long transactionId) {
|
||||||
this.actionName = "ADJUSTTRANSACTION";
|
this.actionName = "ADJUSTTRANSACTION";
|
||||||
this.entityName = "SAVINGSACCOUNT";
|
this.entityName = "SAVINGSACCOUNT";
|
||||||
|
|||||||
+1
@@ -52,6 +52,7 @@ public class SavingsApiConstants {
|
|||||||
|
|
||||||
// command
|
// command
|
||||||
public static final String COMMAND_UNDO_TRANSACTION = "undo";
|
public static final String COMMAND_UNDO_TRANSACTION = "undo";
|
||||||
|
public static final String COMMAND_REVERSE_TRANSACTION = "reverse";
|
||||||
public static final String COMMAND_ADJUST_TRANSACTION = "modify";
|
public static final String COMMAND_ADJUST_TRANSACTION = "modify";
|
||||||
public static final String COMMAND_WAIVE_CHARGE = "waive";
|
public static final String COMMAND_WAIVE_CHARGE = "waive";
|
||||||
public static final String COMMAND_PAY_CHARGE = "paycharge";
|
public static final String COMMAND_PAY_CHARGE = "paycharge";
|
||||||
|
|||||||
+6
-2
@@ -188,6 +188,9 @@ public class SavingsAccountTransactionsApiResource {
|
|||||||
if (is(commandParam, SavingsApiConstants.COMMAND_UNDO_TRANSACTION)) {
|
if (is(commandParam, SavingsApiConstants.COMMAND_UNDO_TRANSACTION)) {
|
||||||
final CommandWrapper commandRequest = builder.undoSavingsAccountTransaction(savingsId, transactionId).build();
|
final CommandWrapper commandRequest = builder.undoSavingsAccountTransaction(savingsId, transactionId).build();
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||||
|
} else if (is(commandParam, SavingsApiConstants.COMMAND_REVERSE_TRANSACTION)) {
|
||||||
|
final CommandWrapper commandRequest = builder.reverseSavingsAccountTransaction(savingsId, transactionId).build();
|
||||||
|
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||||
} else if (is(commandParam, SavingsApiConstants.COMMAND_ADJUST_TRANSACTION)) {
|
} else if (is(commandParam, SavingsApiConstants.COMMAND_ADJUST_TRANSACTION)) {
|
||||||
final CommandWrapper commandRequest = builder.adjustSavingsAccountTransaction(savingsId, transactionId).build();
|
final CommandWrapper commandRequest = builder.adjustSavingsAccountTransaction(savingsId, transactionId).build();
|
||||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||||
@@ -198,8 +201,9 @@ public class SavingsAccountTransactionsApiResource {
|
|||||||
|
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
//
|
//
|
||||||
throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { SavingsApiConstants.COMMAND_UNDO_TRANSACTION,
|
throw new UnrecognizedQueryParamException("command", commandParam,
|
||||||
SavingsApiConstants.COMMAND_ADJUST_TRANSACTION, SavingsApiConstants.COMMAND_RELEASE_AMOUNT });
|
new Object[] { SavingsApiConstants.COMMAND_UNDO_TRANSACTION, SavingsApiConstants.COMMAND_ADJUST_TRANSACTION,
|
||||||
|
SavingsApiConstants.COMMAND_RELEASE_AMOUNT, SavingsApiConstants.COMMAND_REVERSE_TRANSACTION });
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.toApiJsonSerializer.serialize(result);
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
|
|||||||
+23
-5
@@ -71,6 +71,8 @@ public final class SavingsAccountTransactionData implements Serializable {
|
|||||||
private final String submittedByUsername;
|
private final String submittedByUsername;
|
||||||
private final String note;
|
private final String note;
|
||||||
private final boolean isManualTransaction;
|
private final boolean isManualTransaction;
|
||||||
|
private final Boolean isReversal;
|
||||||
|
private final Long originalTransactionId;
|
||||||
private Set<SavingsAccountChargesPaidByData> chargesPaidByData = new HashSet<>();
|
private Set<SavingsAccountChargesPaidByData> chargesPaidByData = new HashSet<>();
|
||||||
|
|
||||||
// templates
|
// templates
|
||||||
@@ -343,6 +345,8 @@ public final class SavingsAccountTransactionData implements Serializable {
|
|||||||
this.paymentTypeOptions = null;
|
this.paymentTypeOptions = null;
|
||||||
this.submittedByUsername = null;
|
this.submittedByUsername = null;
|
||||||
this.note = null;
|
this.note = null;
|
||||||
|
this.isReversal = null;
|
||||||
|
this.originalTransactionId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isChargeTransaction() {
|
public boolean isChargeTransaction() {
|
||||||
@@ -538,6 +542,9 @@ public final class SavingsAccountTransactionData implements Serializable {
|
|||||||
this.submittedByUsername = null;
|
this.submittedByUsername = null;
|
||||||
this.note = null;
|
this.note = null;
|
||||||
this.isManualTransaction = isManualTransaction;
|
this.isManualTransaction = isManualTransaction;
|
||||||
|
this.isReversal = null;
|
||||||
|
this.originalTransactionId = null;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SavingsAccountTransactionData(BigDecimal transactionAmount, LocalDate transactionDate, Long paymentTypeId, String accountNumber,
|
private SavingsAccountTransactionData(BigDecimal transactionAmount, LocalDate transactionDate, Long paymentTypeId, String accountNumber,
|
||||||
@@ -573,6 +580,8 @@ public final class SavingsAccountTransactionData implements Serializable {
|
|||||||
this.submittedByUsername = null;
|
this.submittedByUsername = null;
|
||||||
this.note = null;
|
this.note = null;
|
||||||
this.isManualTransaction = false;
|
this.isManualTransaction = false;
|
||||||
|
this.isReversal = null;
|
||||||
|
this.originalTransactionId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SavingsAccountTransactionData(Integer id, BigDecimal transactionAmount, LocalDate transactionDate, Long paymentTypeId,
|
private SavingsAccountTransactionData(Integer id, BigDecimal transactionAmount, LocalDate transactionDate, Long paymentTypeId,
|
||||||
@@ -610,6 +619,8 @@ public final class SavingsAccountTransactionData implements Serializable {
|
|||||||
this.cumulativeBalance = cumulativeBalance;
|
this.cumulativeBalance = cumulativeBalance;
|
||||||
this.transfer = null;
|
this.transfer = null;
|
||||||
this.isManualTransaction = false;
|
this.isManualTransaction = false;
|
||||||
|
this.isReversal = null;
|
||||||
|
this.originalTransactionId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getRowIndex() {
|
public Integer getRowIndex() {
|
||||||
@@ -740,6 +751,8 @@ public final class SavingsAccountTransactionData implements Serializable {
|
|||||||
this.note = null;
|
this.note = null;
|
||||||
this.balanceEndDate = balanceEndDate;
|
this.balanceEndDate = balanceEndDate;
|
||||||
this.isManualTransaction = false;
|
this.isManualTransaction = false;
|
||||||
|
this.isReversal = null;
|
||||||
|
this.originalTransactionId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SavingsAccountTransactionData create(final Long id, final SavingsAccountTransactionEnumData transactionType,
|
public static SavingsAccountTransactionData create(final Long id, final SavingsAccountTransactionEnumData transactionType,
|
||||||
@@ -757,17 +770,17 @@ public final class SavingsAccountTransactionData implements Serializable {
|
|||||||
final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date,
|
final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date,
|
||||||
final CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance,
|
final CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance,
|
||||||
final boolean reversed, final AccountTransferData transfer, final LocalDate submittedOnDate, final boolean interestedPostedAsOn,
|
final boolean reversed, final AccountTransferData transfer, final LocalDate submittedOnDate, final boolean interestedPostedAsOn,
|
||||||
final String submittedByUsername, final String note) {
|
final String submittedByUsername, final String note, final Boolean isReversal, final Long originalTransactionId) {
|
||||||
final Collection<PaymentTypeData> paymentTypeOptions = null;
|
final Collection<PaymentTypeData> paymentTypeOptions = null;
|
||||||
return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency,
|
return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency,
|
||||||
amount, outstandingChargeAmount, runningBalance, reversed, transfer, paymentTypeOptions, submittedOnDate,
|
amount, outstandingChargeAmount, runningBalance, reversed, transfer, paymentTypeOptions, submittedOnDate,
|
||||||
interestedPostedAsOn, submittedByUsername, note);
|
interestedPostedAsOn, submittedByUsername, note, isReversal, originalTransactionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SavingsAccountTransactionData create(final Long id) {
|
public static SavingsAccountTransactionData create(final Long id) {
|
||||||
final Collection<PaymentTypeData> paymentTypeOptions = null;
|
final Collection<PaymentTypeData> paymentTypeOptions = null;
|
||||||
return new SavingsAccountTransactionData(id, null, null, null, null, null, null, null, null, null, false, null, paymentTypeOptions,
|
return new SavingsAccountTransactionData(id, null, null, null, null, null, null, null, null, null, false, null, paymentTypeOptions,
|
||||||
null, false, null, null);
|
null, false, null, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SavingsAccountTransactionData template(final Long savingsId, final String savingsAccountNo,
|
public static SavingsAccountTransactionData template(final Long savingsId, final String savingsAccountNo,
|
||||||
@@ -806,14 +819,15 @@ public final class SavingsAccountTransactionData implements Serializable {
|
|||||||
final boolean interestedPostedAsOn, final String submittedByUsername, final String note) {
|
final boolean interestedPostedAsOn, final String submittedByUsername, final String note) {
|
||||||
|
|
||||||
this(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency, amount, outstandingChargeAmount,
|
this(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency, amount, outstandingChargeAmount,
|
||||||
runningBalance, reversed, transfer, paymentTypeOptions, date, interestedPostedAsOn, submittedByUsername, note);
|
runningBalance, reversed, transfer, paymentTypeOptions, date, interestedPostedAsOn, submittedByUsername, note, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
|
private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
|
||||||
final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date,
|
final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date,
|
||||||
final CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance,
|
final CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance,
|
||||||
final boolean reversed, final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions,
|
final boolean reversed, final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions,
|
||||||
final LocalDate submittedOnDate, final boolean interestedPostedAsOn, final String submittedByUsername, final String note) {
|
final LocalDate submittedOnDate, final boolean interestedPostedAsOn, final String submittedByUsername, final String note,
|
||||||
|
final Boolean isReversal, final Long originalTransactionId) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.transactionType = transactionType;
|
this.transactionType = transactionType;
|
||||||
this.paymentDetailData = paymentDetailData;
|
this.paymentDetailData = paymentDetailData;
|
||||||
@@ -837,6 +851,8 @@ public final class SavingsAccountTransactionData implements Serializable {
|
|||||||
this.submittedByUsername = submittedByUsername;
|
this.submittedByUsername = submittedByUsername;
|
||||||
this.note = note;
|
this.note = note;
|
||||||
this.isManualTransaction = false;
|
this.isManualTransaction = false;
|
||||||
|
this.isReversal = isReversal;
|
||||||
|
this.originalTransactionId = originalTransactionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
|
private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
|
||||||
@@ -868,6 +884,8 @@ public final class SavingsAccountTransactionData implements Serializable {
|
|||||||
this.note = null;
|
this.note = null;
|
||||||
this.cumulativeBalance = cumulativeBalance;
|
this.cumulativeBalance = cumulativeBalance;
|
||||||
this.isManualTransaction = false;
|
this.isManualTransaction = false;
|
||||||
|
this.isReversal = null;
|
||||||
|
this.originalTransactionId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SavingsAccountTransactionData withWithDrawalTransactionDetails(
|
public static SavingsAccountTransactionData withWithDrawalTransactionDetails(
|
||||||
|
|||||||
+47
@@ -2378,6 +2378,29 @@ public class SavingsAccount extends AbstractPersistableCustom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void undoTransaction(final SavingsAccountTransaction transactionToUndo) {
|
||||||
|
|
||||||
|
if (transactionToUndo.isReversed()) {
|
||||||
|
throw new SavingsAccountTransactionNotFoundException(this.getId(), transactionToUndo.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
validateAttemptToUndoTransferRelatedTransactions(transactionToUndo);
|
||||||
|
validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_UNDO_TRANSACTION, transactionToUndo.transactionLocalDate());
|
||||||
|
transactionToUndo.reverse();
|
||||||
|
if (transactionToUndo.isChargeTransaction() || transactionToUndo.isWaiveCharge()) {
|
||||||
|
// undo charge
|
||||||
|
final Set<SavingsAccountChargePaidBy> chargesPaidBy = transactionToUndo.getSavingsAccountChargesPaid();
|
||||||
|
for (final SavingsAccountChargePaidBy savingsAccountChargePaidBy : chargesPaidBy) {
|
||||||
|
final SavingsAccountCharge chargeToUndo = savingsAccountChargePaidBy.getSavingsAccountCharge();
|
||||||
|
if (transactionToUndo.isChargeTransaction()) {
|
||||||
|
chargeToUndo.undoPayment(this.getCurrency(), transactionToUndo.getAmount(this.getCurrency()));
|
||||||
|
} else if (transactionToUndo.isWaiveCharge()) {
|
||||||
|
chargeToUndo.undoWaiver(this.getCurrency(), transactionToUndo.getAmount(this.getCurrency()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Date findLatestAnnualFeeTransactionDueDate() {
|
private Date findLatestAnnualFeeTransactionDueDate() {
|
||||||
|
|
||||||
Date nextDueDate = null;
|
Date nextDueDate = null;
|
||||||
@@ -2400,6 +2423,30 @@ public class SavingsAccount extends AbstractPersistableCustom {
|
|||||||
return nextDueDate;
|
return nextDueDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void validateAccountBalanceDoesNotBecomeNegativeMinimal(final BigDecimal transactionAmount, final boolean isException) {
|
||||||
|
// final List<SavingsAccountTransaction> transactionsSortedByDate = retreiveListOfTransactions();
|
||||||
|
Money runningBalance = this.summary.getAccountBalance(getCurrency());
|
||||||
|
Money minRequiredBalance = minRequiredBalanceDerived(getCurrency());
|
||||||
|
org.joda.time.LocalDate lastSavingsDate = null;
|
||||||
|
final BigDecimal withdrawalFee = null;
|
||||||
|
|
||||||
|
// check last txn date
|
||||||
|
|
||||||
|
// In overdraft cases, minRequiredBalance can be in violation after interest posting
|
||||||
|
// and should be checked after processing all transactions
|
||||||
|
if (!isOverdraft()) {
|
||||||
|
if (runningBalance.minus(minRequiredBalance).isLessThanZero()) {
|
||||||
|
throw new InsufficientAccountBalanceException("transactionAmount", getAccountBalance(), withdrawalFee, transactionAmount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.getSavingsHoldAmount().compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
if (runningBalance.minus(this.getSavingsHoldAmount()).minus(minRequiredBalance).isLessThanZero()) {
|
||||||
|
throw new InsufficientAccountBalanceException("transactionAmount", getAccountBalance(), withdrawalFee, transactionAmount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String, Object> rejectApplication(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate) {
|
public Map<String, Object> rejectApplication(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate) {
|
||||||
|
|
||||||
final Map<String, Object> actualChanges = new LinkedHashMap<>();
|
final Map<String, Object> actualChanges = new LinkedHashMap<>();
|
||||||
|
|||||||
+3
@@ -40,4 +40,7 @@ public interface SavingsAccountDomainService {
|
|||||||
|
|
||||||
SavingsAccountTransaction handleDividendPayout(SavingsAccount account, LocalDate transactionDate, BigDecimal transactionAmount,
|
SavingsAccountTransaction handleDividendPayout(SavingsAccount account, LocalDate transactionDate, BigDecimal transactionAmount,
|
||||||
boolean backdatedTxnsAllowedTill);
|
boolean backdatedTxnsAllowedTill);
|
||||||
|
|
||||||
|
SavingsAccountTransaction handleReversal(SavingsAccount account, SavingsAccountTransaction savingsAccountTransaction,
|
||||||
|
boolean backdatedTxnsAllowedTill);
|
||||||
}
|
}
|
||||||
|
|||||||
+51
@@ -22,6 +22,7 @@ import java.math.BigDecimal;
|
|||||||
import java.math.MathContext;
|
import java.math.MathContext;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -35,6 +36,7 @@ import org.apache.fineract.infrastructure.security.service.PlatformSecurityConte
|
|||||||
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
|
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
|
||||||
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
|
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
|
||||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||||
|
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||||
import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BusinessEntity;
|
import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BusinessEntity;
|
||||||
import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BusinessEvents;
|
import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BusinessEvents;
|
||||||
import org.apache.fineract.portfolio.common.service.BusinessEventNotifierService;
|
import org.apache.fineract.portfolio.common.service.BusinessEventNotifierService;
|
||||||
@@ -275,4 +277,53 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
|
|||||||
map.put(entityEvent, entity);
|
map.put(entityEvent, entity);
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SavingsAccountTransaction handleReversal(SavingsAccount account, SavingsAccountTransaction savingsAccountTransaction,
|
||||||
|
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<>();
|
||||||
|
|
||||||
|
if (backdatedTxnsAllowedTill) {
|
||||||
|
updateTransactionDetailsWithPivotConfig(account, existingTransactionIds, existingReversedTransactionIds);
|
||||||
|
} else {
|
||||||
|
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
SavingsAccountTransaction reversal = SavingsAccountTransaction.reversal(savingsAccountTransaction);
|
||||||
|
reversal.getSavingsAccountChargesPaid().addAll(chargePaidBySet);
|
||||||
|
account.undoTransaction(savingsAccountTransaction);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
return reversal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -123,7 +123,7 @@ public final class SavingsAccountSummary {
|
|||||||
public void updateSummaryWithPivotConfig(final MonetaryCurrency currency, final SavingsAccountTransactionSummaryWrapper wrapper,
|
public void updateSummaryWithPivotConfig(final MonetaryCurrency currency, final SavingsAccountTransactionSummaryWrapper wrapper,
|
||||||
final SavingsAccountTransaction transaction, final List<SavingsAccountTransaction> savingsAccountTransactions) {
|
final SavingsAccountTransaction transaction, final List<SavingsAccountTransaction> savingsAccountTransactions) {
|
||||||
|
|
||||||
if (transaction != null) {
|
if (transaction != null && !transaction.isReversalTransaction()) {
|
||||||
Money transactionAmount = Money.of(currency, transaction.getAmount());
|
Money transactionAmount = Money.of(currency, transaction.getAmount());
|
||||||
switch (SavingsAccountTransactionType.fromInt(transaction.getTypeOf())) {
|
switch (SavingsAccountTransactionType.fromInt(transaction.getTypeOf())) {
|
||||||
case DEPOSIT:
|
case DEPOSIT:
|
||||||
|
|||||||
+23
@@ -132,6 +132,12 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
|
|||||||
@JoinColumn(name = "savings_account_transaction_id", referencedColumnName = "id")
|
@JoinColumn(name = "savings_account_transaction_id", referencedColumnName = "id")
|
||||||
private List<Note> notes = new ArrayList<>();
|
private List<Note> notes = new ArrayList<>();
|
||||||
|
|
||||||
|
@Column(name = "is_reversal", nullable = false)
|
||||||
|
private boolean reversalTransaction;
|
||||||
|
|
||||||
|
@Column(name = "original_transaction_id")
|
||||||
|
private Long originalTxnId;
|
||||||
|
|
||||||
SavingsAccountTransaction() {
|
SavingsAccountTransaction() {
|
||||||
this.dateOf = null;
|
this.dateOf = null;
|
||||||
this.typeOf = null;
|
this.typeOf = null;
|
||||||
@@ -342,6 +348,15 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
|
|||||||
accountTransaction.reversed, appUser, accountTransaction.isManualTransaction);
|
accountTransaction.reversed, appUser, accountTransaction.isManualTransaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SavingsAccountTransaction reversal(SavingsAccountTransaction accountTransaction) {
|
||||||
|
SavingsAccountTransaction sat = copyTransaction(accountTransaction);
|
||||||
|
sat.reversed = false;
|
||||||
|
sat.setReversalTransaction(true);
|
||||||
|
sat.originalTxnId = accountTransaction.getId();
|
||||||
|
return sat;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public LocalDate transactionLocalDate() {
|
public LocalDate transactionLocalDate() {
|
||||||
return LocalDate.ofInstant(this.dateOf.toInstant(), DateUtils.getDateTimeZoneOfTenant());
|
return LocalDate.ofInstant(this.dateOf.toInstant(), DateUtils.getDateTimeZoneOfTenant());
|
||||||
}
|
}
|
||||||
@@ -863,4 +878,12 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
|
|||||||
public boolean getIsManualTransaction() {
|
public boolean getIsManualTransaction() {
|
||||||
return this.isManualTransaction;
|
return this.isManualTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setReversalTransaction(boolean reversalTransaction) {
|
||||||
|
this.reversalTransaction = reversalTransaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isReversalTransaction() {
|
||||||
|
return reversalTransaction;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.savings.handler;
|
||||||
|
|
||||||
|
import org.apache.fineract.commands.annotation.CommandType;
|
||||||
|
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||||
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author manoj
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "SAVINGSACCOUNT", action = "REVERSETRANSACTION")
|
||||||
|
public class ReverseTransactionSavingsAccountCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
private final SavingsAccountWritePlatformService writePlatformService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public ReverseTransactionSavingsAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||||
|
this.writePlatformService = writePlatformService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
final Long transactionId = Long.valueOf(command.getTransactionId());
|
||||||
|
return this.writePlatformService.reverseTransaction(command.getSavingsId(), transactionId, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-1
@@ -1317,6 +1317,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
|||||||
sqlBuilder.append(" au.username as submittedByUsername, ");
|
sqlBuilder.append(" au.username as submittedByUsername, ");
|
||||||
sqlBuilder.append(" nt.note as transactionNote, ");
|
sqlBuilder.append(" nt.note as transactionNote, ");
|
||||||
sqlBuilder.append("tr.running_balance_derived as runningBalance, tr.is_reversed as reversed,");
|
sqlBuilder.append("tr.running_balance_derived as runningBalance, tr.is_reversed as reversed,");
|
||||||
|
sqlBuilder.append("tr.is_reversal as isReversal, tr.original_transaction_id as originalTransactionId,");
|
||||||
sqlBuilder.append("fromtran.id as fromTransferId, fromtran.is_reversed as fromTransferReversed,");
|
sqlBuilder.append("fromtran.id as fromTransferId, fromtran.is_reversed as fromTransferReversed,");
|
||||||
sqlBuilder.append("fromtran.transaction_date as fromTransferDate, fromtran.amount as fromTransferAmount,");
|
sqlBuilder.append("fromtran.transaction_date as fromTransferDate, fromtran.amount as fromTransferAmount,");
|
||||||
sqlBuilder.append("fromtran.description as fromTransferDescription,");
|
sqlBuilder.append("fromtran.description as fromTransferDescription,");
|
||||||
@@ -1360,6 +1361,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
|||||||
final BigDecimal outstandingChargeAmount = null;
|
final BigDecimal outstandingChargeAmount = null;
|
||||||
final BigDecimal runningBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "runningBalance");
|
final BigDecimal runningBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "runningBalance");
|
||||||
final boolean reversed = rs.getBoolean("reversed");
|
final boolean reversed = rs.getBoolean("reversed");
|
||||||
|
final boolean isReversal = rs.getBoolean("isReversal");
|
||||||
|
final Long originalTransactionId = rs.getLong("originalTransactionId");
|
||||||
|
|
||||||
final Long savingsId = rs.getLong("savingsId");
|
final Long savingsId = rs.getLong("savingsId");
|
||||||
final String accountNo = rs.getString("accountNo");
|
final String accountNo = rs.getString("accountNo");
|
||||||
@@ -1414,7 +1417,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
|||||||
final String note = rs.getString("transactionNote");
|
final String note = rs.getString("transactionNote");
|
||||||
return SavingsAccountTransactionData.create(id, transactionType, paymentDetailData, savingsId, accountNo, date, currency,
|
return SavingsAccountTransactionData.create(id, transactionType, paymentDetailData, savingsId, accountNo, date, currency,
|
||||||
amount, outstandingChargeAmount, runningBalance, reversed, transfer, submittedOnDate, postInterestAsOn,
|
amount, outstandingChargeAmount, runningBalance, reversed, transfer, submittedOnDate, postInterestAsOn,
|
||||||
submittedByUsername, note);
|
submittedByUsername, note, isReversal, originalTransactionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
@@ -41,6 +41,8 @@ public interface SavingsAccountWritePlatformService {
|
|||||||
|
|
||||||
CommandProcessingResult calculateInterest(Long savingsId);
|
CommandProcessingResult calculateInterest(Long savingsId);
|
||||||
|
|
||||||
|
CommandProcessingResult reverseTransaction(Long savingsId, Long transactionId, boolean allowAccountTransferModification);
|
||||||
|
|
||||||
CommandProcessingResult undoTransaction(Long savingsId, Long transactionId, boolean allowAccountTransferModification);
|
CommandProcessingResult undoTransaction(Long savingsId, Long transactionId, boolean allowAccountTransferModification);
|
||||||
|
|
||||||
CommandProcessingResult adjustSavingsTransaction(Long savingsId, Long transactionId, JsonCommand command);
|
CommandProcessingResult adjustSavingsTransaction(Long savingsId, Long transactionId, JsonCommand command);
|
||||||
|
|||||||
+43
@@ -679,6 +679,49 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
|||||||
return savingsAccountData;
|
return savingsAccountData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult reverseTransaction(final Long savingsId, final Long transactionId,
|
||||||
|
final boolean allowAccountTransferModification) {
|
||||||
|
|
||||||
|
final boolean backdatedTxnsAllowedTill = this.savingAccountAssembler.getPivotConfigStatus();
|
||||||
|
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId, backdatedTxnsAllowedTill);
|
||||||
|
|
||||||
|
final SavingsAccountTransaction savingsAccountTransaction = this.savingsAccountTransactionRepository
|
||||||
|
.findOneByIdAndSavingsAccountId(transactionId, savingsId);
|
||||||
|
if (savingsAccountTransaction == null) {
|
||||||
|
throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allowAccountTransferModification
|
||||||
|
&& this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)) {
|
||||||
|
throw new PlatformServiceUnavailableException("error.msg.saving.account.transfer.transaction.update.not.allowed",
|
||||||
|
"Savings account transaction:" + transactionId + " update not allowed as it involves in account transfer",
|
||||||
|
transactionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!account.allowModify()) {
|
||||||
|
throw new PlatformServiceUnavailableException("error.msg.saving.account.transaction.update.not.allowed",
|
||||||
|
"Savings account transaction:" + transactionId + " update not allowed for this savings type", transactionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account.isNotActive()) {
|
||||||
|
throwValidationForActiveStatus(SavingsApiConstants.undoTransactionAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkClientOrGroupActive(account);
|
||||||
|
|
||||||
|
final SavingsAccountTransaction reversal = this.savingsAccountDomainService.handleReversal(account, savingsAccountTransaction,
|
||||||
|
backdatedTxnsAllowedTill);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder() //
|
||||||
|
.withEntityId(reversal.getId()) //
|
||||||
|
.withOfficeId(account.officeId()) //
|
||||||
|
.withClientId(account.clientId()) //
|
||||||
|
.withGroupId(account.groupId()) //
|
||||||
|
.withSavingsId(savingsId) //
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult undoTransaction(final Long savingsId, final Long transactionId,
|
public CommandProcessingResult undoTransaction(final Long savingsId, final Long transactionId,
|
||||||
final boolean allowAccountTransferModification) {
|
final boolean allowAccountTransferModification) {
|
||||||
|
|||||||
@@ -24,4 +24,5 @@
|
|||||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||||
<include file="parts/0003_postgresql_specific_initial_data.xml" relativeToChangelogFile="true"/>
|
<include file="parts/0003_postgresql_specific_initial_data.xml" relativeToChangelogFile="true"/>
|
||||||
<include file="parts/0004_camelcase_column_renaming.xml" relativeToChangelogFile="true"/>
|
<include file="parts/0004_camelcase_column_renaming.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="parts/0005_savings_transaction_reversal.xml" relativeToChangelogFile="true"/>
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
<?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">
|
||||||
|
<addColumn tableName="m_savings_account_transaction">
|
||||||
|
<column name="original_transaction_id" type="BIGINT"/>
|
||||||
|
<column name="is_reversal" type="TINYINT" defaultValueNumeric="0">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet author="fineract" id="2">
|
||||||
|
<insert tableName="m_permission">
|
||||||
|
<column name="grouping" value="transaction_savings"/>
|
||||||
|
<column name="code" value="REVERSETRANSACTION_SAVINGSACCOUNT"/>
|
||||||
|
<column name="entity_name" value="SAVINGSACCOUNT"/>
|
||||||
|
<column name="action_name" value="REVERSETRANSACTION"/>
|
||||||
|
<column name="can_maker_checker" valueBoolean="false"/>
|
||||||
|
</insert>
|
||||||
|
<insert tableName="m_permission">
|
||||||
|
<column name="grouping" value="transaction_savings"/>
|
||||||
|
<column name="code" value="REVERSETRANSACTION_SAVINGSACCOUNT_CHECKER"/>
|
||||||
|
<column name="entity_name" value="SAVINGSACCOUNT"/>
|
||||||
|
<column name="action_name" value="REVERSETRANSACTION_CHECKER"/>
|
||||||
|
<column name="can_maker_checker" valueBoolean="false"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
+55
@@ -2392,4 +2392,59 @@ public class ClientSavingsIntegrationTest {
|
|||||||
HashMap summaryTwo = this.savingsAccountHelper.getSavingsSummary(savingsId);
|
HashMap summaryTwo = this.savingsAccountHelper.getSavingsSummary(savingsId);
|
||||||
assertEquals(balanceAfterChargeTwo, summaryTwo.get("accountBalance"), "Verifying Balance after withdrawal charge two ");
|
assertEquals(balanceAfterChargeTwo, summaryTwo.get("accountBalance"), "Verifying Balance after withdrawal charge two ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Transaction reversal feature, here a new reversal transaction is posted when a savings transaction is
|
||||||
|
* reversed
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAccountBalanceAfterSavingsTransactionReversalPosting() {
|
||||||
|
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
|
||||||
|
|
||||||
|
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||||
|
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
|
||||||
|
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
|
||||||
|
// Assertions.assertNotNull(clientID);
|
||||||
|
final String minBalanceForInterestCalculation = null;
|
||||||
|
final String minRequiredBalance = "0";
|
||||||
|
final String enforceMinRequiredBalance = "false";
|
||||||
|
final boolean allowOverdraft = true;
|
||||||
|
final String MINIMUM_OPENING_BALANCE = "0";
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
HashMap savingsStatusHashMap = this.savingsAccountHelper.approveSavings(savingsId);
|
||||||
|
SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
|
||||||
|
|
||||||
|
savingsStatusHashMap = this.savingsAccountHelper.activateSavings(savingsId);
|
||||||
|
SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
|
||||||
|
|
||||||
|
Integer depositTransactionId = (Integer) this.savingsAccountHelper.depositToSavingsAccount(savingsId, "500",
|
||||||
|
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
|
||||||
|
|
||||||
|
this.savingsAccountHelper.reverseSavingsAccountTransaction(savingsId, depositTransactionId);
|
||||||
|
|
||||||
|
HashMap reversedDepositTransaction = this.savingsAccountHelper.getSavingsTransaction(savingsId, depositTransactionId);
|
||||||
|
|
||||||
|
Assertions.assertTrue((Boolean) reversedDepositTransaction.get("reversed"));
|
||||||
|
|
||||||
|
List<HashMap> transactions = this.savingsAccountHelper.getSavingsTransactions(savingsId);
|
||||||
|
|
||||||
|
HashMap reversalDepositTransaction = transactions.get(0);
|
||||||
|
|
||||||
|
Assertions.assertTrue((Boolean) reversalDepositTransaction.get("isReversal"));
|
||||||
|
|
||||||
|
HashMap summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
|
||||||
|
|
||||||
|
Float balance = Float.parseFloat("0.0");
|
||||||
|
|
||||||
|
assertEquals(balance, summary.get("accountBalance"), "Verifying opening Balance is 500");
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.integrationtests.common.savings;
|
package org.apache.fineract.integrationtests.common.savings;
|
||||||
|
|
||||||
|
import com.google.common.reflect.TypeToken;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import io.restassured.specification.RequestSpecification;
|
import io.restassured.specification.RequestSpecification;
|
||||||
import io.restassured.specification.ResponseSpecification;
|
import io.restassured.specification.ResponseSpecification;
|
||||||
@@ -69,6 +70,7 @@ public class SavingsAccountHelper {
|
|||||||
private static final String GSIM_DEPOSIT_SAVINGS_COMMAND = "gsimDeposit";
|
private static final String GSIM_DEPOSIT_SAVINGS_COMMAND = "gsimDeposit";
|
||||||
private static final String MODIFY_TRASACTION_COMMAND = "modify";
|
private static final String MODIFY_TRASACTION_COMMAND = "modify";
|
||||||
private static final String UNDO_TRASACTION_COMMAND = "undo";
|
private static final String UNDO_TRASACTION_COMMAND = "undo";
|
||||||
|
private static final String REVERSE_TRASACTION_COMMAND = "reverse";
|
||||||
|
|
||||||
private static final String BLOCK_SAVINGS_COMMAND = "block";
|
private static final String BLOCK_SAVINGS_COMMAND = "block";
|
||||||
private static final String UNBLOCK_SAVINGS_COMMAND = "unblock";
|
private static final String UNBLOCK_SAVINGS_COMMAND = "unblock";
|
||||||
@@ -272,6 +274,12 @@ public class SavingsAccountHelper {
|
|||||||
getSavingsTransactionJSON("0", LAST_TRANSACTION_DATE), CommonConstants.RESPONSE_RESOURCE_ID);
|
getSavingsTransactionJSON("0", LAST_TRANSACTION_DATE), CommonConstants.RESPONSE_RESOURCE_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Integer reverseSavingsAccountTransaction(final Integer savingsId, final Integer transactionId) {
|
||||||
|
LOG.info("\n--------------------------------- REVERSE SAVINGS TRANSACTION --------------------------------");
|
||||||
|
return (Integer) performSavingActions(createAdjustTransactionURL(REVERSE_TRASACTION_COMMAND, savingsId, transactionId),
|
||||||
|
getSavingsTransactionJSON("0", LAST_TRANSACTION_DATE), CommonConstants.RESPONSE_RESOURCE_ID);
|
||||||
|
}
|
||||||
|
|
||||||
public void calculateInterestForSavings(final Integer savingsId) {
|
public void calculateInterestForSavings(final Integer savingsId) {
|
||||||
LOG.info("--------------------------------- CALCULATING INTEREST FOR SAVINGS --------------------------------");
|
LOG.info("--------------------------------- CALCULATING INTEREST FOR SAVINGS --------------------------------");
|
||||||
performSavingActions(createSavingsCalculateInterestURL(CALCULATE_INTEREST_SAVINGS_COMMAND, savingsId),
|
performSavingActions(createSavingsCalculateInterestURL(CALCULATE_INTEREST_SAVINGS_COMMAND, savingsId),
|
||||||
@@ -598,6 +606,12 @@ public class SavingsAccountHelper {
|
|||||||
return Utils.performServerGet(requestSpec, responseSpec, URL, "");
|
return Utils.performServerGet(requestSpec, responseSpec, URL, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<HashMap> getSavingsTransactions(final Integer savingsID) {
|
||||||
|
final Object get = getSavingsCollectionAttribute(savingsID, "transactions");
|
||||||
|
final String json = new Gson().toJson(get);
|
||||||
|
return new Gson().fromJson(json, new TypeToken<ArrayList<HashMap>>() {}.getType());
|
||||||
|
}
|
||||||
|
|
||||||
public Object getSavingsInterest(final Integer savingsID) {
|
public Object getSavingsInterest(final Integer savingsID) {
|
||||||
final String URL = SAVINGS_ACCOUNT_URL + "/" + savingsID + "?associations=summary&" + Utils.TENANT_IDENTIFIER;
|
final String URL = SAVINGS_ACCOUNT_URL + "/" + savingsID + "?associations=summary&" + Utils.TENANT_IDENTIFIER;
|
||||||
final HashMap response = Utils.performServerGet(requestSpec, responseSpec, URL, "summary");
|
final HashMap response = Utils.performServerGet(requestSpec, responseSpec, URL, "summary");
|
||||||
|
|||||||
Reference in New Issue
Block a user