Merge branch 'FINERACT-482' into develop

This commit is contained in:
Nazeer Hussain Shaik
2017-07-13 19:34:07 +05:30
11 changed files with 106 additions and 38 deletions
@@ -28,10 +28,12 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.integrationtests.common.CommonConstants;
import org.apache.fineract.integrationtests.common.SchedulerJobHelper;
@@ -2214,13 +2216,17 @@ public class ClientSavingsIntegrationTest {
Integer holdTransactionId = (Integer) this.savingsAccountHelper.holdAmountInSavingsAccount(savingsId, String.valueOf(balance - 100),
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
error = (List) savingsAccountHelperValidationError.withdrawalFromSavingsAccount(savingsId, "300",
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_ERROR);
SavingsAccountHelper.TRANSACTION_DATE_PLUS_ONE, CommonConstants.RESPONSE_ERROR);
assertEquals("error.msg.savingsaccount.transaction.insufficient.account.balance",
error.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
Integer releaseTransactionId = this.savingsAccountHelper.releaseAmount(savingsId, holdTransactionId);
withdrawTransactionId = (Integer) this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, "300",
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
Date today = DateUtils.getDateOfTenant();
String todayDate = today.toString();
SimpleDateFormat dt1 = new SimpleDateFormat("dd MMM yyyy");
todayDate = dt1.format(today).toString();
withdrawTransactionId = (Integer) this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, "300", todayDate,
CommonConstants.RESPONSE_RESOURCE_ID);
withdrawTransaction = this.savingsAccountHelper.getSavingsTransaction(savingsId, withdrawTransactionId);
balance -= new Float("300");
assertEquals("Verifying Withdrawal Amount", new Float("300"), withdrawTransaction.get("amount"));
@@ -69,6 +69,7 @@ public class SavingsAccountHelper {
public static final String CREATED_DATE_PLUS_ONE = "09 January 2013";
public static final String CREATED_DATE_MINUS_ONE = "07 January 2013";
public static final String TRANSACTION_DATE = "01 March 2013";
public static final String TRANSACTION_DATE_PLUS_ONE = "02 March 2013";
public static final String LAST_TRANSACTION_DATE = "01 March 2013";
public static final String ACCOUNT_TYPE_INDIVIDUAL = "INDIVIDUAL";
@@ -133,6 +133,7 @@ public class SavingsApiConstants {
public static final String minBalanceForInterestCalculationParamName = "minBalanceForInterestCalculation";
public static final String withdrawBalanceParamName = "withdrawBalance";
public static final String onHoldFundsParamName = "onHoldFunds";
public static final String savingsAmountOnHold = "savingsAmountOnHold";
public static final String withHoldTaxParamName = "withHoldTax";
public static final String taxGroupIdParamName = "taxGroupId";
@@ -65,7 +65,7 @@ public class SavingsApiSetConstants extends SavingsApiConstants {
"interestCalculationTypeOptions", "interestCalculationDaysInYearTypeOptions",
"lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions", "withdrawalFee", "annualFee",
onHoldFundsParamName, nominalAnnualInterestRateOverdraftParamName,
minOverdraftForInterestCalculationParamName, datatables));
minOverdraftForInterestCalculationParamName, datatables, savingsAmountOnHold));
protected static final Set<String> SAVINGS_TRANSACTION_RESPONSE_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(idParamName, "accountId", accountNoParamName, "currency", "amount", dateParamName,
@@ -76,6 +76,7 @@ public class SavingsAccountData {
private final Integer daysToInactive;
private final Integer daysToDormancy;
private final Integer daysToEscheat;
private final BigDecimal savingsAmountOnHold;
// associations
private final SavingsAccountSummaryData summary;
@@ -118,7 +119,7 @@ public class SavingsAccountData {
final BigDecimal onHoldFunds, final BigDecimal nominalAnnualInterestRateOverdraft,
final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax, final TaxGroupData taxGroup,
final LocalDate lastActiveTransactionDate, final boolean isDormancyTrackingActive, final Integer daysToInactive,
final Integer daysToDormancy, final Integer daysToEscheat) {
final Integer daysToDormancy, final Integer daysToEscheat, final BigDecimal savingsAmountOnHold) {
final Collection<SavingsProductData> productOptions = null;
final Collection<StaffData> fieldOfficerOptions = null;
@@ -142,7 +143,7 @@ public class SavingsAccountData {
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit,
minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup,
lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
}
public static SavingsAccountData lookup(final Long accountId, final String accountNo, final EnumOptionData depositType) {
@@ -204,6 +205,7 @@ public class SavingsAccountData {
final Integer daysToInactive = null;
final Integer daysToDormancy = null;
final Integer daysToEscheat = null;
final BigDecimal savingsAmountOnHold = null;
return new SavingsAccountData(accountId, accountNo, depositType, externalId, groupId, groupName, clientId, clientName, productId,
productName, fieldOfficerId, fieldOfficerName, status, subStatus, timeline, currency, nominalAnnualInterestRate,
@@ -214,7 +216,7 @@ public class SavingsAccountData {
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit,
minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup,
lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
}
public static SavingsAccountData lookupWithProductDetails(final Long accountId, final String accountNo,
@@ -274,6 +276,7 @@ public class SavingsAccountData {
final Integer daysToInactive = null;
final Integer daysToDormancy = null;
final Integer daysToEscheat = null;
final BigDecimal savingsAmountOnHold = null;
return new SavingsAccountData(accountId, accountNo, depositType, externalId, groupId, groupName, clientId, clientName, productId,
productName, fieldOfficerId, fieldOfficerName, status, subStatus, timeline, currency, nominalAnnualInterestRate,
@@ -284,7 +287,7 @@ public class SavingsAccountData {
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit,
minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup,
lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
}
public static SavingsAccountData withTemplateOptions(final SavingsAccountData account, final SavingsAccountData template,
@@ -320,7 +323,7 @@ public class SavingsAccountData {
account.minBalanceForInterestCalculation, account.onHoldFunds, account.nominalAnnualInterestRateOverdraft,
account.minOverdraftForInterestCalculation, account.withHoldTax, account.taxGroup,
account.lastActiveTransactionDate, account.isDormancyTrackingActive, account.daysToInactive,
account.daysToDormancy, account.daysToEscheat);
account.daysToDormancy, account.daysToEscheat, account.savingsAmountOnHold);
}
public static SavingsAccountData withTemplateOptions(final SavingsAccountData account,
@@ -345,7 +348,7 @@ public class SavingsAccountData {
account.overdraftLimit, account.minRequiredBalance, account.enforceMinRequiredBalance,
account.minBalanceForInterestCalculation, account.onHoldFunds, account.nominalAnnualInterestRateOverdraft,
account.minOverdraftForInterestCalculation, account.withHoldTax, account.taxGroup, account.lastActiveTransactionDate,
account.isDormancyTrackingActive, account.daysToInactive, account.daysToDormancy, account.daysToEscheat);
account.isDormancyTrackingActive, account.daysToInactive, account.daysToDormancy, account.daysToEscheat, account.savingsAmountOnHold);
}
public static SavingsAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId,
@@ -407,6 +410,7 @@ public class SavingsAccountData {
final Integer daysToInactive = null;
final Integer daysToDormancy = null;
final Integer daysToEscheat = null;
final BigDecimal savingsAmountOnHold = null;
return new SavingsAccountData(id, accountNo, depositType, externalId, groupId, groupName, clientId, clientName, productId,
productName, fieldOfficerId, fieldOfficerName, status, subStatus, timeline, currency, nominalAnnualInterestRate,
@@ -417,7 +421,7 @@ public class SavingsAccountData {
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit,
minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup,
lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
}
private SavingsAccountData(final Long id, final String accountNo, final EnumOptionData depositType, final String externalId,
@@ -441,7 +445,7 @@ public class SavingsAccountData {
final BigDecimal onHoldFunds, final BigDecimal nominalAnnualInterestRateOverdraft,
final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax, final TaxGroupData taxGroup,
final LocalDate lastActiveTransactionDate, final boolean isDormancyTrackingActive, final Integer daysToInactive,
final Integer daysToDormancy, final Integer daysToEscheat) {
final Integer daysToDormancy, final Integer daysToEscheat, final BigDecimal savingsAmountOnHold) {
this.id = id;
this.accountNo = accountNo;
this.depositType = depositType;
@@ -507,6 +511,7 @@ public class SavingsAccountData {
this.daysToInactive = daysToInactive;
this.daysToDormancy = daysToDormancy;
this.daysToEscheat = daysToEscheat;
this.savingsAmountOnHold = savingsAmountOnHold;
}
private SavingsAccountChargeData getWithdrawalFee() {
@@ -123,7 +123,7 @@ public class SavingsAccountTransactionDataValidator {
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateClosing(final JsonCommand command) {
public void validateClosing(final JsonCommand command, final SavingsAccount account) {
final String json = command.json();
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
@@ -146,6 +146,11 @@ public class SavingsAccountTransactionDataValidator {
baseDataValidator.reset().parameter(withdrawBalanceParamName).value(withdrawBalance).isOneOfTheseValues(true, false);
}
if (account.getSavingsHoldAmount().compareTo(BigDecimal.ZERO) == 1) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
"amount.is.on.hold.release.the.amount.to.continue", account.getId());
}
validatePaymentTypeDetails(baseDataValidator, element);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
@@ -194,10 +199,9 @@ public class SavingsAccountTransactionDataValidator {
baseDataValidator.reset().parameter(SavingsApiConstants.statusParamName)
.failWithCodeNoParameterAddedToErrorCode(SavingsApiConstants.ERROR_MSG_SAVINGS_ACCOUNT_NOT_ACTIVE);
}
account.holdFunds(amount);
if (account.getWithdrawableBalance().compareTo(BigDecimal.ZERO) == -1) {
account.holdAmount(amount);
if (account.getWithdrawableBalance().compareTo(BigDecimal.ZERO)==-1){
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("insufficient balance", account.getId());
baseDataValidator.failWithCode("validation.msg.savingsaccount.insufficient balance", "Insufficient balance");
}
LocalDate lastTransactionDate = account.retrieveLastTransactionDate();
// compare two dates now
@@ -208,7 +212,8 @@ public class SavingsAccountTransactionDataValidator {
throwExceptionIfValidationWarningsExist(dataValidationErrors);
final PaymentDetail paymentDetails = null;
Date createdDate = DateUtils.getDateOfTenant();
Date createdDate = new Date();
SavingsAccountTransaction transaction = SavingsAccountTransaction.holdAmount(account, account.office(), paymentDetails,
transactionDate, Money.of(account.getCurrency(), amount), createdDate, createdUser);
return transaction;
@@ -219,15 +224,24 @@ public class SavingsAccountTransactionDataValidator {
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SAVINGS_ACCOUNT_RESOURCE_NAME);
if (holdTransaction == null) {
baseDataValidator.failWithCode("validation.msg.validation.errors.exist", "Transaction not found");
} else if (holdTransaction.getReleaseIdOfHoldAmountTransaction() != null) {
baseDataValidator.parameter(SavingsApiConstants.amountParamName).value(holdTransaction.getAmount()).failWithCode("validation.msg.amount.is.not.on.hold",
"Transaction amount is not on hold");
}
if (holdTransaction == null) {
baseDataValidator.failWithCode("validation.msg.validation.errors.exist", "Transaction not found");
} else if (holdTransaction.getReleaseIdOfHoldAmountTransaction() != null) {
baseDataValidator.parameter(SavingsApiConstants.amountParamName).value(holdTransaction.getAmount())
.failWithCode("validation.msg.amount.is.not.on.hold", "Transaction amount is not on hold");
}
if (holdTransaction != null) {
boolean isActive = holdTransaction.getSavingsAccount().isActive();
if (!isActive) {
baseDataValidator.reset().parameter(SavingsApiConstants.statusParamName)
.failWithCodeNoParameterAddedToErrorCode(
SavingsApiConstants.ERROR_MSG_SAVINGS_ACCOUNT_NOT_ACTIVE);
}
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
Date createdDate = DateUtils.getDateOfTenant();
Date createdDate = new Date();
LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
SavingsAccountTransaction transaction = SavingsAccountTransaction.releaseAmount(holdTransaction, transactionDate, createdDate,
createdUser);
@@ -325,6 +325,8 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
@JoinColumn(name = "tax_group_id")
private TaxGroup taxGroup;
@Column(name = "total_savings_amount_on_hold", scale = 6, precision = 19, nullable = true)
private BigDecimal savingsOnHoldAmount;
protected SavingsAccount() {
//
}
@@ -1051,19 +1053,16 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
Money runningBalance = Money.zero(this.currency);
Money minRequiredBalance = minRequiredBalanceDerived(getCurrency());
LocalDate lastSavingsDate = null;
final BigDecimal withdrawalFee = null;
for (final SavingsAccountTransaction transaction : transactionsSortedByDate) {
if (transaction.isNotReversed() && transaction.isCredit()) {
runningBalance = runningBalance.plus(transaction.getAmount(this.currency));
} else if (transaction.isNotReversed() && transaction.isDebit()) {
runningBalance = runningBalance.minus(transaction.getAmount(this.currency));
} else if(transaction.isAmountOnHold() && transaction.getReleaseIdOfHoldAmountTransaction() == null){
runningBalance = runningBalance.minus(transaction.getAmount(this.currency));
}else {
continue;
}
final BigDecimal withdrawalFee = null;
/*
* Loop through the onHold funds and see if we need to deduct or add
* to minimum required balance and the point in time the transaction
@@ -1093,6 +1092,12 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
lastSavingsDate = transaction.transactionLocalDate();
}
if (this.getSavingsHoldAmount().compareTo(BigDecimal.ZERO) == 1) {
if (runningBalance.minus(this.getSavingsHoldAmount()).isLessThanZero()) {
throw new InsufficientAccountBalanceException("transactionAmount", getAccountBalance(), withdrawalFee,
transactionAmount);
}
}
}
public void validateAccountBalanceDoesNotBecomeNegative(final String transactionAction,
@@ -2753,7 +2758,7 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
}
public BigDecimal getWithdrawableBalance() {
return getAccountBalance().subtract(minRequiredBalanceDerived(getCurrency()).getAmount()).subtract(this.getOnHoldFunds());
return getAccountBalance().subtract(minRequiredBalanceDerived(getCurrency()).getAmount()).subtract(this.getOnHoldFunds()).subtract(this.getSavingsHoldAmount());
}
public TaxGroup getTaxGroup() {
@@ -3006,9 +3011,27 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
public LocalDate retrieveLastTransactionDate() {
final List<SavingsAccountTransaction> transactionsSortedByDate = retreiveListOfTransactions();
SavingsAccountTransaction lastTransaction = transactionsSortedByDate.get(transactionsSortedByDate.size() - 1);
return lastTransaction.transactionLocalDate();
SavingsAccountTransaction lastTransaction = null;
if (transactionsSortedByDate.size() > 0) {
lastTransaction = transactionsSortedByDate.get(transactionsSortedByDate.size() - 1);
}
LocalDate lastransactionDate = null;
if (lastTransaction != null) {
lastransactionDate = lastTransaction.transactionLocalDate();
}
return lastransactionDate;
}
public BigDecimal getSavingsHoldAmount() {
return this.savingsOnHoldAmount == null ? BigDecimal.ZERO : this.savingsOnHoldAmount;
}
public void holdAmount(BigDecimal amount) {
this.savingsOnHoldAmount = getSavingsHoldAmount().add(amount);
}
public void releaseAmount(BigDecimal amount) {
this.savingsOnHoldAmount = getSavingsHoldAmount().subtract(amount);
}
}
@@ -787,4 +787,8 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom<L
public Long getReleaseIdOfHoldAmountTransaction() {
return this.releaseIdOfHoldAmountTransaction;
}
public boolean isAmountOnHoldNotReleased() {
return (isAmountOnHold() && getReleaseIdOfHoldAmountTransaction() == null);
}
}
@@ -304,6 +304,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
sqlBuilder.append("sa.withhold_tax as withHoldTax, ");
sqlBuilder.append("sa.total_withhold_tax_derived as totalWithholdTax, ");
sqlBuilder.append("sa.last_interest_calculation_date as lastInterestCalculationDate, ");
sqlBuilder.append("sa.total_savings_amount_on_hold as onHoldAmount, ");
sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName, ");
sqlBuilder.append("(select IFNULL(max(sat.transaction_date),sa.activatedon_date) ");
sqlBuilder.append("from m_savings_account_transaction as sat ");
@@ -511,12 +512,19 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
"minBalanceForInterestCalculation");
final BigDecimal onHoldFunds = rs.getBigDecimal("onHoldFunds");
final BigDecimal onHoldAmount = rs.getBigDecimal("onHoldAmount");
BigDecimal availableBalance = accountBalance;
if (availableBalance != null && onHoldFunds != null) {
availableBalance = availableBalance.subtract(onHoldFunds);
}
if (availableBalance != null && onHoldAmount != null) {
availableBalance = availableBalance.subtract(onHoldAmount);
}
BigDecimal interestNotPosted = BigDecimal.ZERO;
LocalDate lastInterestCalculationDate = null;
if(totalInterestEarned != null){
@@ -542,7 +550,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers,
summary, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax,
taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, onHoldAmount);
}
}
@@ -1074,7 +1082,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
// final LocalDate annualFeeNextDueDate = null;
final SavingsAccountSummaryData summary = null;
final BigDecimal onHoldFunds = null;
final BigDecimal savingsAmountOnHold = null;
final SavingsAccountSubStatusEnumData subStatus = null;
final LocalDate lastActiveTransactionDate = null;
final boolean isDormancyTrackingActive = false;
@@ -1090,7 +1099,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers,
summary, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax,
taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
}
}
@@ -612,8 +612,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
public CommandProcessingResult close(final Long savingsId, final JsonCommand command) {
final AppUser user = this.context.authenticatedUser();
this.savingsAccountTransactionDataValidator.validateClosing(command);
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
this.savingsAccountTransactionDataValidator.validateClosing(command, account);
final boolean isLinkedWithAnyActiveLoan = this.accountAssociationsReadPlatformService.isLinkedWithAnyActiveAccount(savingsId);
if (isLinkedWithAnyActiveLoan) {
@@ -1403,7 +1404,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
.validateReleaseAmountAndAssembleForm(holdTransaction, submittedBy);
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
checkClientOrGroupActive(account);
account.releaseFunds(transaction.getAmount());
account.releaseAmount(transaction.getAmount());
this.savingsAccountTransactionRepository.save(transaction);
holdTransaction.updateReleaseId(transaction.getId());
@@ -40,5 +40,9 @@ INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `c
ALTER TABLE `m_savings_account_transaction` ADD COLUMN `release_id_of_hold_amount` BIGINT(20) NULL DEFAULT NULL;
-- modify `m_savings_account`
ALTER TABLE `m_savings_account` ADD COLUMN `total_savings_amount_on_hold` DECIMAL(19,6) NULL DEFAULT NULL;