FINERACT-1553: Lien added

This commit is contained in:
logoutdhaval
2022-04-06 16:48:45 +05:30
parent 4648a96a35
commit 52063fc354
27 changed files with 904 additions and 245 deletions
@@ -401,8 +401,8 @@ public class InteropServiceImpl implements InteropService {
PaymentDetail paymentDetail = instance(findPaymentType(), savingsAccount.getExternalId(), null, getRoutingCode(), transferCode,
null);
SavingsAccountTransaction holdTransaction = SavingsAccountTransaction.holdAmount(savingsAccount, savingsAccount.office(),
paymentDetail, transactionDate.toLocalDate(), Money.of(savingsAccount.getCurrency(), total), new Date(),
getLoginUser());
paymentDetail, transactionDate.toLocalDate(), Money.of(savingsAccount.getCurrency(), total), new Date(), getLoginUser(),
false);
MonetaryCurrency accountCurrency = savingsAccount.getCurrency().copy();
holdTransaction.updateRunningBalance(
Money.of(accountCurrency, savingsAccount.getWithdrawableBalance().subtract(holdTransaction.getAmount())));
@@ -135,6 +135,8 @@ public class SavingsApiConstants {
public static final String minOverdraftForInterestCalculationParamName = "minOverdraftForInterestCalculation";
public static final String minRequiredBalanceParamName = "minRequiredBalance";
public static final String enforceMinRequiredBalanceParamName = "enforceMinRequiredBalance";
public static final String maxAllowedLienLimitParamName = "maxAllowedLienLimit";
public static final String lienAllowedParamName = "lienAllowed";
public static final String minBalanceForInterestCalculationParamName = "minBalanceForInterestCalculation";
public static final String withdrawBalanceParamName = "withdrawBalance";
public static final String onHoldFundsParamName = "onHoldFunds";
@@ -18,6 +18,11 @@
*/
package org.apache.fineract.portfolio.savings.api;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.Collection;
import javax.ws.rs.Consumes;
@@ -130,6 +135,9 @@ public class SavingsAccountTransactionsApiResource {
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = SavingsAccountTransactionsApiResourceSwagger.PostSavingsAccountTransactionsRequest.class)))
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = SavingsAccountTransactionsApiResourceSwagger.PostSavingsAccountTransactionsResponse.class))) })
public String transaction(@PathParam("savingsId") final Long savingsId, @QueryParam("command") final String commandParam,
final String apiRequestBodyAsJson) {
try {
@@ -0,0 +1,60 @@
/**
* 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.api;
import io.swagger.v3.oas.annotations.media.Schema;
final class SavingsAccountTransactionsApiResourceSwagger {
private SavingsAccountTransactionsApiResourceSwagger() {}
@Schema(description = "PostSavingsAccountTransactionsRequest")
public static final class PostSavingsAccountTransactionsRequest {
private PostSavingsAccountTransactionsRequest() {}
@Schema(example = "27 March 2022")
public String transactionDate;
@Schema(example = "1000")
public Integer transactionAmount;
@Schema(example = "en")
public String locale;
@Schema(example = "dd MMMM yyyy")
public String dateFormat;
@Schema(example = "true")
public String lienAllowed;
@Schema(example = "String")
public String reasonForBlock;
}
@Schema(description = "PostSavingsAccountTransactionsResponse")
public static final class PostSavingsAccountTransactionsResponse {
private PostSavingsAccountTransactionsResponse() {}
@Schema(example = "1")
public Integer officeId;
@Schema(example = "1")
public Integer clientId;
@Schema(example = "1")
public Integer savingsId;
@Schema(example = "1")
public Integer resourceId;
}
}
@@ -135,7 +135,7 @@ public class SavingsProductsApiResource {
@Operation(summary = "Create a Savings Product", description = "Creates a Savings Product\n\n"
+ "Mandatory Fields: name, shortName, description, currencyCode, digitsAfterDecimal,inMultiplesOf, nominalAnnualInterestRate, interestCompoundingPeriodType, interestCalculationType, interestCalculationDaysInYearType,accountingRule\n\n"
+ "Mandatory Fields for Cash based accounting (accountingRule = 2): savingsReferenceAccountId, savingsControlAccountId, interestOnSavingsAccountId, incomeFromFeeAccountId, transfersInSuspenseAccountId, incomeFromPenaltyAccountId\n\n"
+ "Optional Fields: minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, paymentChannelToFundSourceMappings, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, charges, allowOverdraft, overdraftLimit, minBalanceForInterestCalculation,withHoldTax,taxGroupId,accountMapping")
+ "Optional Fields: minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, paymentChannelToFundSourceMappings, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, charges, allowOverdraft, overdraftLimit, minBalanceForInterestCalculation,withHoldTax,taxGroupId,accountMapping, lienAllowed, maxAllowedLienLimit")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = SavingsProductsApiResourceSwagger.PostSavingsProductsRequest.class)))
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = SavingsProductsApiResourceSwagger.PostSavingsProductsResponse.class))) })
@@ -37,9 +37,9 @@ public class SavingsAccountConstant extends SavingsApiConstants {
lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName,
// withdrawalFeeAmountParamName, withdrawalFeeTypeParamName,
withdrawalFeeForTransfersParamName, feeAmountParamName, feeOnMonthDayParamName, chargesParamName, allowOverdraftParamName,
overdraftLimitParamName, minRequiredBalanceParamName, enforceMinRequiredBalanceParamName,
nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName, withHoldTaxParamName, datatables,
gsimApplicationId, gsimLastApplication));
overdraftLimitParamName, minRequiredBalanceParamName, enforceMinRequiredBalanceParamName, lienAllowedParamName,
maxAllowedLienLimitParamName, nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName,
withHoldTaxParamName, datatables, gsimApplicationId, gsimLastApplication));
/**
* These parameters will match the class level parameters of {@link SavingsAccountData}. Where possible, we try to
@@ -80,6 +80,8 @@ public final class SavingsAccountData implements Serializable {
private final BigDecimal overdraftLimit;
private final BigDecimal minRequiredBalance;
private final boolean enforceMinRequiredBalance;
private final BigDecimal maxAllowedLienLimit;
private final boolean lienAllowed;
private final BigDecimal minBalanceForInterestCalculation;
private final BigDecimal onHoldFunds;
private final boolean withHoldTax;
@@ -191,6 +193,8 @@ public final class SavingsAccountData implements Serializable {
this.overdraftLimit = overdraftLimit;
this.minRequiredBalance = null;
this.enforceMinRequiredBalance = false;
this.maxAllowedLienLimit = null;
this.lienAllowed = false;
this.minBalanceForInterestCalculation = null;
this.onHoldFunds = null;
this.withHoldTax = false;
@@ -546,6 +550,8 @@ public final class SavingsAccountData implements Serializable {
this.overdraftLimit = overdraftLimit;
this.minRequiredBalance = null;
this.enforceMinRequiredBalance = false;
this.maxAllowedLienLimit = null;
this.lienAllowed = false;
this.minBalanceForInterestCalculation = null;
this.onHoldFunds = null;
this.withHoldTax = false;
@@ -594,11 +600,11 @@ public final class SavingsAccountData implements Serializable {
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType,
final boolean withdrawalFeeForTransfers, final SavingsAccountSummaryData summary, final boolean allowOverdraft,
final BigDecimal overdraftLimit, final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance,
final BigDecimal minBalanceForInterestCalculation, 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 BigDecimal savingsAmountOnHold) {
final BigDecimal maxAllowedLienLimit, final boolean lienAllowed, final BigDecimal minBalanceForInterestCalculation,
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 BigDecimal savingsAmountOnHold) {
final Collection<SavingsProductData> productOptions = null;
final Collection<StaffData> fieldOfficerOptions = null;
@@ -620,9 +626,9 @@ public final class SavingsAccountData implements Serializable {
transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, minRequiredBalance,
enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
minOverdraftForInterestCalculation, withHoldTax, taxGroup, lastActiveTransactionDate, isDormancyTrackingActive,
daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation, onHoldFunds,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, lastActiveTransactionDate,
isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
}
public static SavingsAccountData lookup(final Long accountId, final String accountNo, final EnumOptionData depositType) {
@@ -659,6 +665,8 @@ public final class SavingsAccountData implements Serializable {
final BigDecimal minOverdraftForInterestCalculation = null;
final BigDecimal minRequiredBalance = null;
final boolean enforceMinRequiredBalance = false;
final BigDecimal maxAllowedLienLimit = null;
final boolean lienAllowed = false;
final BigDecimal minBalanceForInterestCalculation = null;
final BigDecimal onHoldFunds = null;
@@ -694,9 +702,9 @@ public final class SavingsAccountData implements Serializable {
withdrawalFeeForTransfers, summary, transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit,
minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, lastActiveTransactionDate,
isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
minRequiredBalance, enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation,
onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup,
lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
}
public static SavingsAccountData lookupWithProductDetails(final Long accountId, final String accountNo,
@@ -731,6 +739,8 @@ public final class SavingsAccountData implements Serializable {
final BigDecimal minOverdraftForInterestCalculation = null;
final BigDecimal minRequiredBalance = null;
final boolean enforceMinRequiredBalance = false;
final BigDecimal maxAllowedLienLimit = null;
final boolean lienAllowed = false;
final BigDecimal minBalanceForInterestCalculation = null;
final BigDecimal onHoldFunds = null;
@@ -766,9 +776,9 @@ public final class SavingsAccountData implements Serializable {
withdrawalFeeForTransfers, summary, transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit,
minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, lastActiveTransactionDate,
isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
minRequiredBalance, enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation,
onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup,
lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
}
public static SavingsAccountData withTemplateOptions(final SavingsAccountData account, final SavingsAccountData template,
@@ -801,10 +811,10 @@ public final class SavingsAccountData implements Serializable {
template.interestCalculationTypeOptions, template.interestCalculationDaysInYearTypeOptions,
template.lockinPeriodFrequencyTypeOptions, template.withdrawalFeeTypeOptions, charges, template.chargeOptions,
account.allowOverdraft, 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.savingsAmountOnHold);
account.maxAllowedLienLimit, account.lienAllowed, account.minBalanceForInterestCalculation, account.onHoldFunds,
account.nominalAnnualInterestRateOverdraft, account.minOverdraftForInterestCalculation, account.withHoldTax,
account.taxGroup, account.lastActiveTransactionDate, account.isDormancyTrackingActive, account.daysToInactive,
account.daysToDormancy, account.daysToEscheat, account.savingsAmountOnHold);
}
public static SavingsAccountData withTemplateOptions(final SavingsAccountData account,
@@ -827,10 +837,11 @@ public final class SavingsAccountData implements Serializable {
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges,
chargeOptions, account.allowOverdraft, 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.savingsAmountOnHold);
account.enforceMinRequiredBalance, account.maxAllowedLienLimit, account.lienAllowed,
account.minBalanceForInterestCalculation, account.onHoldFunds, account.nominalAnnualInterestRateOverdraft,
account.minOverdraftForInterestCalculation, account.withHoldTax, account.taxGroup, account.lastActiveTransactionDate,
account.isDormancyTrackingActive, account.daysToInactive, account.daysToDormancy, account.daysToEscheat,
account.savingsAmountOnHold);
}
public static SavingsAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId,
@@ -867,6 +878,8 @@ public final class SavingsAccountData implements Serializable {
final BigDecimal minOverdraftForInterestCalculation = null;
final BigDecimal minRequiredBalance = null;
final boolean enforceMinRequiredBalance = false;
final BigDecimal maxAllowedLienLimit = null;
final boolean lienAllowed = false;
final BigDecimal minBalanceForInterestCalculation = null;
final BigDecimal onHoldFunds = null;
final boolean withHoldTax = false;
@@ -902,9 +915,9 @@ public final class SavingsAccountData implements Serializable {
withdrawalFeeForTransfers, summary, transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit,
minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, lastActiveTransactionDate,
isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
minRequiredBalance, enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation,
onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup,
lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
}
private SavingsAccountData(final Long id, final String accountNo, final EnumOptionData depositType, final String externalId,
@@ -924,11 +937,11 @@ public final class SavingsAccountData implements Serializable {
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
final Collection<SavingsAccountChargeData> charges, final Collection<ChargeData> chargeOptions, final boolean allowOverdraft,
final BigDecimal overdraftLimit, final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance,
final BigDecimal minBalanceForInterestCalculation, 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 BigDecimal savingsAmountOnHold) {
final BigDecimal maxAllowedLienLimit, final boolean lienAllowd, final BigDecimal minBalanceForInterestCalculation,
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 BigDecimal savingsAmountOnHold) {
this.id = id;
this.accountNo = accountNo;
this.depositType = depositType;
@@ -986,6 +999,8 @@ public final class SavingsAccountData implements Serializable {
this.minOverdraftForInterestCalculation = minOverdraftForInterestCalculation;
this.minRequiredBalance = minRequiredBalance;
this.enforceMinRequiredBalance = enforceMinRequiredBalance;
this.maxAllowedLienLimit = maxAllowedLienLimit;
this.lienAllowed = lienAllowd;
this.minBalanceForInterestCalculation = minBalanceForInterestCalculation;
this.onHoldFunds = onHoldFunds;
this.withHoldTax = withHoldTax;
@@ -33,8 +33,10 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interest
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lienAllowedParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.maxAllowedLienLimitParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minOverdraftForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateOverdraftParamName;
@@ -60,6 +62,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
@@ -221,7 +224,7 @@ public class SavingsAccountDataValidator {
validateSavingsCharges(element, baseDataValidator);
validateOverdraftParams(baseDataValidator, element);
validateLienParams(baseDataValidator, element);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
@@ -415,6 +418,29 @@ public class SavingsAccountDataValidator {
}
}
private void validateLienParams(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
if (this.fromApiJsonHelper.parameterExists(lienAllowedParamName, element)) {
final Boolean lienAllowed = this.fromApiJsonHelper.extractBooleanNamed(lienAllowedParamName, element);
baseDataValidator.reset().parameter(lienAllowedParamName).value(lienAllowed).ignoreIfNull().validateForBooleanValue();
}
if (BooleanUtils.isTrue(this.fromApiJsonHelper.extractBooleanNamed(lienAllowedParamName, element))) {
final BigDecimal maxAllowedLienLimit = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxAllowedLienLimitParamName,
element);
baseDataValidator.reset().parameter(maxAllowedLienLimitParamName).value(maxAllowedLienLimit).ignoreIfNull()
.zeroOrPositiveAmount();
}
if (BooleanUtils.isTrue(this.fromApiJsonHelper.extractBooleanNamed(lienAllowedParamName, element))
&& BooleanUtils.isTrue(this.fromApiJsonHelper.extractBooleanNamed(allowOverdraftParamName, element))) {
final BigDecimal maxAllowedLienLimit = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxAllowedLienLimitParamName,
element);
final BigDecimal overdraftLimit = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(overdraftLimitParamName, element);
if (overdraftLimit.compareTo(maxAllowedLienLimit) > 0) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("Overdraft.limit.can.not.be.greater.than.lien.limit");
}
}
}
private void validateOverdraftParams(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
if (this.fromApiJsonHelper.parameterExists(allowOverdraftParamName, element)) {
final Boolean allowOverdraft = this.fromApiJsonHelper.extractBooleanNamed(allowOverdraftParamName, element);
@@ -73,6 +73,7 @@ public final class SavingsAccountTransactionData implements Serializable {
private final boolean isManualTransaction;
private final Boolean isReversal;
private final Long originalTransactionId;
private final Boolean lienTransaction;
private final Long releaseTransactionId;
private final String reasonForBlock;
private Set<SavingsAccountChargesPaidByData> chargesPaidByData = new HashSet<>();
@@ -105,29 +106,31 @@ public final class SavingsAccountTransactionData implements Serializable {
String accountNumber, String checkNumber, String routingCode, String receiptNumber, String bankNumber, Long savingsAccountId,
SavingsAccountTransactionEnumData transactionType, Integer rowIndex, String locale, String dateFormat) {
return new SavingsAccountTransactionData(transactionAmount, transactionDate, paymentTypeId, accountNumber, checkNumber, routingCode,
receiptNumber, bankNumber, savingsAccountId, transactionType, rowIndex, locale, dateFormat);
receiptNumber, bankNumber, savingsAccountId, transactionType, rowIndex, locale, dateFormat, false);
}
public static SavingsAccountTransactionData interestPosting(final SavingsAccountData savingsAccount, final LocalDate date,
final Money amount, final boolean isManualTransaction) {
final boolean isReversed = false;
final Boolean lienTransaction = false;
final SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.INTEREST_POSTING;
SavingsAccountTransactionEnumData savingsAccountTransactionEnumData = new SavingsAccountTransactionEnumData(
savingsAccountTransactionType.getValue().longValue(), savingsAccountTransactionType.getCode(),
savingsAccountTransactionType.getValue().toString());
return new SavingsAccountTransactionData(amount.getAmount(), date, savingsAccount.getId(), savingsAccountTransactionEnumData,
isReversed, null, isManualTransaction);
isReversed, null, isManualTransaction, lienTransaction);
}
public static SavingsAccountTransactionData overdraftInterest(final SavingsAccountData savingsAccount, final LocalDate date,
final Money amount, final boolean isManualTransaction) {
final boolean isReversed = false;
final Boolean lienTransaction = false;
final SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.OVERDRAFT_INTEREST;
SavingsAccountTransactionEnumData savingsAccountTransactionEnumData = new SavingsAccountTransactionEnumData(
savingsAccountTransactionType.getValue().longValue(), savingsAccountTransactionType.getCode(),
savingsAccountTransactionType.getValue().toString());
return new SavingsAccountTransactionData(amount.getAmount(), date, savingsAccount.getId(), savingsAccountTransactionEnumData,
isReversed, null, isManualTransaction);
isReversed, null, isManualTransaction, lienTransaction);
}
public List<TaxDetailsData> getTaxDetails() {
@@ -311,12 +314,13 @@ public final class SavingsAccountTransactionData implements Serializable {
return new SavingsAccountTransactionData(accountTransaction.getSavingsAccountId(), null, accountTransaction.getPaymentDetailData(),
accountTransaction.getTransactionType(), accountTransaction.getTransactionLocalDate(),
accountTransaction.getSubmittedOnDate(), accountTransaction.getAmount(), accountTransaction.isReversed(), null,
accountTransaction.isManualTransaction());
accountTransaction.isManualTransaction(), accountTransaction.lienTransaction);
}
private SavingsAccountTransactionData(final Long savingsId, final Long officeId, final PaymentDetailData paymentDetailData,
final SavingsAccountTransactionEnumData savingsAccountTransactionType, final LocalDate transactionDate, final Date createdDate,
final BigDecimal amount, final boolean isReversed, final Long userId, final boolean isManualTransaction) {
final BigDecimal amount, final boolean isReversed, final Long userId, final boolean isManualTransaction,
final Boolean lienTransaction) {
this.savingsAccountId = savingsId;
this.paymentDetailData = paymentDetailData;
this.transactionType = savingsAccountTransactionType;
@@ -324,6 +328,7 @@ public final class SavingsAccountTransactionData implements Serializable {
this.submittedOnDate = createdDate;
this.amount = amount;
this.isManualTransaction = isManualTransaction;
this.lienTransaction = lienTransaction;
this.id = null;
this.accountId = null;
this.accountNo = null;
@@ -442,12 +447,13 @@ public final class SavingsAccountTransactionData implements Serializable {
final Money amount, final Map<TaxComponentData, BigDecimal> taxDetails) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.WITHHOLD_TAX;
SavingsAccountTransactionEnumData transactionType = new SavingsAccountTransactionEnumData(
savingsAccountTransactionType.getValue().longValue(), savingsAccountTransactionType.getCode(),
savingsAccountTransactionType.getValue().toString());
SavingsAccountTransactionData accountTransaction = new SavingsAccountTransactionData(amount.getAmount(), date,
savingsAccount.getId(), transactionType, isReversed, null, isManualTransaction);
savingsAccount.getId(), transactionType, isReversed, null, isManualTransaction, lienTransaction);
updateTaxDetails(taxDetails, accountTransaction);
return accountTransaction;
}
@@ -515,7 +521,8 @@ public final class SavingsAccountTransactionData implements Serializable {
}
private SavingsAccountTransactionData(BigDecimal transactionAmount, LocalDate transactionDate, Long savingsAccountId,
SavingsAccountTransactionEnumData transactionType, boolean isReversed, String locale, boolean isManualTransaction) {
SavingsAccountTransactionEnumData transactionType, boolean isReversed, String locale, boolean isManualTransaction,
final Boolean lienTransaction) {
this.id = null;
this.transactionType = transactionType;
this.accountId = null;
@@ -548,13 +555,15 @@ public final class SavingsAccountTransactionData implements Serializable {
this.isManualTransaction = isManualTransaction;
this.isReversal = null;
this.originalTransactionId = null;
this.lienTransaction = lienTransaction;
this.releaseTransactionId = null;
this.reasonForBlock = null;
}
private SavingsAccountTransactionData(BigDecimal transactionAmount, LocalDate transactionDate, Long paymentTypeId, String accountNumber,
String checkNumber, String routingCode, String receiptNumber, String bankNumber, Long savingsAccountId,
SavingsAccountTransactionEnumData transactionType, Integer rowIndex, String locale, String dateFormat) {
SavingsAccountTransactionEnumData transactionType, Integer rowIndex, String locale, String dateFormat,
final Boolean lienTransaction) {
this.id = null;
this.transactionType = transactionType;
this.accountId = null;
@@ -587,6 +596,7 @@ public final class SavingsAccountTransactionData implements Serializable {
this.isManualTransaction = false;
this.isReversal = null;
this.originalTransactionId = null;
this.lienTransaction = lienTransaction;
this.releaseTransactionId = null;
this.reasonForBlock = null;
}
@@ -594,7 +604,7 @@ public final class SavingsAccountTransactionData implements Serializable {
private SavingsAccountTransactionData(Integer id, BigDecimal transactionAmount, LocalDate transactionDate, Long paymentTypeId,
String accountNumber, String checkNumber, String routingCode, String receiptNumber, String bankNumber, Long savingsAccountId,
SavingsAccountTransactionEnumData transactionType, Integer rowIndex, String locale, String dateFormat,
BigDecimal cumulativeBalance) {
BigDecimal cumulativeBalance, final Boolean lienTransaction) {
this.id = null;
this.transactionType = transactionType;
this.accountId = null;
@@ -628,6 +638,7 @@ public final class SavingsAccountTransactionData implements Serializable {
this.isManualTransaction = false;
this.isReversal = null;
this.originalTransactionId = null;
this.lienTransaction = lienTransaction;
this.releaseTransactionId = null;
this.reasonForBlock = null;
}
@@ -721,24 +732,27 @@ public final class SavingsAccountTransactionData implements Serializable {
final Collection<PaymentTypeData> paymentTypeOptions = null;
return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency,
amount, outstandingChargeAmount, runningBalance, reversed, submittedOnDate, paymentTypeOptions, interestedPostedAsOn,
cumulativeBalance, balanceEndDate);
cumulativeBalance, balanceEndDate, false);
}
private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
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 boolean reversed, final LocalDate submittedOnDate, final Collection<PaymentTypeData> paymentTypeOptions,
final boolean interestedPostedAsOn, final BigDecimal cumulativeBalance, final LocalDate balanceEndDate) {
final boolean interestedPostedAsOn, final BigDecimal cumulativeBalance, final LocalDate balanceEndDate,
final Boolean lienTransaction) {
this(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency, amount, outstandingChargeAmount,
runningBalance, reversed, paymentTypeOptions, submittedOnDate, interestedPostedAsOn, cumulativeBalance, balanceEndDate);
runningBalance, reversed, paymentTypeOptions, submittedOnDate, interestedPostedAsOn, cumulativeBalance, balanceEndDate,
lienTransaction);
}
private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
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 boolean reversed, final Collection<PaymentTypeData> paymentTypeOptions, final LocalDate submittedOnDate,
final boolean interestedPostedAsOn, final BigDecimal cumulativeBalance, final LocalDate balanceEndDate) {
final boolean interestedPostedAsOn, final BigDecimal cumulativeBalance, final LocalDate balanceEndDate,
final Boolean lienTransaction) {
this.id = id;
this.transactionDate = date;
this.transactionType = transactionType;
@@ -762,6 +776,7 @@ public final class SavingsAccountTransactionData implements Serializable {
this.isManualTransaction = false;
this.isReversal = null;
this.originalTransactionId = null;
this.lienTransaction = lienTransaction;
this.releaseTransactionId = null;
this.reasonForBlock = null;
}
@@ -774,7 +789,7 @@ public final class SavingsAccountTransactionData implements Serializable {
final Collection<PaymentTypeData> paymentTypeOptions = null;
return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency,
amount, outstandingChargeAmount, runningBalance, reversed, transfer, paymentTypeOptions, interestedPostedAsOn,
submittedByUsername, note);
submittedByUsername, note, false);
}
public static SavingsAccountTransactionData create(final Long id, final SavingsAccountTransactionEnumData transactionType,
@@ -782,17 +797,18 @@ public final class SavingsAccountTransactionData implements Serializable {
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 String submittedByUsername, final String note, final Boolean isReversal, final Long originalTransactionId,
final Long releaseTransactionId, final String reasonForBlock) {
final Boolean lienTransaction, final Long releaseTransactionId, final String reasonForBlock) {
final Collection<PaymentTypeData> paymentTypeOptions = null;
return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency,
amount, outstandingChargeAmount, runningBalance, reversed, transfer, paymentTypeOptions, submittedOnDate,
interestedPostedAsOn, submittedByUsername, note, isReversal, originalTransactionId, releaseTransactionId, reasonForBlock);
interestedPostedAsOn, submittedByUsername, note, isReversal, originalTransactionId, lienTransaction, releaseTransactionId,
reasonForBlock);
}
public static SavingsAccountTransactionData create(final Long id) {
final Collection<PaymentTypeData> paymentTypeOptions = null;
return new SavingsAccountTransactionData(id, null, null, null, null, null, null, null, null, null, false, null, paymentTypeOptions,
null, false, null, null, null, null);
null, false, null, null, null, null, false, null, null);
}
public static SavingsAccountTransactionData template(final Long savingsId, final String savingsAccountNo,
@@ -808,9 +824,10 @@ public final class SavingsAccountTransactionData implements Serializable {
final boolean interestedPostedAsOn = false;
final String submittedByUsername = null;
final String note = null;
final Boolean lienTransaction = false;
return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, defaultLocalDate,
currency, amount, outstandingChargeAmount, runningBalance, reversed, null, null, interestedPostedAsOn, submittedByUsername,
note);
note, lienTransaction);
}
public static SavingsAccountTransactionData templateOnTop(final SavingsAccountTransactionData savingsAccountTransactionData,
@@ -821,17 +838,19 @@ public final class SavingsAccountTransactionData implements Serializable {
savingsAccountTransactionData.amount, savingsAccountTransactionData.outstandingChargeAmount,
savingsAccountTransactionData.runningBalance, savingsAccountTransactionData.reversed,
savingsAccountTransactionData.transfer, paymentTypeOptions, savingsAccountTransactionData.interestedPostedAsOn,
savingsAccountTransactionData.submittedByUsername, savingsAccountTransactionData.note);
savingsAccountTransactionData.submittedByUsername, savingsAccountTransactionData.note,
savingsAccountTransactionData.lienTransaction);
}
private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
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 boolean reversed, final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions,
final boolean interestedPostedAsOn, final String submittedByUsername, final String note) {
final boolean interestedPostedAsOn, final String submittedByUsername, final String note, final Boolean lienTransaction) {
this(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency, amount, outstandingChargeAmount,
runningBalance, reversed, transfer, paymentTypeOptions, date, interestedPostedAsOn, submittedByUsername, note, null, null);
runningBalance, reversed, transfer, paymentTypeOptions, date, interestedPostedAsOn, submittedByUsername, note, null, null,
lienTransaction, null, null);
}
private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
@@ -839,7 +858,8 @@ public final class SavingsAccountTransactionData implements Serializable {
final CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance,
final boolean reversed, final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions,
final LocalDate submittedOnDate, final boolean interestedPostedAsOn, final String submittedByUsername, final String note,
final Boolean isReversal, final Long originalTransactionId, final Long releaseTransactionId, final String reasonForBlock) {
final Boolean isReversal, final Long originalTransactionId, final Boolean lienTransaction, final Long releaseTransactionId,
final String reasonForBlock) {
this.id = id;
this.transactionType = transactionType;
this.paymentDetailData = paymentDetailData;
@@ -865,6 +885,7 @@ public final class SavingsAccountTransactionData implements Serializable {
this.isManualTransaction = false;
this.isReversal = isReversal;
this.originalTransactionId = originalTransactionId;
this.lienTransaction = lienTransaction;
this.releaseTransactionId = releaseTransactionId;
this.reasonForBlock = reasonForBlock;
}
@@ -873,7 +894,8 @@ public final class SavingsAccountTransactionData implements Serializable {
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 boolean reversed, final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions,
final LocalDate submittedOnDate, final boolean interestedPostedAsOn, final BigDecimal cumulativeBalance) {
final LocalDate submittedOnDate, final boolean interestedPostedAsOn, final BigDecimal cumulativeBalance,
final Boolean lienTransaction) {
this.id = id;
this.transactionType = transactionType;
this.paymentDetailData = paymentDetailData;
@@ -900,41 +922,7 @@ public final class SavingsAccountTransactionData implements Serializable {
this.isManualTransaction = false;
this.isReversal = null;
this.originalTransactionId = null;
this.releaseTransactionId = null;
this.reasonForBlock = null;
}
private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
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 boolean reversed, final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions,
final LocalDate submittedOnDate, final boolean interestedPostedAsOn, final String submittedByUsername, final String note,
final Boolean isReversal, final Long originalTransactionId) {
this.id = id;
this.transactionType = transactionType;
this.paymentDetailData = paymentDetailData;
this.accountId = savingsId;
this.accountNo = savingsAccountNo;
this.date = date;
this.currency = currency;
this.amount = amount;
this.outstandingChargeAmount = outstandingChargeAmount;
this.runningBalance = runningBalance;
this.reversed = reversed;
this.transfer = transfer;
this.paymentTypeOptions = paymentTypeOptions;
if (submittedOnDate != null) {
this.submittedOnDate = Date.from(submittedOnDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
} else {
this.submittedOnDate = null;
}
this.interestedPostedAsOn = interestedPostedAsOn;
this.submittedByUsername = submittedByUsername;
this.note = note;
this.isManualTransaction = false;
this.isReversal = isReversal;
this.originalTransactionId = originalTransactionId;
this.lienTransaction = lienTransaction;
this.releaseTransactionId = null;
this.reasonForBlock = null;
}
@@ -953,7 +941,7 @@ public final class SavingsAccountTransactionData implements Serializable {
savingsAccountTransactionData.runningBalance, savingsAccountTransactionData.reversed,
savingsAccountTransactionData.transfer, savingsAccountTransactionData.paymentTypeOptions,
savingsAccountTransactionData.interestedPostedAsOn, savingsAccountTransactionData.submittedByUsername,
savingsAccountTransactionData.note);
savingsAccountTransactionData.note, savingsAccountTransactionData.lienTransaction);
}
public void setId(final Long id) {
@@ -23,7 +23,7 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.activate
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.bankNumberParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.checkNumberParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.closedOnDateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lienParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lienAllowedParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.paymentTypeIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.receiptNumberParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.routingCodeParamName;
@@ -44,6 +44,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
@@ -53,8 +54,6 @@ import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
@@ -71,8 +70,7 @@ public class SavingsAccountTransactionDataValidator {
private final FromJsonHelper fromApiJsonHelper;
private static final Set<String> SAVINGS_ACCOUNT_HOLD_AMOUNT_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(transactionDateParamName, SavingsApiConstants.dateFormatParamName, SavingsApiConstants.localeParamName,
transactionAmountParamName, lienParamName, SavingsApiConstants.reasonForBlockParamName));
transactionAmountParamName, lienAllowedParamName, SavingsApiConstants.reasonForBlockParamName));
private final ConfigurationDomainService configurationDomainService;
private final SavingsAccountAssembler savingAccountAssembler;
@@ -217,7 +215,7 @@ public class SavingsAccountTransactionDataValidator {
}
public SavingsAccountTransaction validateHoldAndAssembleForm(final String json, final SavingsAccount account, final AppUser createdUser,
public void validateHoldAndAssembleForm(final String json, final SavingsAccount account, final AppUser createdUser,
final boolean backdatedTxnsAllowedTill) {
if (StringUtils.isBlank(json)) {
@@ -249,21 +247,43 @@ public class SavingsAccountTransactionDataValidator {
.failWithCodeNoParameterAddedToErrorCode(SavingsApiConstants.ERROR_MSG_SAVINGS_ACCOUNT_NOT_ACTIVE);
}
Boolean lien = false;
if (this.fromApiJsonHelper.parameterExists(lienParamName, element)) {
lien = this.fromApiJsonHelper.extractBooleanNamed(lienParamName, element);
}
Boolean isEnforceMinRequiredBalanceEnabled = account.getEnforceMinRequiredBalance();
Boolean isAccountLienEnabled = account.isLienAllowed();
Boolean isOverdraftEnabled = account.isAllowOverdraft();
if (account.getEnforceMinRequiredBalance()) {
if (account.getWithdrawableBalance().compareTo(BigDecimal.ZERO) < 0) {
if (!lien) {
if (account.getWithdrawableBalanceWithoutMinimumBalance().compareTo(account.getMinRequiredBalance()) < 0) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("insufficient balance", account.getId());
Boolean lienAllowed = false;
if (BooleanUtils.isTrue(fromApiJsonHelper.extractBooleanNamed(lienAllowedParamName, element))) {
lienAllowed = this.fromApiJsonHelper.extractBooleanNamed(lienAllowedParamName, element);
if (isAccountLienEnabled) {
if (isOverdraftEnabled) {
if (account.getOverdraftLimit().compareTo(account.getMaxAllowedLienLimit()) > 0) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
"Overdraft.limit.can.not.be.greater.than.lien.limit", account.getId());
}
}
if (amount.compareTo(account.getMaxAllowedLienLimit()) > 0) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("lien.limit.exceeded", account.getId());
}
} else {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("lien.is.not.allowed.in.product.level", account.getId());
}
} else {
if (isOverdraftEnabled) {
if (amount.compareTo(account.getWithdrawableBalance()) > 0 && amount.compareTo(account.getOverdraftLimit()) > 0) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("insufficient.balance", account.getId());
}
}
if (isEnforceMinRequiredBalanceEnabled) {
if (amount.compareTo(account.getWithdrawableBalance()) > 0) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("insufficient.balance", account.getId());
}
}
if (!isOverdraftEnabled && !isEnforceMinRequiredBalanceEnabled) {
if (amount.compareTo(account.getWithdrawableBalance()) > 0) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("insufficient.balance", account.getId());
}
}
}
LocalDate lastTransactionDate = null;
if (!backdatedTxnsAllowedTill) {
@@ -279,13 +299,6 @@ public class SavingsAccountTransactionDataValidator {
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
final PaymentDetail paymentDetails = null;
Date createdDate = new Date();
SavingsAccountTransaction transaction = SavingsAccountTransaction.holdAmount(account, account.office(), paymentDetails,
transactionDate, Money.of(account.getCurrency(), amount), createdDate, createdUser);
return transaction;
}
public SavingsAccountTransaction validateReleaseAmountAndAssembleForm(final SavingsAccountTransaction holdTransaction,
@@ -312,7 +325,6 @@ public class SavingsAccountTransactionDataValidator {
throwExceptionIfValidationWarningsExist(dataValidationErrors);
Date createdDate = new Date();
LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
SavingsAccountTransaction transaction = SavingsAccountTransaction.releaseAmount(holdTransaction, transactionDate, createdDate,
createdUser);
return transaction;
@@ -56,6 +56,8 @@ public final class SavingsProductData implements Serializable {
private final BigDecimal overdraftLimit;
private final BigDecimal minRequiredBalance;
private final boolean enforceMinRequiredBalance;
private final BigDecimal maxAllowedLienLimit;
private final boolean lienAllowed;
private final BigDecimal minBalanceForInterestCalculation;
private final BigDecimal nominalAnnualInterestRateOverdraft;
private final BigDecimal minOverdraftForInterestCalculation;
@@ -124,6 +126,8 @@ public final class SavingsProductData implements Serializable {
final BigDecimal overdraftLimit = null;
final BigDecimal minRequiredBalance = null;
final boolean enforceMinRequiredBalance = false;
final BigDecimal maxAllowedLienLimit = null;
final boolean lienAllowed = false;
final BigDecimal minBalanceForInterestCalculation = null;
final BigDecimal nominalAnnualInterestRateOverdraft = null;
final BigDecimal minOverdraftForInterestCalculation = null;
@@ -141,9 +145,9 @@ public final class SavingsProductData implements Serializable {
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, charges, chargeOptions,
penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit,
minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft,
minOverdraftForInterestCalculation, withHoldTax, taxGroup, taxGroupOptions, isDormancyTrackingActive, daysToInactive,
daysToDormancy, daysToEscheat, accountMappingForPayment);
minRequiredBalance, enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, taxGroupOptions,
isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, accountMappingForPayment);
}
public static SavingsProductData withCharges(final SavingsProductData product, final Collection<ChargeData> charges) {
@@ -157,10 +161,11 @@ public final class SavingsProductData implements Serializable {
product.lockinPeriodFrequencyTypeOptions, product.withdrawalFeeTypeOptions, product.paymentTypeOptions,
product.accountingRuleOptions, product.accountingMappingOptions, charges, product.chargeOptions, product.penaltyOptions,
product.feeToIncomeAccountMappings, product.penaltyToIncomeAccountMappings, product.allowOverdraft, product.overdraftLimit,
product.minRequiredBalance, product.enforceMinRequiredBalance, product.minBalanceForInterestCalculation,
product.nominalAnnualInterestRateOverdraft, product.minOverdraftForInterestCalculation, product.withHoldTax,
product.taxGroup, product.taxGroupOptions, product.isDormancyTrackingActive, product.daysToInactive, product.daysToDormancy,
product.daysToEscheat, product.accountMappingForPayment);
product.minRequiredBalance, product.enforceMinRequiredBalance, product.maxAllowedLienLimit, product.lienAllowed,
product.minBalanceForInterestCalculation, product.nominalAnnualInterestRateOverdraft,
product.minOverdraftForInterestCalculation, product.withHoldTax, product.taxGroup, product.taxGroupOptions,
product.isDormancyTrackingActive, product.daysToInactive, product.daysToDormancy, product.daysToEscheat,
product.accountMappingForPayment);
}
/**
@@ -192,10 +197,10 @@ public final class SavingsProductData implements Serializable {
accountingRuleOptions, accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions,
existingProduct.feeToIncomeAccountMappings, existingProduct.penaltyToIncomeAccountMappings, existingProduct.allowOverdraft,
existingProduct.overdraftLimit, existingProduct.minRequiredBalance, existingProduct.enforceMinRequiredBalance,
existingProduct.minBalanceForInterestCalculation, existingProduct.nominalAnnualInterestRateOverdraft,
existingProduct.minOverdraftForInterestCalculation, existingProduct.withHoldTax, existingProduct.taxGroup, taxGroupOptions,
existingProduct.isDormancyTrackingActive, existingProduct.daysToInactive, existingProduct.daysToDormancy,
existingProduct.daysToEscheat, accountMappingForPayment);
existingProduct.maxAllowedLienLimit, existingProduct.lienAllowed, existingProduct.minBalanceForInterestCalculation,
existingProduct.nominalAnnualInterestRateOverdraft, existingProduct.minOverdraftForInterestCalculation,
existingProduct.withHoldTax, existingProduct.taxGroup, taxGroupOptions, existingProduct.isDormancyTrackingActive,
existingProduct.daysToInactive, existingProduct.daysToDormancy, existingProduct.daysToEscheat, accountMappingForPayment);
}
public static SavingsProductData withAccountingDetails(final SavingsProductData existingProduct,
@@ -227,11 +232,12 @@ public final class SavingsProductData implements Serializable {
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions,
accountingRuleOptions, accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions,
feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, existingProduct.allowOverdraft, existingProduct.overdraftLimit,
existingProduct.minRequiredBalance, existingProduct.enforceMinRequiredBalance,
existingProduct.minBalanceForInterestCalculation, existingProduct.nominalAnnualInterestRateOverdraft,
existingProduct.minOverdraftForInterestCalculation, existingProduct.withHoldTax, existingProduct.taxGroup,
existingProduct.taxGroupOptions, existingProduct.isDormancyTrackingActive, existingProduct.daysToInactive,
existingProduct.daysToDormancy, existingProduct.daysToEscheat, existingProduct.accountMappingForPayment);
existingProduct.minRequiredBalance, existingProduct.enforceMinRequiredBalance, existingProduct.maxAllowedLienLimit,
existingProduct.lienAllowed, existingProduct.minBalanceForInterestCalculation,
existingProduct.nominalAnnualInterestRateOverdraft, existingProduct.minOverdraftForInterestCalculation,
existingProduct.withHoldTax, existingProduct.taxGroup, existingProduct.taxGroupOptions,
existingProduct.isDormancyTrackingActive, existingProduct.daysToInactive, existingProduct.daysToDormancy,
existingProduct.daysToEscheat, existingProduct.accountMappingForPayment);
}
public static SavingsProductData instance(final Long id, final String name, final String shortName, final String description,
@@ -240,7 +246,8 @@ public final class SavingsProductData implements Serializable {
final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers,
final EnumOptionData accountingType, final boolean allowOverdraft, final BigDecimal overdraftLimit,
final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance, final BigDecimal minBalanceForInterestCalculation,
final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance, final BigDecimal maxAllowedLienLimit,
final boolean lienAllowed, final BigDecimal minBalanceForInterestCalculation,
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation,
final boolean withHoldTax, final TaxGroupData taxGroup, final Boolean isDormancyTrackingActive, final Long daysToInactive,
final Long daysToDormancy, final Long daysToEscheat) {
@@ -273,9 +280,9 @@ public final class SavingsProductData implements Serializable {
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, charges, chargeOptions,
penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit,
minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft,
minOverdraftForInterestCalculation, withHoldTax, taxGroup, taxGroupOptions, isDormancyTrackingActive, daysToInactive,
daysToDormancy, daysToEscheat, accountMappingForPayment);
minRequiredBalance, enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, taxGroupOptions,
isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, accountMappingForPayment);
}
public static SavingsProductData lookup(final Long id, final String name) {
@@ -301,6 +308,8 @@ public final class SavingsProductData implements Serializable {
final BigDecimal minOverdraftForInterestCalculation = null;
final BigDecimal minRequiredBalance = null;
final boolean enforceMinRequiredBalance = false;
final BigDecimal maxAllowedLienLimit = null;
final boolean lienAllowed = false;
final BigDecimal minBalanceForInterestCalculation = null;
final boolean withHoldTax = false;
final TaxGroupData taxGroup = null;
@@ -334,9 +343,9 @@ public final class SavingsProductData implements Serializable {
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, charges, chargeOptions,
penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit,
minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft,
minOverdraftForInterestCalculation, withHoldTax, taxGroup, taxGroupOptions, isDormancyTrackingActive, daysToInactive,
daysToDormancy, daysToEscheat, accountMappingForPayment);
minRequiredBalance, enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, taxGroupOptions,
isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, accountMappingForPayment);
}
public static SavingsProductData createForInterestPosting(final Long id, final EnumOptionData accountingRule) {
@@ -387,6 +396,8 @@ public final class SavingsProductData implements Serializable {
this.overdraftLimit = null;
this.minRequiredBalance = null;
this.enforceMinRequiredBalance = false;
this.maxAllowedLienLimit = null;
this.lienAllowed = false;
this.minBalanceForInterestCalculation = null;
this.nominalAnnualInterestRateOverdraft = null;
this.minOverdraftForInterestCalculation = null;
@@ -418,10 +429,11 @@ public final class SavingsProductData implements Serializable {
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings,
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings, final boolean allowOverdraft,
final BigDecimal overdraftLimit, final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance,
final BigDecimal minBalanceForInterestCalculation, final BigDecimal nominalAnnualInterestRateOverdraft,
final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax, final TaxGroupData taxGroup,
final Collection<TaxGroupData> taxGroupOptions, final Boolean isDormancyTrackingActive, final Long daysToInactive,
final Long daysToDormancy, final Long daysToEscheat, final String accountMappingForPayment) {
final BigDecimal maxAllowedLienLimit, final boolean lienAllowed, final BigDecimal minBalanceForInterestCalculation,
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation,
final boolean withHoldTax, final TaxGroupData taxGroup, final Collection<TaxGroupData> taxGroupOptions,
final Boolean isDormancyTrackingActive, final Long daysToInactive, final Long daysToDormancy, final Long daysToEscheat,
final String accountMappingForPayment) {
this.id = id;
this.name = name;
this.shortName = shortName;
@@ -468,6 +480,8 @@ public final class SavingsProductData implements Serializable {
this.overdraftLimit = overdraftLimit;
this.minRequiredBalance = minRequiredBalance;
this.enforceMinRequiredBalance = enforceMinRequiredBalance;
this.maxAllowedLienLimit = maxAllowedLienLimit;
this.lienAllowed = lienAllowed;
this.minBalanceForInterestCalculation = minBalanceForInterestCalculation;
this.nominalAnnualInterestRateOverdraft = nominalAnnualInterestRateOverdraft;
this.minOverdraftForInterestCalculation = minOverdraftForInterestCalculation;
@@ -563,6 +577,10 @@ public final class SavingsProductData implements Serializable {
return minRequiredBalance;
}
public BigDecimal getMaxAllowedLienLimit() {
return maxAllowedLienLimit;
}
public Long getId() {
return id;
}
@@ -34,8 +34,10 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interest
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.isDormancyTrackingActiveParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lienAllowedParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.maxAllowedLienLimitParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minBalanceForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minOverdraftForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName;
@@ -62,6 +64,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.accounting.common.AccountingConstants.SavingProductAccountingParams;
import org.apache.fineract.accounting.common.AccountingRuleType;
@@ -103,6 +106,7 @@ public class SavingsProductDataValidator {
daysToInactiveParamName, daysToEscheatParamName, allowOverdraftParamName, overdraftLimitParamName,
nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName,
SavingsApiConstants.minRequiredBalanceParamName, SavingsApiConstants.enforceMinRequiredBalanceParamName,
SavingsApiConstants.maxAllowedLienLimitParamName, SavingsApiConstants.lienAllowedParamName,
minBalanceForInterestCalculationParamName, withHoldTaxParamName, taxGroupIdParamName));
@Autowired
@@ -340,9 +344,8 @@ public class SavingsProductDataValidator {
baseDataValidator.reset().parameter(minBalanceForInterestCalculationParamName).value(minBalanceForInterestCalculation)
.ignoreIfNull().zeroOrPositiveAmount();
}
validateTaxWithHoldingParams(baseDataValidator, element, true);
validateLienParams(baseDataValidator, element);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
@@ -545,7 +548,7 @@ public class SavingsProductDataValidator {
}
validateTaxWithHoldingParams(baseDataValidator, element, false);
validateLienParams(baseDataValidator, element);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
@@ -650,6 +653,29 @@ public class SavingsProductDataValidator {
}
private void validateLienParams(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
if (this.fromApiJsonHelper.parameterExists(lienAllowedParamName, element)) {
final Boolean lienAllowed = this.fromApiJsonHelper.extractBooleanNamed(lienAllowedParamName, element);
baseDataValidator.reset().parameter(lienAllowedParamName).value(lienAllowed).ignoreIfNull().validateForBooleanValue();
}
if (this.fromApiJsonHelper.parameterExists(maxAllowedLienLimitParamName, element)) {
final BigDecimal maxAllowedLienLimit = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxAllowedLienLimitParamName,
element);
baseDataValidator.reset().parameter(maxAllowedLienLimitParamName).value(maxAllowedLienLimit).ignoreIfNull()
.zeroOrPositiveAmount();
}
if (BooleanUtils.isTrue(this.fromApiJsonHelper.extractBooleanNamed(lienAllowedParamName, element))
&& BooleanUtils.isTrue(this.fromApiJsonHelper.extractBooleanNamed(allowOverdraftParamName, element))) {
final BigDecimal maxAllowedLienLimit = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxAllowedLienLimitParamName,
element);
final BigDecimal overdraftLimit = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(overdraftLimitParamName, element);
if (overdraftLimit.compareTo(maxAllowedLienLimit) > 0) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("Overdraft.limit.can.not.be.greater.than.lien.limit");
}
}
}
private void validateTaxWithHoldingParams(final DataValidatorBuilder baseDataValidator, final JsonElement element,
final boolean isCreate) {
if (this.fromApiJsonHelper.parameterExists(withHoldTaxParamName, element)) {
@@ -22,9 +22,11 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.SAVINGS_
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.allowOverdraftParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.enforceMinRequiredBalanceParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lienAllowedParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.localeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.maxAllowedLienLimitParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minOverdraftForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredBalanceParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateOverdraftParamName;
@@ -293,6 +295,12 @@ public class SavingsAccount extends AbstractPersistableCustom {
@Column(name = "min_required_balance", scale = 6, precision = 19, nullable = true)
private BigDecimal minRequiredBalance;
@Column(name = "is_lien_allowed", nullable = false)
private boolean lienAllowed;
@Column(name = "max_allowed_lien_limit", scale = 6, precision = 19, nullable = true)
private BigDecimal maxAllowedLienLimit;
@Column(name = "on_hold_funds_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal onHoldFunds;
@@ -337,7 +345,6 @@ public class SavingsAccount extends AbstractPersistableCustom {
@Column(name = "total_savings_amount_on_hold", scale = 6, precision = 19, nullable = true)
private BigDecimal savingsOnHoldAmount;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "account", orphanRemoval = true, fetch = FetchType.LAZY)
protected List<InteropIdentifier> identifiers = new ArrayList<>();
@@ -354,15 +361,17 @@ public class SavingsAccount extends AbstractPersistableCustom {
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final boolean withdrawalFeeApplicableForTransfer, final Set<SavingsAccountCharge> savingsAccountCharges,
final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance,
final BigDecimal minRequiredBalance, final BigDecimal nominalAnnualInterestRateOverdraft,
final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax) {
final BigDecimal minRequiredBalance, final BigDecimal maxAllowedLienLimit, final boolean lienAllowed,
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation,
final boolean withHoldTax) {
final SavingsAccountStatusType status = SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL;
return new SavingsAccount(client, group, product, fieldOfficer, accountNo, externalId, status, accountType, submittedOnDate,
submittedBy, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
withdrawalFeeApplicableForTransfer, savingsAccountCharges, allowOverdraft, overdraftLimit, enforceMinRequiredBalance,
minRequiredBalance, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax);
minRequiredBalance, maxAllowedLienLimit, lienAllowed, nominalAnnualInterestRateOverdraft,
minOverdraftForInterestCalculation, withHoldTax);
}
protected SavingsAccount(final Client client, final Group group, final SavingsProduct product, final Staff fieldOfficer,
@@ -377,8 +386,8 @@ public class SavingsAccount extends AbstractPersistableCustom {
this(client, group, product, fieldOfficer, accountNo, externalId, status, accountType, submittedOnDate, submittedBy,
nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
withdrawalFeeApplicableForTransfer, savingsAccountCharges, allowOverdraft, overdraftLimit, false, null, null, null,
withHoldTax);
withdrawalFeeApplicableForTransfer, savingsAccountCharges, allowOverdraft, overdraftLimit, false, null, null, false, null,
null, withHoldTax);
}
protected SavingsAccount(final Client client, final Group group, final SavingsProduct product, final Staff savingsOfficer,
@@ -390,8 +399,8 @@ public class SavingsAccount extends AbstractPersistableCustom {
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final boolean withdrawalFeeApplicableForTransfer, final Set<SavingsAccountCharge> savingsAccountCharges,
final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance,
final BigDecimal minRequiredBalance, final BigDecimal nominalAnnualInterestRateOverdraft,
final BigDecimal minOverdraftForInterestCalculation, boolean withHoldTax) {
final BigDecimal minRequiredBalance, final BigDecimal maxAllowedLienLimit, final boolean lienAllowed,
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation, boolean withHoldTax) {
this.client = client;
this.group = group;
this.product = product;
@@ -434,6 +443,8 @@ public class SavingsAccount extends AbstractPersistableCustom {
this.enforceMinRequiredBalance = enforceMinRequiredBalance;
this.minRequiredBalance = minRequiredBalance;
this.lienAllowed = lienAllowed;
this.maxAllowedLienLimit = maxAllowedLienLimit;
this.minBalanceForInterestCalculation = product.minBalanceForInterestCalculation();
// this.savingsOfficerHistory = null;
this.withHoldTax = withHoldTax;
@@ -1751,6 +1762,18 @@ public class SavingsAccount extends AbstractPersistableCustom {
actualChanges.put(localeParamName, localeAsInput);
this.minRequiredBalance = newValue;
}
if (command.isChangeInBooleanParameterNamed(lienAllowedParamName, this.lienAllowed)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(lienAllowedParamName);
actualChanges.put(lienAllowedParamName, newValue);
this.lienAllowed = newValue;
}
if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(maxAllowedLienLimitParamName, this.maxAllowedLienLimit)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(maxAllowedLienLimitParamName);
actualChanges.put(maxAllowedLienLimitParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
this.maxAllowedLienLimit = newValue;
}
if (command.isChangeInBooleanParameterNamed(withHoldTaxParamName, this.withHoldTax)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName);
@@ -1950,6 +1973,10 @@ public class SavingsAccount extends AbstractPersistableCustom {
return this.enforceMinRequiredBalance;
}
public Boolean getLienAllowed() {
return this.lienAllowed;
}
public void unassignSavingsOfficer() {
this.savingsOfficer = null;
}
@@ -3429,6 +3456,11 @@ public class SavingsAccount extends AbstractPersistableCustom {
.subtract(this.getSavingsHoldAmount());
}
public BigDecimal getWithdrawableBalanceWithLien() {
return getAccountBalance().subtract(minRequiredBalanceDerived(getCurrency()).getAmount()).subtract(this.getOnHoldFunds())
.subtract(this.getSavingsHoldAmount());
}
public BigDecimal getWithdrawableBalanceWithoutMinimumBalance() {
return getAccountBalance().subtract(this.getOnHoldFunds()).subtract(this.getSavingsHoldAmount());
}
@@ -3833,6 +3865,14 @@ public class SavingsAccount extends AbstractPersistableCustom {
return this.enforceMinRequiredBalance;
}
public BigDecimal getMaxAllowedLienLimit() {
return this.maxAllowedLienLimit;
}
public boolean isLienAllowed() {
return this.lienAllowed;
}
public BigDecimal getMinBalanceForInterestCalculation() {
return this.minBalanceForInterestCalculation;
}
@@ -30,8 +30,10 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interest
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lienAllowedParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.maxAllowedLienLimitParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minOverdraftForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredBalanceParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName;
@@ -294,6 +296,20 @@ public class SavingsAccountAssembler {
minRequiredBalance = product.minRequiredBalance();
}
boolean lienAllowed = false;
if (command.parameterExists(lienAllowedParamName)) {
lienAllowed = command.booleanPrimitiveValueOfParameterNamed(lienAllowedParamName);
} else {
lienAllowed = product.isLienAllowed();
}
BigDecimal maxAllowedLienLimit = BigDecimal.ZERO;
if (command.parameterExists(maxAllowedLienLimitParamName)) {
maxAllowedLienLimit = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(maxAllowedLienLimitParamName);
} else {
maxAllowedLienLimit = product.maxAllowedLienLimit();
}
boolean withHoldTax = product.withHoldTax();
if (command.parameterExists(withHoldTaxParamName)) {
withHoldTax = command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName);
@@ -306,8 +322,8 @@ public class SavingsAccountAssembler {
externalId, accountType, submittedOnDate, submittedBy, interestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, iswithdrawalFeeApplicableForTransfer, charges, allowOverdraft,
overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, nominalAnnualInterestRateOverdraft,
minOverdraftForInterestCalculation, withHoldTax);
overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, maxAllowedLienLimit, lienAllowed,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax);
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
account.validateNewApplicationState(DateUtils.getLocalDateOfTenant(), SAVINGS_ACCOUNT_RESOURCE_NAME);
@@ -423,8 +439,9 @@ public class SavingsAccountAssembler {
product.interestPostingPeriodType(), product.interestCalculationType(), product.interestCalculationDaysInYearType(),
product.minRequiredOpeningBalance(), product.lockinPeriodFrequency(), product.lockinPeriodFrequencyType(),
product.isWithdrawalFeeApplicableForTransfer(), charges, product.isAllowOverdraft(), product.overdraftLimit(),
product.isMinRequiredBalanceEnforced(), product.minRequiredBalance(), product.nominalAnnualInterestRateOverdraft(),
product.minOverdraftForInterestCalculation(), product.withHoldTax());
product.isMinRequiredBalanceEnforced(), product.minRequiredBalance(), product.maxAllowedLienLimit(),
product.isLienAllowed(), product.nominalAnnualInterestRateOverdraft(), product.minOverdraftForInterestCalculation(),
product.withHoldTax());
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
account.validateNewApplicationState(DateUtils.getLocalDateOfTenant(), SAVINGS_ACCOUNT_RESOURCE_NAME);
@@ -24,6 +24,7 @@ import java.time.format.DateTimeFormatter;
import java.util.Set;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
import org.apache.fineract.portfolio.savings.SavingsTransactionBooleanValues;
import org.apache.fineract.useradministration.domain.AppUser;
public interface SavingsAccountDomainService {
@@ -43,4 +44,7 @@ public interface SavingsAccountDomainService {
SavingsAccountTransaction handleReversal(SavingsAccount account, SavingsAccountTransaction savingsAccountTransaction,
boolean backdatedTxnsAllowedTill);
SavingsAccountTransaction handleHold(SavingsAccount account, AppUser createdUser, BigDecimal amount, LocalDate transactionDate,
Boolean lienAllowed);
}
@@ -36,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.ApplicationCurrencyRepositoryWrapper;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BusinessEntity;
import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BusinessEvents;
@@ -219,6 +220,18 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
return deposit;
}
@Transactional
@Override
public SavingsAccountTransaction handleHold(final SavingsAccount account, final AppUser createdUser, BigDecimal amount,
LocalDate transactionDate, Boolean lienAllowed) {
final PaymentDetail paymentDetails = null;
Date createdDate = new Date();
SavingsAccountTransaction transaction = SavingsAccountTransaction.holdAmount(account, account.office(), paymentDetails,
transactionDate, Money.of(account.getCurrency(), amount), createdDate, createdUser, lienAllowed);
return transaction;
}
@Override
public SavingsAccountTransaction handleDividendPayout(final SavingsAccount account, final LocalDate transactionDate,
final BigDecimal transactionAmount, final boolean backdatedTxnsAllowedTill) {
@@ -140,6 +140,9 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
@Column(name = "original_transaction_id")
private Long originalTxnId;
@Column(name = "is_lien_transaction")
private Boolean lienTransaction;
SavingsAccountTransaction() {
this.dateOf = null;
this.typeOf = null;
@@ -150,8 +153,9 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, Date createdDate, final AppUser appUser) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.DEPOSIT.getValue(), date,
createdDate, amount, isReversed, appUser, isManualTransaction);
createdDate, amount, isReversed, appUser, isManualTransaction, lienTransaction);
}
public static SavingsAccountTransaction deposit(final SavingsAccount savingsAccount, final Office office,
@@ -159,54 +163,61 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
final SavingsAccountTransactionType savingsAccountTransactionType) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, savingsAccountTransactionType.getValue(), date,
createdDate, amount, isReversed, appUser, isManualTransaction);
createdDate, amount, isReversed, appUser, isManualTransaction, lienTransaction);
}
public static SavingsAccountTransaction withdrawal(final SavingsAccount savingsAccount, final Office office,
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, Date createdDate, final AppUser appUser) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.WITHDRAWAL.getValue(),
date, createdDate, amount, isReversed, appUser, isManualTransaction);
date, createdDate, amount, isReversed, appUser, isManualTransaction, lienTransaction);
}
public static SavingsAccountTransaction interestPosting(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final boolean isManualTransaction) {
final boolean isReversed = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.INTEREST_POSTING.getValue(), date,
amount, isReversed, null, isManualTransaction);
amount, isReversed, null, isManualTransaction, lienTransaction);
}
public static SavingsAccountTransaction overdraftInterest(final SavingsAccount savingsAccount, final Office office,
final LocalDate date, final Money amount, final boolean isManualTransaction) {
final boolean isReversed = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.OVERDRAFT_INTEREST.getValue(), date,
amount, isReversed, null, isManualTransaction);
amount, isReversed, null, isManualTransaction, lienTransaction);
}
public static SavingsAccountTransaction withdrawalFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final AppUser appUser) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue(), date, amount,
isReversed, appUser, isManualTransaction);
isReversed, appUser, isManualTransaction, lienTransaction);
}
public static SavingsAccountTransaction annualFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final AppUser appUser) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.ANNUAL_FEE.getValue(), date, amount,
isReversed, appUser, isManualTransaction);
isReversed, appUser, isManualTransaction, lienTransaction);
}
public static SavingsAccountTransaction charge(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final AppUser appUser) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.PAY_CHARGE.getValue(), date, amount,
isReversed, appUser, isManualTransaction);
isReversed, appUser, isManualTransaction, lienTransaction);
}
public static SavingsAccountTransaction from(final Integer transactionTypeEnum, final LocalDate transactionDate,
@@ -221,8 +232,9 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
final Money amount, final AppUser appUser) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.WAIVE_CHARGES.getValue(), date, amount,
isReversed, appUser, isManualTransaction);
isReversed, appUser, isManualTransaction, lienTransaction);
}
public static SavingsAccountTransaction initiateTransfer(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
@@ -230,9 +242,10 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.INITIATE_TRANSFER.getValue(), date, new Date(),
savingsAccount.getSummary().getAccountBalance(), isReversed, appUser, isManualTransaction);
savingsAccount.getSummary().getAccountBalance(), isReversed, appUser, isManualTransaction, lienTransaction);
}
public static SavingsAccountTransaction approveTransfer(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
@@ -240,9 +253,10 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.APPROVE_TRANSFER.getValue(), date, new Date(),
savingsAccount.getSummary().getAccountBalance(), isReversed, appUser, isManualTransaction);
savingsAccount.getSummary().getAccountBalance(), isReversed, appUser, isManualTransaction, lienTransaction);
}
public static SavingsAccountTransaction withdrawTransfer(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
@@ -250,17 +264,20 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.WITHDRAW_TRANSFER.getValue(), date, new Date(),
savingsAccount.getSummary().getAccountBalance(), isReversed, appUser, isManualTransaction);
savingsAccount.getSummary().getAccountBalance(), isReversed, appUser, isManualTransaction, lienTransaction);
}
public static SavingsAccountTransaction withHoldTax(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final Map<TaxComponent, BigDecimal> taxDetails) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
SavingsAccountTransaction accountTransaction = new SavingsAccountTransaction(savingsAccount, office,
SavingsAccountTransactionType.WITHHOLD_TAX.getValue(), date, amount, isReversed, null, isManualTransaction);
SavingsAccountTransactionType.WITHHOLD_TAX.getValue(), date, amount, isReversed, null, isManualTransaction,
lienTransaction);
updateTaxDetails(taxDetails, accountTransaction);
return accountTransaction;
}
@@ -269,9 +286,10 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
final boolean accountTransaction) {
final boolean isReversed = false;
final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, savingsAccount.office(), paymentDetail,
SavingsAccountTransactionType.ESCHEAT.getValue(), date, new Date(), savingsAccount.getSummary().getAccountBalance(),
isReversed, appUser, accountTransaction);
isReversed, appUser, accountTransaction, lienTransaction);
}
public static void updateTaxDetails(final Map<TaxComponent, BigDecimal> taxDetails,
@@ -287,25 +305,27 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
public static SavingsAccountTransaction copyTransaction(SavingsAccountTransaction accountTransaction) {
return new SavingsAccountTransaction(accountTransaction.savingsAccount, accountTransaction.office, accountTransaction.paymentDetail,
accountTransaction.typeOf, accountTransaction.transactionLocalDate(), accountTransaction.createdDate,
accountTransaction.amount, accountTransaction.reversed, accountTransaction.appUser, accountTransaction.isManualTransaction);
accountTransaction.amount, accountTransaction.reversed, accountTransaction.appUser, accountTransaction.isManualTransaction,
accountTransaction.lienTransaction);
}
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final Integer typeOf,
final LocalDate transactionLocalDate, final Money amount, final boolean isReversed, final AppUser appUser,
final boolean isManualTransaction) {
this(savingsAccount, office, null, typeOf, transactionLocalDate, new Date(), amount, isReversed, appUser, isManualTransaction);
final boolean isManualTransaction, final Boolean lienTransaction) {
this(savingsAccount, office, null, typeOf, transactionLocalDate, new Date(), amount, isReversed, appUser, isManualTransaction,
lienTransaction);
}
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final PaymentDetail paymentDetail,
final Integer typeOf, final LocalDate transactionLocalDate, final Date createdDate, final Money amount,
final boolean isReversed, final AppUser appUser, final boolean isManualTransaction) {
final boolean isReversed, final AppUser appUser, final boolean isManualTransaction, final Boolean lienTransaction) {
this(savingsAccount, office, paymentDetail, typeOf, transactionLocalDate, createdDate, amount.getAmount(), isReversed, appUser,
isManualTransaction);
isManualTransaction, lienTransaction);
}
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final PaymentDetail paymentDetail,
final Integer typeOf, final LocalDate transactionLocalDate, final Date createdDate, final BigDecimal amount,
final boolean isReversed, final AppUser appUser, final boolean isManualTransaction) {
final boolean isReversed, final AppUser appUser, final boolean isManualTransaction, final Boolean lienTransaction) {
this.savingsAccount = savingsAccount;
this.office = office;
this.typeOf = typeOf;
@@ -316,6 +336,7 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
this.createdDate = createdDate;
this.appUser = appUser;
this.isManualTransaction = isManualTransaction;
this.lienTransaction = lienTransaction;
}
private SavingsAccountTransaction(final Integer transactionTypeEnum, final LocalDate transactionDate, final BigDecimal amount,
@@ -337,18 +358,19 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
}
public static SavingsAccountTransaction holdAmount(final SavingsAccount savingsAccount, final Office office,
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, Date createdDate, final AppUser appUser) {
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, Date createdDate, final AppUser appUser,
final Boolean lienTransaction) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.AMOUNT_HOLD.getValue(),
date, createdDate, amount, isReversed, appUser, isManualTransaction);
date, createdDate, amount, isReversed, appUser, isManualTransaction, lienTransaction);
}
public static SavingsAccountTransaction releaseAmount(SavingsAccountTransaction accountTransaction, LocalDate transactionDate,
Date createdDate, final AppUser appUser) {
return new SavingsAccountTransaction(accountTransaction.savingsAccount, accountTransaction.office, accountTransaction.paymentDetail,
SavingsAccountTransactionType.AMOUNT_RELEASE.getValue(), transactionDate, createdDate, accountTransaction.amount,
accountTransaction.reversed, appUser, accountTransaction.isManualTransaction);
accountTransaction.reversed, appUser, accountTransaction.isManualTransaction, accountTransaction.lienTransaction);
}
public static SavingsAccountTransaction reversal(SavingsAccountTransaction accountTransaction) {
@@ -35,9 +35,11 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interest
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.isDormancyTrackingActiveParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lienAllowedParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.localeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.maxAllowedLienLimitParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minBalanceForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minOverdraftForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredBalanceParamName;
@@ -179,6 +181,12 @@ public class SavingsProduct extends AbstractPersistableCustom {
@Column(name = "min_required_balance", scale = 6, precision = 19, nullable = true)
private BigDecimal minRequiredBalance;
@Column(name = "is_lien_allowed", nullable = false)
private boolean lienAllowed;
@Column(name = "max_allowed_lien_limit", scale = 6, precision = 19, nullable = true)
private BigDecimal maxAllowedLienLimit;
@Column(name = "min_balance_for_interest_calculation", scale = 6, precision = 19, nullable = true)
private BigDecimal minBalanceForInterestCalculation;
@@ -209,17 +217,17 @@ public class SavingsProduct extends AbstractPersistableCustom {
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set<Charge> charges,
final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance,
final BigDecimal minRequiredBalance, final BigDecimal minBalanceForInterestCalculation,
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation, boolean withHoldTax,
TaxGroup taxGroup, final Boolean isDormancyTrackingActive, final Long daysToInactive, final Long daysToDormancy,
final Long daysToEscheat) {
final BigDecimal minRequiredBalance, final boolean lienAllowed, final BigDecimal maxAllowedLienLimit,
final BigDecimal minBalanceForInterestCalculation, final BigDecimal nominalAnnualInterestRateOverdraft,
final BigDecimal minOverdraftForInterestCalculation, boolean withHoldTax, TaxGroup taxGroup,
final Boolean isDormancyTrackingActive, final Long daysToInactive, final Long daysToDormancy, final Long daysToEscheat) {
return new SavingsProduct(name, shortName, description, currency, interestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges,
allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, minBalanceForInterestCalculation,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, isDormancyTrackingActive,
daysToInactive, daysToDormancy, daysToEscheat);
allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, lienAllowed, maxAllowedLienLimit,
minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax,
taxGroup, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
}
protected SavingsProduct() {
@@ -238,7 +246,7 @@ public class SavingsProduct extends AbstractPersistableCustom {
this(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges, allowOverdraft, overdraftLimit,
false, null, minBalanceForInterestCalculation, null, null, withHoldTax, taxGroup, null, null, null, null);
false, null, false, null, minBalanceForInterestCalculation, null, null, withHoldTax, taxGroup, null, null, null, null);
}
protected SavingsProduct(final String name, final String shortName, final String description, final MonetaryCurrency currency,
@@ -248,10 +256,10 @@ public class SavingsProduct extends AbstractPersistableCustom {
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set<Charge> charges,
final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance,
final BigDecimal minRequiredBalance, BigDecimal minBalanceForInterestCalculation,
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation,
final boolean withHoldTax, final TaxGroup taxGroup, final Boolean isDormancyTrackingActive, final Long daysToInactive,
final Long daysToDormancy, final Long daysToEscheat) {
final BigDecimal minRequiredBalance, final boolean lienAllowed, final BigDecimal maxAllowedLienLimit,
BigDecimal minBalanceForInterestCalculation, final BigDecimal nominalAnnualInterestRateOverdraft,
final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax, final TaxGroup taxGroup,
final Boolean isDormancyTrackingActive, final Long daysToInactive, final Long daysToDormancy, final Long daysToEscheat) {
this.name = name;
this.shortName = shortName;
@@ -293,6 +301,8 @@ public class SavingsProduct extends AbstractPersistableCustom {
this.enforceMinRequiredBalance = enforceMinRequiredBalance;
this.minRequiredBalance = minRequiredBalance;
this.lienAllowed = lienAllowed;
this.maxAllowedLienLimit = maxAllowedLienLimit;
this.minBalanceForInterestCalculation = minBalanceForInterestCalculation;
this.withHoldTax = withHoldTax;
this.taxGroup = taxGroup;
@@ -540,6 +550,19 @@ public class SavingsProduct extends AbstractPersistableCustom {
this.minRequiredBalance = newValue;
}
if (command.isChangeInBooleanParameterNamed(lienAllowedParamName, this.lienAllowed)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(lienAllowedParamName);
actualChanges.put(lienAllowedParamName, newValue);
this.lienAllowed = newValue;
}
if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(maxAllowedLienLimitParamName, this.maxAllowedLienLimit)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(maxAllowedLienLimitParamName);
actualChanges.put(maxAllowedLienLimitParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
this.maxAllowedLienLimit = newValue;
}
if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(minBalanceForInterestCalculationParamName,
this.minBalanceForInterestCalculation)) {
final BigDecimal newValue = command
@@ -689,6 +712,14 @@ public class SavingsProduct extends AbstractPersistableCustom {
return this.enforceMinRequiredBalance;
}
public BigDecimal maxAllowedLienLimit() {
return this.maxAllowedLienLimit;
}
public boolean isLienAllowed() {
return this.lienAllowed;
}
public Set<Charge> charges() {
return this.charges;
}
@@ -34,8 +34,10 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interest
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.isDormancyTrackingActiveParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lienAllowedParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.maxAllowedLienLimitParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minBalanceForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minOverdraftForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredBalanceParamName;
@@ -170,6 +172,16 @@ public class SavingsProductAssembler {
if (command.parameterExists(minRequiredBalanceParamName)) {
minRequiredBalance = command.bigDecimalValueOfParameterNamed(minRequiredBalanceParamName);
}
boolean lienAllowed = false;
if (command.parameterExists(lienAllowedParamName)) {
lienAllowed = command.booleanPrimitiveValueOfParameterNamed(lienAllowedParamName);
}
BigDecimal maxAllowedLienLimit = BigDecimal.ZERO;
if (command.parameterExists(maxAllowedLienLimitParamName)) {
maxAllowedLienLimit = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(maxAllowedLienLimitParamName);
}
final BigDecimal minBalanceForInterestCalculation = command
.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minBalanceForInterestCalculationParamName);
@@ -184,9 +196,9 @@ public class SavingsProductAssembler {
return SavingsProduct.createNew(name, shortName, description, currency, interestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, iswithdrawalFeeApplicableForTransfer, accountingRuleType, charges,
allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, minBalanceForInterestCalculation,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, isDormancyTrackingActive,
daysToInactive, daysToDormancy, daysToEscheat);
allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, lienAllowed, maxAllowedLienLimit,
minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax,
taxGroup, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
}
public Set<Charge> assembleListOfSavingsProductCharges(final JsonCommand command, final String savingsProductCurrencyCode) {
@@ -332,6 +332,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
sqlBuilder.append("sa.min_balance_for_interest_calculation as minBalanceForInterestCalculation,");
sqlBuilder.append("sa.min_required_balance as minRequiredBalance, ");
sqlBuilder.append("sa.enforce_min_required_balance as enforceMinRequiredBalance, ");
sqlBuilder.append("sa.max_allowed_lien_limit as maxAllowedLienLimit, ");
sqlBuilder.append("sa.is_lien_allowed as lienAllowed, ");
sqlBuilder.append("sa.on_hold_funds_derived as onHoldFunds, ");
sqlBuilder.append("sa.withhold_tax as withHoldTax, ");
sqlBuilder.append("sa.total_withhold_tax_derived as totalWithholdTax, ");
@@ -575,14 +577,17 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final BigDecimal minRequiredBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredBalance");
final boolean enforceMinRequiredBalance = rs.getBoolean("enforceMinRequiredBalance");
final BigDecimal maxAllowedLienLimit = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "maxAllowedLienLimit");
final boolean lienAllowed = rs.getBoolean("lienAllowed");
savingsAccountData = SavingsAccountData.instance(id, accountNo, depositType, externalId, null, null, null, null,
productId, null, null, null, status, subStatus, null, timeline, currency, nominalAnnualInterestRate,
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
withdrawalFeeForTransfers, summary, allowOverdraft, overdraftLimit, minRequiredBalance,
enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
minOverdraftForInterestCalculation, withHoldTax, taxGroupData, lastActiveTransactionDate,
isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, onHoldAmount);
enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation, onHoldFunds,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroupData,
lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat,
onHoldAmount);
savingsAccountData.setClientData(clientData);
savingsAccountData.setGroupGeneralData(groupGeneralData);
@@ -811,6 +816,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
sqlBuilder.append("sa.min_balance_for_interest_calculation as minBalanceForInterestCalculation,");
sqlBuilder.append("sa.min_required_balance as minRequiredBalance, ");
sqlBuilder.append("sa.enforce_min_required_balance as enforceMinRequiredBalance, ");
sqlBuilder.append("sa.max_allowed_lien_limit as maxAllowedLienLimit, ");
sqlBuilder.append("sa.is_lien_allowed as lienAllowed, ");
sqlBuilder.append("sa.on_hold_funds_derived as onHoldFunds, ");
sqlBuilder.append("sa.withhold_tax as withHoldTax, ");
sqlBuilder.append("sa.total_withhold_tax_derived as totalWithholdTax, ");
@@ -991,6 +998,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final BigDecimal minRequiredBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredBalance");
final boolean enforceMinRequiredBalance = rs.getBoolean("enforceMinRequiredBalance");
final BigDecimal maxAllowedLienLimit = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "maxAllowedLienLimit");
final boolean lienAllowed = rs.getBoolean("lienAllowed");
/*
* final BigDecimal annualFeeAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "annualFeeAmount");
*
@@ -1057,9 +1067,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
withdrawalFeeForTransfers, summary, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation,
withHoldTax, taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy,
daysToEscheat, onHoldAmount);
maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
minOverdraftForInterestCalculation, withHoldTax, taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive,
daysToInactive, daysToDormancy, daysToEscheat, onHoldAmount);
}
}
@@ -1320,7 +1330,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
sqlBuilder.append(" au.username as submittedByUsername, ");
sqlBuilder.append(" nt.note as transactionNote, ");
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(
"tr.is_reversal as isReversal, tr.original_transaction_id as originalTransactionId, tr.is_lien_transaction as lienTransaction, ");
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.description as fromTransferDescription,");
@@ -1368,6 +1379,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final boolean reversed = rs.getBoolean("reversed");
final boolean isReversal = rs.getBoolean("isReversal");
final Long originalTransactionId = rs.getLong("originalTransactionId");
final Boolean lienTransaction = rs.getBoolean("lienTransaction");
final Long savingsId = rs.getLong("savingsId");
final String accountNo = rs.getString("accountNo");
@@ -1422,7 +1434,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final String note = rs.getString("transactionNote");
return SavingsAccountTransactionData.create(id, transactionType, paymentDetailData, savingsId, accountNo, date, currency,
amount, outstandingChargeAmount, runningBalance, reversed, transfer, submittedOnDate, postInterestAsOn,
submittedByUsername, note, isReversal, originalTransactionId, releaseTransactionId, reasonForBlock);
submittedByUsername, note, isReversal, originalTransactionId, lienTransaction, releaseTransactionId, reasonForBlock);
}
}
@@ -1510,7 +1522,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
// ");
// sqlBuilder.append("sp.annual_fee_on_day as annualFeeOnDay ");
sqlBuilder.append("sp.min_required_balance as minRequiredBalance, ");
sqlBuilder.append("sp.enforce_min_required_balance as enforceMinRequiredBalance ");
sqlBuilder.append("sp.enforce_min_required_balance as enforceMinRequiredBalance, ");
sqlBuilder.append("sp.max_allowed_lien_limit as maxAllowedLienLimit, ");
sqlBuilder.append("sp.is_lien_allowed as lienAllowed ");
sqlBuilder.append("from m_savings_product sp ");
sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code ");
sqlBuilder.append("left join m_tax_group tg on tg.id = sp.tax_group_id ");
@@ -1580,6 +1594,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final BigDecimal minRequiredBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredBalance");
final boolean enforceMinRequiredBalance = rs.getBoolean("enforceMinRequiredBalance");
final BigDecimal maxAllowedLienLimit = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "maxAllowedLienLimit");
final boolean lienAllowed = rs.getBoolean("lienAllowed");
final BigDecimal minBalanceForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
"minBalanceForInterestCalculation");
@@ -1639,9 +1655,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
nominalAnnualIterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
withdrawalFeeForTransfers, summary, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation,
withHoldTax, taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy,
daysToEscheat, savingsAmountOnHold);
maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
minOverdraftForInterestCalculation, withHoldTax, taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive,
daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
}
}
@@ -23,7 +23,9 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.SAVINGS_
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.amountParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargeIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lienAllowedParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.transactionAmountParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.transactionDateParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawBalanceParamName;
@@ -1811,6 +1813,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final AppUser submittedBy = this.context.authenticatedUser();
final boolean backdatedTxnsAllowedTill = this.savingAccountAssembler.getPivotConfigStatus();
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId, backdatedTxnsAllowedTill);
final LocalDate transactionDate = command.localDateValueOfParameterNamed(transactionDateParamName);
final boolean lienAllowed = command.booleanObjectValueOfParameterNamed(lienAllowedParamName);
checkClientOrGroupActive(account);
@@ -1823,11 +1827,11 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
runningBalance = runningBalance.minus(amount);
}
this.savingsAccountTransactionDataValidator.validateHoldAndAssembleForm(command.json(), account, submittedBy,
backdatedTxnsAllowedTill);
SavingsAccountTransaction transaction = this.savingsAccountDomainService.handleHold(account, getAppUserIfPresent(), amount,
transactionDate, lienAllowed);
account.holdAmount(amount);
SavingsAccountTransaction transaction = this.savingsAccountTransactionDataValidator.validateHoldAndAssembleForm(command.json(),
account, submittedBy, backdatedTxnsAllowedTill);
transaction.updateRunningBalance(runningBalance);
final String reasonForBlock = command.stringValueOfParameterNamed(SavingsApiConstants.reasonForBlockParamName);
@@ -129,6 +129,8 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
sqlBuilder.append("sp.min_overdraft_for_interest_calculation as minOverdraftForInterestCalculation, ");
sqlBuilder.append("sp.min_required_balance as minRequiredBalance, ");
sqlBuilder.append("sp.enforce_min_required_balance as enforceMinRequiredBalance, ");
sqlBuilder.append("sp.max_allowed_lien_limit as maxAllowedLienLimit, ");
sqlBuilder.append("sp.is_lien_allowed as lienAllowed, ");
sqlBuilder.append("sp.min_balance_for_interest_calculation as minBalanceForInterestCalculation,");
sqlBuilder.append("sp.accounting_type as accountingType, ");
sqlBuilder.append("sp.withhold_tax as withHoldTax,");
@@ -203,6 +205,8 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
final BigDecimal minRequiredBalance = rs.getBigDecimal("minRequiredBalance");
final boolean enforceMinRequiredBalance = rs.getBoolean("enforceMinRequiredBalance");
final BigDecimal maxAllowedLienLimit = rs.getBigDecimal("maxAllowedLienLimit");
final boolean lienAllowed = rs.getBoolean("lienAllowed");
final BigDecimal minBalanceForInterestCalculation = rs.getBigDecimal("minBalanceForInterestCalculation");
final boolean withHoldTax = rs.getBoolean("withHoldTax");
@@ -221,9 +225,9 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
return SavingsProductData.instance(id, name, shortName, description, currency, nominalAnnualInterestRate,
compoundingInterestPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers,
accountingRuleType, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax,
taxGroupData, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
accountingRuleType, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, maxAllowedLienLimit,
lienAllowed, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation,
withHoldTax, taxGroupData, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
}
}
@@ -29,4 +29,5 @@
<include file="parts/0007_product_loan_higher_than_applied_loan_amount_management.xml" relativeToChangelogFile="true"/>
<include file="parts/0008_loan_charge_add_external_id.xml" relativeToChangelogFile="true"/>
<include file="parts/0009_hold_reason_savings_account.xml" relativeToChangelogFile="true"/>
<include file="parts/0010_lien_allowed_on_savings_account_products.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>
@@ -0,0 +1,44 @@
<?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">
<column defaultValueComputed="NULL" name="max_allowed_lien_limit" type="DECIMAL(19, 6)"/>
<column defaultValueBoolean="false" name="is_lien_allowed" type="boolean">
<constraints nullable="false"/>
</column>
</addColumn>
<addColumn tableName="m_savings_product">
<column defaultValueComputed="NULL" name="max_allowed_lien_limit" type="DECIMAL(19, 6)"/>
<column defaultValueBoolean="false" name="is_lien_allowed" type="boolean">
<constraints nullable="false"/>
</column>
</addColumn>
<addColumn tableName="m_savings_account_transaction">
<column defaultValueBoolean="false" name="is_lien_transaction" type="boolean">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -2017,6 +2017,68 @@ public class ClientSavingsIntegrationTest {
enforceMinRequiredBalance, allowOverdraft, taxGroupId, false);
}
// LienAtProductLevel
private Integer createSavingsProduct(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String minOpenningBalance, String minBalanceForInterestCalculation, final boolean enforceMinRequiredBalance,
final boolean allowOverDraft, final boolean lienAllowed) {
LOG.info("------------------------------CREATING NEW SAVINGS PRODUCT WITH LIEN---------------------------------------");
SavingsProductHelper savingsProductHelper = new SavingsProductHelper();
if (lienAllowed) {
final String maxAllowedLienLimit = "2000.0";
savingsProductHelper.withLienAllowed(maxAllowedLienLimit);
}
if (enforceMinRequiredBalance) {
final String minRequiredBalance = "100.0";
savingsProductHelper.withMinRequiredBalance(minRequiredBalance);
savingsProductHelper.withEnforceMinRequiredBalance("true");
}
if (allowOverDraft) {
final String overDraftLimit = "500.0";
savingsProductHelper.withOverDraft(overDraftLimit);
}
final String savingsProductJSON = savingsProductHelper.withInterestCompoundingPeriodTypeAsDaily()
.withInterestPostingPeriodTypeAsMonthly()
.withInterestCalculationPeriodTypeAsDailyBalance()
.withMinBalanceForInterestCalculation(minBalanceForInterestCalculation)
.withMinimumOpenningBalance(minOpenningBalance).build();
return SavingsProductHelper.createSavingsProduct(savingsProductJSON, requestSpec, responseSpec);
}
// LienAtProductlevel with Overdraft Limit > Lien Limit
private Integer createSavingsProduct(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String minOpenningBalance, String minBalanceForInterestCalculation, final boolean enforceMinRequiredBalance,
final boolean allowOverDraft, final String overDraftLimit, final boolean lienAllowed, final String lienAllowedLimit) {
LOG.info("------------------------------CREATING NEW SAVINGS PRODUCT WITH LIEN---------------------------------------");
SavingsProductHelper savingsProductHelper = new SavingsProductHelper();
if (lienAllowed) {
savingsProductHelper.withLienAllowed(lienAllowedLimit);
}
if (enforceMinRequiredBalance) {
final String minRequiredBalance = "100.0";
savingsProductHelper.withMinRequiredBalance(minRequiredBalance);
savingsProductHelper.withEnforceMinRequiredBalance("true");
}
if (allowOverDraft) {
savingsProductHelper.withOverDraft(overDraftLimit);
}
final String savingsProductJSON = savingsProductHelper.withInterestCompoundingPeriodTypeAsDaily()
.withInterestPostingPeriodTypeAsMonthly()
.withInterestCalculationPeriodTypeAsDailyBalance()
.withMinBalanceForInterestCalculation(minBalanceForInterestCalculation)
.withMinimumOpenningBalance(minOpenningBalance).build();
return SavingsProductHelper.createSavingsProduct(savingsProductJSON, requestSpec, responseSpec);
}
private Integer createSavingsProduct(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String minOpenningBalance, String minBalanceForInterestCalculation, String minRequiredBalance,
String enforceMinRequiredBalance, final boolean allowOverdraft, final String taxGroupId, boolean withDormancy) {
@@ -2177,26 +2239,23 @@ public class ClientSavingsIntegrationTest {
@SuppressWarnings("unchecked")
@Test
public void testSavingsAccountLienStatus() {
public void testSavingsAccountLienAllowedAtProductLevelWithEnforceBalance() {
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
SavingsAccountHelper savingsAccountHelperValidationError = new SavingsAccountHelper(this.requestSpec,
new ResponseSpecBuilder().build());
final ResponseSpecification errorResponse = new ResponseSpecBuilder().expectStatusCode(403).build();
final SavingsAccountHelper validationErrorHelper = new SavingsAccountHelper(this.requestSpec, errorResponse);
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
final String minBalanceForInterestCalculation = null;
final String minRequiredBalance = null;
final String enforceMinRequiredBalance = "true";
final boolean enforceMinRequiredBalance = true;
final boolean allowOverdraft = false;
final boolean lienAllowed = true;
final Integer savingsProductID = createSavingsProduct(this.requestSpec, this.responseSpec, MINIMUM_OPENING_BALANCE,
minBalanceForInterestCalculation, minRequiredBalance, enforceMinRequiredBalance, allowOverdraft);
minBalanceForInterestCalculation, enforceMinRequiredBalance, allowOverdraft, lienAllowed);
Assertions.assertNotNull(savingsProductID);
final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplication(clientID, savingsProductID, ACCOUNT_TYPE_INDIVIDUAL);
@@ -2211,38 +2270,264 @@ public class ClientSavingsIntegrationTest {
savingsStatusHashMap = this.savingsAccountHelper.activateSavings(savingsId);
SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
List<HashMap> error = (List) savingsAccountHelperValidationError.holdAmountInSavingsAccount(savingsId, "1500", false,
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_ERROR); // lien:false
List<HashMap> error = (List) savingsAccountHelperValidationError.holdAmountInSavingsAccount(savingsId, "2000", false,
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_ERROR);
assertEquals("validation.msg.savingsaccount.insufficient.balance", error.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
assertEquals("validation.msg.savingsaccount.insufficient balance", error.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
Integer holdTransactionId = (Integer) this.savingsAccountHelper.holdAmountInSavingsAccount(savingsId, "1500", true,
Integer lienHoldTransactionId = (Integer) this.savingsAccountHelper.holdAmountInSavingsAccount(savingsId, "2000", true,
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
HashMap summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
Float balance = Float.parseFloat("-500");
Float balance = Float.parseFloat("-1000");
assertEquals(balance, summary.get("availableBalance"), "Verifying available Balance is -500");
Integer depositTransactionId = (Integer) this.savingsAccountHelper.depositToSavingsAccount(savingsId, "1000",
assertEquals(balance, summary.get("availableBalance"), "Verifying available Balance is -1000");
Integer depositTransactionId = (Integer) this.savingsAccountHelper.depositToSavingsAccount(savingsId, "1200",
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
Calendar todaysDate = Calendar.getInstance();
final String TRANSACTION_DATE = dateFormat.format(todaysDate.getTime());
ArrayList<HashMap> savingsAccountErrorData = (ArrayList<HashMap>) validationErrorHelper.withdrawalFromSavingsAccount(savingsId,
"1000", TRANSACTION_DATE, CommonConstants.RESPONSE_ERROR);
"200", TRANSACTION_DATE, CommonConstants.RESPONSE_ERROR);
assertEquals("error.msg.savingsaccount.transaction.insufficient.account.balance",
savingsAccountErrorData.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
Integer withdrawTransactionId = (Integer) this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, "500", TRANSACTION_DATE,
Integer withdrawTransactionId = (Integer) this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, "100", TRANSACTION_DATE,
CommonConstants.RESPONSE_RESOURCE_ID);
Assertions.assertNotNull(withdrawTransactionId);
}
@SuppressWarnings("unchecked")
@Test
public void testSavingsAccountLienAllowedAtProductLevelWithOverDraftLimit() {
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
SavingsAccountHelper savingsAccountHelperValidationError = new SavingsAccountHelper(this.requestSpec,
new ResponseSpecBuilder().build());
final ResponseSpecification errorResponse = new ResponseSpecBuilder().expectStatusCode(403).build();
final SavingsAccountHelper validationErrorHelper = new SavingsAccountHelper(this.requestSpec, errorResponse);
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
final String minBalanceForInterestCalculation = null;
final boolean enforceMinRequiredBalance = false;
final boolean allowOverdraft = true;
final boolean lienAllowed = true;
final Integer savingsProductID = createSavingsProduct(this.requestSpec, this.responseSpec, MINIMUM_OPENING_BALANCE,
minBalanceForInterestCalculation, enforceMinRequiredBalance, allowOverdraft, lienAllowed);
Assertions.assertNotNull(savingsProductID);
final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplication(clientID, savingsProductID, ACCOUNT_TYPE_INDIVIDUAL);
Assertions.assertNotNull(savingsProductID);
HashMap savingsStatusHashMap = SavingsStatusChecker.getStatusOfSavings(this.requestSpec, this.responseSpec, savingsId);
SavingsStatusChecker.verifySavingsIsPending(savingsStatusHashMap);
savingsStatusHashMap = this.savingsAccountHelper.approveSavings(savingsId);
SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
savingsStatusHashMap = this.savingsAccountHelper.activateSavings(savingsId);
SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
List<HashMap> error = (List) savingsAccountHelperValidationError.holdAmountInSavingsAccount(savingsId, "2000", false,
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_ERROR);
assertEquals("validation.msg.savingsaccount.insufficient.balance", error.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
Integer lienHoldTransactionId = (Integer) this.savingsAccountHelper.holdAmountInSavingsAccount(savingsId, "2000", true,
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
HashMap summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
Float balance = Float.parseFloat("-1000");
assertEquals(balance, summary.get("availableBalance"), "Verifying available Balance is -1000");
Integer depositTransactionId = (Integer) this.savingsAccountHelper.depositToSavingsAccount(savingsId, "1200",
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
Calendar todaysDate = Calendar.getInstance();
final String TRANSACTION_DATE = dateFormat.format(todaysDate.getTime());
ArrayList<HashMap> savingsAccountErrorData = (ArrayList<HashMap>) validationErrorHelper.withdrawalFromSavingsAccount(savingsId,
"300", TRANSACTION_DATE, CommonConstants.RESPONSE_ERROR);
assertEquals("error.msg.savingsaccount.transaction.insufficient.account.balance",
savingsAccountErrorData.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
Integer withdrawTransactionId = (Integer) this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, "200", TRANSACTION_DATE,
CommonConstants.RESPONSE_RESOURCE_ID);
Assertions.assertNotNull(withdrawTransactionId);
}
@SuppressWarnings("unchecked")
@Test
public void testSavingsAccountLienAllowedAtProductLevelWithNoConfig() {
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
SavingsAccountHelper savingsAccountHelperValidationError = new SavingsAccountHelper(this.requestSpec,
new ResponseSpecBuilder().build());
final ResponseSpecification errorResponse = new ResponseSpecBuilder().expectStatusCode(403).build();
final SavingsAccountHelper validationErrorHelper = new SavingsAccountHelper(this.requestSpec, errorResponse);
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
final String minBalanceForInterestCalculation = null;
final boolean enforceMinRequiredBalance = false;
final boolean allowOverdraft = false;
final boolean lienAllowed = true;
final Integer savingsProductID = createSavingsProduct(this.requestSpec, this.responseSpec, MINIMUM_OPENING_BALANCE,
minBalanceForInterestCalculation, enforceMinRequiredBalance, allowOverdraft, lienAllowed);
Assertions.assertNotNull(savingsProductID);
final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplication(clientID, savingsProductID, ACCOUNT_TYPE_INDIVIDUAL);
Assertions.assertNotNull(savingsProductID);
HashMap savingsStatusHashMap = SavingsStatusChecker.getStatusOfSavings(this.requestSpec, this.responseSpec, savingsId);
SavingsStatusChecker.verifySavingsIsPending(savingsStatusHashMap);
savingsStatusHashMap = this.savingsAccountHelper.approveSavings(savingsId);
SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
savingsStatusHashMap = this.savingsAccountHelper.activateSavings(savingsId);
SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
List<HashMap> error = (List) savingsAccountHelperValidationError.holdAmountInSavingsAccount(savingsId, "2000", false,
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_ERROR);
assertEquals("validation.msg.savingsaccount.insufficient.balance", error.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
Integer lienHoldTransactionId = (Integer) this.savingsAccountHelper.holdAmountInSavingsAccount(savingsId, "2000", true,
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
HashMap summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
Float balance = Float.parseFloat("-1000");
assertEquals(balance, summary.get("availableBalance"), "Verifying available Balance is -1000");
Integer depositTransactionId = (Integer) this.savingsAccountHelper.depositToSavingsAccount(savingsId, "1100",
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
Calendar todaysDate = Calendar.getInstance();
final String TRANSACTION_DATE = dateFormat.format(todaysDate.getTime());
ArrayList<HashMap> savingsAccountErrorData = (ArrayList<HashMap>) validationErrorHelper.withdrawalFromSavingsAccount(savingsId,
"200", TRANSACTION_DATE, CommonConstants.RESPONSE_ERROR);// withdrawable amount = 200;
assertEquals("error.msg.savingsaccount.transaction.insufficient.account.balance",
savingsAccountErrorData.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
Integer withdrawTransactionId = (Integer) this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, "100", TRANSACTION_DATE,
CommonConstants.RESPONSE_RESOURCE_ID);
Assertions.assertNotNull(withdrawTransactionId);
}
@SuppressWarnings("unchecked")
@Test
public void testSavingsAccountWithoutLienAllowed() {
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
SavingsAccountHelper savingsAccountHelperValidationError = new SavingsAccountHelper(this.requestSpec,
new ResponseSpecBuilder().build());
final ResponseSpecification errorResponse = new ResponseSpecBuilder().expectStatusCode(403).build();
final SavingsAccountHelper validationErrorHelper = new SavingsAccountHelper(this.requestSpec, errorResponse);
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
final String minBalanceForInterestCalculation = null;
final boolean enforceMinRequiredBalance = false;
final boolean allowOverdraft = true;
final boolean lienAllowed = false;
final Integer savingsProductID = createSavingsProduct(this.requestSpec, this.responseSpec, MINIMUM_OPENING_BALANCE,
minBalanceForInterestCalculation, enforceMinRequiredBalance, allowOverdraft, lienAllowed);
Assertions.assertNotNull(savingsProductID);
final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplication(clientID, savingsProductID, ACCOUNT_TYPE_INDIVIDUAL);
Assertions.assertNotNull(savingsProductID);
HashMap savingsStatusHashMap = SavingsStatusChecker.getStatusOfSavings(this.requestSpec, this.responseSpec, savingsId);
SavingsStatusChecker.verifySavingsIsPending(savingsStatusHashMap);
savingsStatusHashMap = this.savingsAccountHelper.approveSavings(savingsId);
SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
savingsStatusHashMap = this.savingsAccountHelper.activateSavings(savingsId);
SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
List<HashMap> error = (List) savingsAccountHelperValidationError.holdAmountInSavingsAccount(savingsId, "2000", true,
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_ERROR);
assertEquals("validation.msg.savingsaccount.lien.is.not.allowed.in.product.level",
error.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
Integer lienHoldTransactionId = (Integer) this.savingsAccountHelper.holdAmountInSavingsAccount(savingsId, "1500", false,
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);// as per overdraft limit
HashMap summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
Float balance = Float.parseFloat("-500"); // opening balance
assertEquals(balance, summary.get("availableBalance"), "Verifying available Balance is -500");
Integer depositTransactionId = (Integer) this.savingsAccountHelper.depositToSavingsAccount(savingsId, "2000", // available
// to
// use
// 1100
// and
// another
// 1000
// on
// transactional
// hold
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
Calendar todaysDate = Calendar.getInstance();
final String TRANSACTION_DATE = dateFormat.format(todaysDate.getTime());
ArrayList<HashMap> savingsAccountErrorData = (ArrayList<HashMap>) validationErrorHelper.withdrawalFromSavingsAccount(savingsId,
"1600", TRANSACTION_DATE, CommonConstants.RESPONSE_ERROR);// can not withdraw: amount on transactional
// hold
assertEquals("error.msg.savingsaccount.transaction.insufficient.account.balance",
savingsAccountErrorData.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
Integer withdrawTransactionId = (Integer) this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, "1500",
TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
Assertions.assertNotNull(withdrawTransactionId);
}
@SuppressWarnings("unchecked")
@Test
public void testSavingsAccountLienAllowedAtProductLevelWithOverDraftLimitGreaterThanLienLimit() {
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
final ResponseSpecification errorResponse = new ResponseSpecBuilder().expectStatusCode(400).build();
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
final String minBalanceForInterestCalculation = null;
final boolean enforceMinRequiredBalance = false;
final boolean allowOverdraft = true;
final String overDraftLimit = "2000.0";
final boolean lienAllowed = true;
final String lineAllowedLimit = "1000.0";
final Integer savingsProductID = createSavingsProduct(this.requestSpec, errorResponse, MINIMUM_OPENING_BALANCE,
minBalanceForInterestCalculation, enforceMinRequiredBalance, allowOverdraft, overDraftLimit, lienAllowed, lineAllowedLimit);
Assertions.assertNull(savingsProductID);
}
/**
* incorrect savings account balance when charge transaction is reversed during an overdraft recalculate Daily
* Balances
@@ -377,11 +377,12 @@ public class SavingsAccountHelper {
getActivatedSavingsAsJSON(), isBlock);
}
public Object holdAmountInSavingsAccount(final Integer savingsID, final String amount, final Boolean lienStatus, String date,
public Object holdAmountInSavingsAccount(final Integer savingsID, final String amount, final Boolean lienAllowed, String date,
String jsonAttributeToGetback) {
LOG.info("--------------------------------- SAVINGS TRANSACTION HOLD AMOUNT--------------------------------");
return performSavingActions(createSavingsTransactionURL(HOLD_AMOUNT_SAVINGS_COMMAND, savingsID),
getLienSavingsTransactionJSON(amount, date, lienStatus), jsonAttributeToGetback);
getLienSavingsTransactionJSON(amount, date, lienAllowed), jsonAttributeToGetback);
}
public Integer releaseAmount(final Integer savingsId, final Integer transactionId) {
@@ -459,13 +460,13 @@ public class SavingsAccountHelper {
return savingsAccountWithdrawalJson;
}
private String getLienSavingsTransactionJSON(final String amount, final String transactionDate, final Boolean lien) {
private String getLienSavingsTransactionJSON(final String amount, final String transactionDate, final Boolean lienAllowed) {
final HashMap<String, Object> map = new HashMap<>();
map.put("locale", CommonConstants.LOCALE);
map.put("dateFormat", CommonConstants.DATE_FORMAT);
map.put("transactionDate", transactionDate);
map.put("transactionAmount", amount);
map.put("lien", lien);
map.put("lienAllowed", lienAllowed);
map.put("reasonForBlock", "unUsualActivity");
String savingsAccountWithdrawalJson = new Gson().toJson(map);
LOG.info(savingsAccountWithdrawalJson);
@@ -86,6 +86,8 @@ public class SavingsProductHelper {
private String overdraftLimit = null;
private String minRequiredBalance = null;
private String enforceMinRequiredBalance = "false";
private String lienAllowed = "false";
private String maxAllowedLienLimit = null;
private Boolean withHoldTax = false;
private String taxGroupId = null;
private boolean isDormancyTrackingActive = false;
@@ -129,6 +131,8 @@ public class SavingsProductHelper {
map.put("overdraftLimit", this.overdraftLimit);
map.put("minRequiredBalance", this.minRequiredBalance);
map.put("enforceMinRequiredBalance", this.enforceMinRequiredBalance);
map.put("lienAllowed", this.lienAllowed);
map.put("maxAllowedLienLimit", this.maxAllowedLienLimit);
map.put("withHoldTax", this.withHoldTax.toString());
if (withHoldTax) {
@@ -216,16 +220,22 @@ public class SavingsProductHelper {
return this;
}
public SavingsProductHelper withMinRequiredBalance(String minBalance) {
this.minRequiredBalance = minBalance;
public SavingsProductHelper withMinRequiredBalance(final String minRequiredBalance) {
this.minRequiredBalance = minRequiredBalance;
return this;
}
public SavingsProductHelper withEnforceMinRequiredBalance(String enforceMinRequiredBalance) {
public SavingsProductHelper withEnforceMinRequiredBalance(final String enforceMinRequiredBalance) {
this.enforceMinRequiredBalance = enforceMinRequiredBalance;
return this;
}
public SavingsProductHelper withLienAllowed(final String maxAllowedLienLimit) {
this.lienAllowed = "true";
this.maxAllowedLienLimit = maxAllowedLienLimit;
return this;
}
public SavingsProductHelper withOverDraft(final String overdraftLimit) {
this.allowOverdraft = "true";
this.overdraftLimit = overdraftLimit;