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