Merge pull request #2215 from logoutdhaval/fineract-1553

FINERACT-1553: Lien configured at product level
This commit is contained in:
Manoj
2022-04-06 19:15:03 +05:30
committed by GitHub
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, PaymentDetail paymentDetail = instance(findPaymentType(), savingsAccount.getExternalId(), null, getRoutingCode(), transferCode,
null); null);
SavingsAccountTransaction holdTransaction = SavingsAccountTransaction.holdAmount(savingsAccount, savingsAccount.office(), SavingsAccountTransaction holdTransaction = SavingsAccountTransaction.holdAmount(savingsAccount, savingsAccount.office(),
paymentDetail, transactionDate.toLocalDate(), Money.of(savingsAccount.getCurrency(), total), new Date(), paymentDetail, transactionDate.toLocalDate(), Money.of(savingsAccount.getCurrency(), total), new Date(), getLoginUser(),
getLoginUser()); false);
MonetaryCurrency accountCurrency = savingsAccount.getCurrency().copy(); MonetaryCurrency accountCurrency = savingsAccount.getCurrency().copy();
holdTransaction.updateRunningBalance( holdTransaction.updateRunningBalance(
Money.of(accountCurrency, savingsAccount.getWithdrawableBalance().subtract(holdTransaction.getAmount()))); 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 minOverdraftForInterestCalculationParamName = "minOverdraftForInterestCalculation";
public static final String minRequiredBalanceParamName = "minRequiredBalance"; public static final String minRequiredBalanceParamName = "minRequiredBalance";
public static final String enforceMinRequiredBalanceParamName = "enforceMinRequiredBalance"; 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 minBalanceForInterestCalculationParamName = "minBalanceForInterestCalculation";
public static final String withdrawBalanceParamName = "withdrawBalance"; public static final String withdrawBalanceParamName = "withdrawBalance";
public static final String onHoldFundsParamName = "onHoldFunds"; public static final String onHoldFundsParamName = "onHoldFunds";
@@ -18,6 +18,11 @@
*/ */
package org.apache.fineract.portfolio.savings.api; 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 io.swagger.v3.oas.annotations.tags.Tag;
import java.util.Collection; import java.util.Collection;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
@@ -130,6 +135,9 @@ public class SavingsAccountTransactionsApiResource {
@POST @POST
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ 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, public String transaction(@PathParam("savingsId") final Long savingsId, @QueryParam("command") final String commandParam,
final String apiRequestBodyAsJson) { final String apiRequestBodyAsJson) {
try { 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" @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: 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" + "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))) @RequestBody(required = true, content = @Content(schema = @Schema(implementation = SavingsProductsApiResourceSwagger.PostSavingsProductsRequest.class)))
@ApiResponses({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = SavingsProductsApiResourceSwagger.PostSavingsProductsResponse.class))) }) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = SavingsProductsApiResourceSwagger.PostSavingsProductsResponse.class))) })
@@ -37,9 +37,9 @@ public class SavingsAccountConstant extends SavingsApiConstants {
lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName,
// withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, // withdrawalFeeAmountParamName, withdrawalFeeTypeParamName,
withdrawalFeeForTransfersParamName, feeAmountParamName, feeOnMonthDayParamName, chargesParamName, allowOverdraftParamName, withdrawalFeeForTransfersParamName, feeAmountParamName, feeOnMonthDayParamName, chargesParamName, allowOverdraftParamName,
overdraftLimitParamName, minRequiredBalanceParamName, enforceMinRequiredBalanceParamName, overdraftLimitParamName, minRequiredBalanceParamName, enforceMinRequiredBalanceParamName, lienAllowedParamName,
nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName, withHoldTaxParamName, datatables, maxAllowedLienLimitParamName, nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName,
gsimApplicationId, gsimLastApplication)); withHoldTaxParamName, datatables, gsimApplicationId, gsimLastApplication));
/** /**
* These parameters will match the class level parameters of {@link SavingsAccountData}. Where possible, we try to * 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 overdraftLimit;
private final BigDecimal minRequiredBalance; private final BigDecimal minRequiredBalance;
private final boolean enforceMinRequiredBalance; private final boolean enforceMinRequiredBalance;
private final BigDecimal maxAllowedLienLimit;
private final boolean lienAllowed;
private final BigDecimal minBalanceForInterestCalculation; private final BigDecimal minBalanceForInterestCalculation;
private final BigDecimal onHoldFunds; private final BigDecimal onHoldFunds;
private final boolean withHoldTax; private final boolean withHoldTax;
@@ -191,6 +193,8 @@ public final class SavingsAccountData implements Serializable {
this.overdraftLimit = overdraftLimit; this.overdraftLimit = overdraftLimit;
this.minRequiredBalance = null; this.minRequiredBalance = null;
this.enforceMinRequiredBalance = false; this.enforceMinRequiredBalance = false;
this.maxAllowedLienLimit = null;
this.lienAllowed = false;
this.minBalanceForInterestCalculation = null; this.minBalanceForInterestCalculation = null;
this.onHoldFunds = null; this.onHoldFunds = null;
this.withHoldTax = false; this.withHoldTax = false;
@@ -546,6 +550,8 @@ public final class SavingsAccountData implements Serializable {
this.overdraftLimit = overdraftLimit; this.overdraftLimit = overdraftLimit;
this.minRequiredBalance = null; this.minRequiredBalance = null;
this.enforceMinRequiredBalance = false; this.enforceMinRequiredBalance = false;
this.maxAllowedLienLimit = null;
this.lienAllowed = false;
this.minBalanceForInterestCalculation = null; this.minBalanceForInterestCalculation = null;
this.onHoldFunds = null; this.onHoldFunds = null;
this.withHoldTax = false; this.withHoldTax = false;
@@ -594,11 +600,11 @@ public final class SavingsAccountData implements Serializable {
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType,
final boolean withdrawalFeeForTransfers, final SavingsAccountSummaryData summary, final boolean allowOverdraft, final boolean withdrawalFeeForTransfers, final SavingsAccountSummaryData summary, final boolean allowOverdraft,
final BigDecimal overdraftLimit, final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance, final BigDecimal overdraftLimit, final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance,
final BigDecimal minBalanceForInterestCalculation, final BigDecimal onHoldFunds, final BigDecimal maxAllowedLienLimit, final boolean lienAllowed, final BigDecimal minBalanceForInterestCalculation,
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation, final BigDecimal onHoldFunds, final BigDecimal nominalAnnualInterestRateOverdraft,
final boolean withHoldTax, final TaxGroupData taxGroup, final LocalDate lastActiveTransactionDate, final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax, final TaxGroupData taxGroup,
final boolean isDormancyTrackingActive, final Integer daysToInactive, final Integer daysToDormancy, final Integer daysToEscheat, final LocalDate lastActiveTransactionDate, final boolean isDormancyTrackingActive, final Integer daysToInactive,
final BigDecimal savingsAmountOnHold) { final Integer daysToDormancy, final Integer daysToEscheat, final BigDecimal savingsAmountOnHold) {
final Collection<SavingsProductData> productOptions = null; final Collection<SavingsProductData> productOptions = null;
final Collection<StaffData> fieldOfficerOptions = null; final Collection<StaffData> fieldOfficerOptions = null;
@@ -620,9 +626,9 @@ public final class SavingsAccountData implements Serializable {
transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, minRequiredBalance, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, minRequiredBalance,
enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft, enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation, onHoldFunds,
minOverdraftForInterestCalculation, withHoldTax, taxGroup, lastActiveTransactionDate, isDormancyTrackingActive, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, lastActiveTransactionDate,
daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold); isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
} }
public static SavingsAccountData lookup(final Long accountId, final String accountNo, final EnumOptionData depositType) { public static SavingsAccountData lookup(final Long accountId, final String accountNo, final EnumOptionData depositType) {
@@ -659,6 +665,8 @@ public final class SavingsAccountData implements Serializable {
final BigDecimal minOverdraftForInterestCalculation = null; final BigDecimal minOverdraftForInterestCalculation = null;
final BigDecimal minRequiredBalance = null; final BigDecimal minRequiredBalance = null;
final boolean enforceMinRequiredBalance = false; final boolean enforceMinRequiredBalance = false;
final BigDecimal maxAllowedLienLimit = null;
final boolean lienAllowed = false;
final BigDecimal minBalanceForInterestCalculation = null; final BigDecimal minBalanceForInterestCalculation = null;
final BigDecimal onHoldFunds = null; final BigDecimal onHoldFunds = null;
@@ -694,9 +702,9 @@ public final class SavingsAccountData implements Serializable {
withdrawalFeeForTransfers, summary, transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions, withdrawalFeeForTransfers, summary, transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit,
minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds, minRequiredBalance, enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, lastActiveTransactionDate, onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup,
isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold); lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
} }
public static SavingsAccountData lookupWithProductDetails(final Long accountId, final String accountNo, public static SavingsAccountData lookupWithProductDetails(final Long accountId, final String accountNo,
@@ -731,6 +739,8 @@ public final class SavingsAccountData implements Serializable {
final BigDecimal minOverdraftForInterestCalculation = null; final BigDecimal minOverdraftForInterestCalculation = null;
final BigDecimal minRequiredBalance = null; final BigDecimal minRequiredBalance = null;
final boolean enforceMinRequiredBalance = false; final boolean enforceMinRequiredBalance = false;
final BigDecimal maxAllowedLienLimit = null;
final boolean lienAllowed = false;
final BigDecimal minBalanceForInterestCalculation = null; final BigDecimal minBalanceForInterestCalculation = null;
final BigDecimal onHoldFunds = null; final BigDecimal onHoldFunds = null;
@@ -766,9 +776,9 @@ public final class SavingsAccountData implements Serializable {
withdrawalFeeForTransfers, summary, transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions, withdrawalFeeForTransfers, summary, transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit,
minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds, minRequiredBalance, enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, lastActiveTransactionDate, onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup,
isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold); lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
} }
public static SavingsAccountData withTemplateOptions(final SavingsAccountData account, final SavingsAccountData template, public static SavingsAccountData withTemplateOptions(final SavingsAccountData account, final SavingsAccountData template,
@@ -801,10 +811,10 @@ public final class SavingsAccountData implements Serializable {
template.interestCalculationTypeOptions, template.interestCalculationDaysInYearTypeOptions, template.interestCalculationTypeOptions, template.interestCalculationDaysInYearTypeOptions,
template.lockinPeriodFrequencyTypeOptions, template.withdrawalFeeTypeOptions, charges, template.chargeOptions, template.lockinPeriodFrequencyTypeOptions, template.withdrawalFeeTypeOptions, charges, template.chargeOptions,
account.allowOverdraft, account.overdraftLimit, account.minRequiredBalance, account.enforceMinRequiredBalance, account.allowOverdraft, account.overdraftLimit, account.minRequiredBalance, account.enforceMinRequiredBalance,
account.minBalanceForInterestCalculation, account.onHoldFunds, account.nominalAnnualInterestRateOverdraft, account.maxAllowedLienLimit, account.lienAllowed, account.minBalanceForInterestCalculation, account.onHoldFunds,
account.minOverdraftForInterestCalculation, account.withHoldTax, account.taxGroup, account.lastActiveTransactionDate, account.nominalAnnualInterestRateOverdraft, account.minOverdraftForInterestCalculation, account.withHoldTax,
account.isDormancyTrackingActive, account.daysToInactive, account.daysToDormancy, account.daysToEscheat, account.taxGroup, account.lastActiveTransactionDate, account.isDormancyTrackingActive, account.daysToInactive,
account.savingsAmountOnHold); account.daysToDormancy, account.daysToEscheat, account.savingsAmountOnHold);
} }
public static SavingsAccountData withTemplateOptions(final SavingsAccountData account, public static SavingsAccountData withTemplateOptions(final SavingsAccountData account,
@@ -827,10 +837,11 @@ public final class SavingsAccountData implements Serializable {
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges,
chargeOptions, account.allowOverdraft, account.overdraftLimit, account.minRequiredBalance, chargeOptions, account.allowOverdraft, account.overdraftLimit, account.minRequiredBalance,
account.enforceMinRequiredBalance, account.minBalanceForInterestCalculation, account.onHoldFunds, account.enforceMinRequiredBalance, account.maxAllowedLienLimit, account.lienAllowed,
account.nominalAnnualInterestRateOverdraft, account.minOverdraftForInterestCalculation, account.withHoldTax, account.minBalanceForInterestCalculation, account.onHoldFunds, account.nominalAnnualInterestRateOverdraft,
account.taxGroup, account.lastActiveTransactionDate, account.isDormancyTrackingActive, account.daysToInactive, account.minOverdraftForInterestCalculation, account.withHoldTax, account.taxGroup, account.lastActiveTransactionDate,
account.daysToDormancy, account.daysToEscheat, account.savingsAmountOnHold); account.isDormancyTrackingActive, account.daysToInactive, account.daysToDormancy, account.daysToEscheat,
account.savingsAmountOnHold);
} }
public static SavingsAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId, public static SavingsAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId,
@@ -867,6 +878,8 @@ public final class SavingsAccountData implements Serializable {
final BigDecimal minOverdraftForInterestCalculation = null; final BigDecimal minOverdraftForInterestCalculation = null;
final BigDecimal minRequiredBalance = null; final BigDecimal minRequiredBalance = null;
final boolean enforceMinRequiredBalance = false; final boolean enforceMinRequiredBalance = false;
final BigDecimal maxAllowedLienLimit = null;
final boolean lienAllowed = false;
final BigDecimal minBalanceForInterestCalculation = null; final BigDecimal minBalanceForInterestCalculation = null;
final BigDecimal onHoldFunds = null; final BigDecimal onHoldFunds = null;
final boolean withHoldTax = false; final boolean withHoldTax = false;
@@ -902,9 +915,9 @@ public final class SavingsAccountData implements Serializable {
withdrawalFeeForTransfers, summary, transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions, withdrawalFeeForTransfers, summary, transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit,
minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds, minRequiredBalance, enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, lastActiveTransactionDate, onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup,
isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold); lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
} }
private SavingsAccountData(final Long id, final String accountNo, final EnumOptionData depositType, final String externalId, private SavingsAccountData(final Long id, final String accountNo, final EnumOptionData depositType, final String externalId,
@@ -924,11 +937,11 @@ public final class SavingsAccountData implements Serializable {
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions, final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
final Collection<SavingsAccountChargeData> charges, final Collection<ChargeData> chargeOptions, final boolean allowOverdraft, final Collection<SavingsAccountChargeData> charges, final Collection<ChargeData> chargeOptions, final boolean allowOverdraft,
final BigDecimal overdraftLimit, final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance, final BigDecimal overdraftLimit, final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance,
final BigDecimal minBalanceForInterestCalculation, final BigDecimal onHoldFunds, final BigDecimal maxAllowedLienLimit, final boolean lienAllowd, final BigDecimal minBalanceForInterestCalculation,
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation, final BigDecimal onHoldFunds, final BigDecimal nominalAnnualInterestRateOverdraft,
final boolean withHoldTax, final TaxGroupData taxGroup, final LocalDate lastActiveTransactionDate, final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax, final TaxGroupData taxGroup,
final boolean isDormancyTrackingActive, final Integer daysToInactive, final Integer daysToDormancy, final Integer daysToEscheat, final LocalDate lastActiveTransactionDate, final boolean isDormancyTrackingActive, final Integer daysToInactive,
final BigDecimal savingsAmountOnHold) { final Integer daysToDormancy, final Integer daysToEscheat, final BigDecimal savingsAmountOnHold) {
this.id = id; this.id = id;
this.accountNo = accountNo; this.accountNo = accountNo;
this.depositType = depositType; this.depositType = depositType;
@@ -986,6 +999,8 @@ public final class SavingsAccountData implements Serializable {
this.minOverdraftForInterestCalculation = minOverdraftForInterestCalculation; this.minOverdraftForInterestCalculation = minOverdraftForInterestCalculation;
this.minRequiredBalance = minRequiredBalance; this.minRequiredBalance = minRequiredBalance;
this.enforceMinRequiredBalance = enforceMinRequiredBalance; this.enforceMinRequiredBalance = enforceMinRequiredBalance;
this.maxAllowedLienLimit = maxAllowedLienLimit;
this.lienAllowed = lienAllowd;
this.minBalanceForInterestCalculation = minBalanceForInterestCalculation; this.minBalanceForInterestCalculation = minBalanceForInterestCalculation;
this.onHoldFunds = onHoldFunds; this.onHoldFunds = onHoldFunds;
this.withHoldTax = withHoldTax; 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.interestCalculationTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName; 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.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.lockinPeriodFrequencyParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName; 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.minOverdraftForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateOverdraftParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateOverdraftParamName;
@@ -60,6 +62,7 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.data.ApiParameterError; import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
@@ -221,7 +224,7 @@ public class SavingsAccountDataValidator {
validateSavingsCharges(element, baseDataValidator); validateSavingsCharges(element, baseDataValidator);
validateOverdraftParams(baseDataValidator, element); validateOverdraftParams(baseDataValidator, element);
validateLienParams(baseDataValidator, element);
throwExceptionIfValidationWarningsExist(dataValidationErrors); 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) { private void validateOverdraftParams(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
if (this.fromApiJsonHelper.parameterExists(allowOverdraftParamName, element)) { if (this.fromApiJsonHelper.parameterExists(allowOverdraftParamName, element)) {
final Boolean allowOverdraft = this.fromApiJsonHelper.extractBooleanNamed(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 isManualTransaction;
private final Boolean isReversal; private final Boolean isReversal;
private final Long originalTransactionId; private final Long originalTransactionId;
private final Boolean lienTransaction;
private final Long releaseTransactionId; private final Long releaseTransactionId;
private final String reasonForBlock; private final String reasonForBlock;
private Set<SavingsAccountChargesPaidByData> chargesPaidByData = new HashSet<>(); 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, 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) {
return new SavingsAccountTransactionData(transactionAmount, transactionDate, paymentTypeId, accountNumber, checkNumber, routingCode, 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, public static SavingsAccountTransactionData interestPosting(final SavingsAccountData savingsAccount, final LocalDate date,
final Money amount, final boolean isManualTransaction) { final Money amount, final boolean isManualTransaction) {
final boolean isReversed = false; final boolean isReversed = false;
final Boolean lienTransaction = false;
final SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.INTEREST_POSTING; final SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.INTEREST_POSTING;
SavingsAccountTransactionEnumData savingsAccountTransactionEnumData = new SavingsAccountTransactionEnumData( SavingsAccountTransactionEnumData savingsAccountTransactionEnumData = new SavingsAccountTransactionEnumData(
savingsAccountTransactionType.getValue().longValue(), savingsAccountTransactionType.getCode(), savingsAccountTransactionType.getValue().longValue(), savingsAccountTransactionType.getCode(),
savingsAccountTransactionType.getValue().toString()); savingsAccountTransactionType.getValue().toString());
return new SavingsAccountTransactionData(amount.getAmount(), date, savingsAccount.getId(), savingsAccountTransactionEnumData, 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, public static SavingsAccountTransactionData overdraftInterest(final SavingsAccountData savingsAccount, final LocalDate date,
final Money amount, final boolean isManualTransaction) { final Money amount, final boolean isManualTransaction) {
final boolean isReversed = false; final boolean isReversed = false;
final Boolean lienTransaction = false;
final SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.OVERDRAFT_INTEREST; final SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.OVERDRAFT_INTEREST;
SavingsAccountTransactionEnumData savingsAccountTransactionEnumData = new SavingsAccountTransactionEnumData( SavingsAccountTransactionEnumData savingsAccountTransactionEnumData = new SavingsAccountTransactionEnumData(
savingsAccountTransactionType.getValue().longValue(), savingsAccountTransactionType.getCode(), savingsAccountTransactionType.getValue().longValue(), savingsAccountTransactionType.getCode(),
savingsAccountTransactionType.getValue().toString()); savingsAccountTransactionType.getValue().toString());
return new SavingsAccountTransactionData(amount.getAmount(), date, savingsAccount.getId(), savingsAccountTransactionEnumData, return new SavingsAccountTransactionData(amount.getAmount(), date, savingsAccount.getId(), savingsAccountTransactionEnumData,
isReversed, null, isManualTransaction); isReversed, null, isManualTransaction, lienTransaction);
} }
public List<TaxDetailsData> getTaxDetails() { public List<TaxDetailsData> getTaxDetails() {
@@ -311,12 +314,13 @@ public final class SavingsAccountTransactionData implements Serializable {
return new SavingsAccountTransactionData(accountTransaction.getSavingsAccountId(), null, accountTransaction.getPaymentDetailData(), return new SavingsAccountTransactionData(accountTransaction.getSavingsAccountId(), null, accountTransaction.getPaymentDetailData(),
accountTransaction.getTransactionType(), accountTransaction.getTransactionLocalDate(), accountTransaction.getTransactionType(), accountTransaction.getTransactionLocalDate(),
accountTransaction.getSubmittedOnDate(), accountTransaction.getAmount(), accountTransaction.isReversed(), null, accountTransaction.getSubmittedOnDate(), accountTransaction.getAmount(), accountTransaction.isReversed(), null,
accountTransaction.isManualTransaction()); accountTransaction.isManualTransaction(), accountTransaction.lienTransaction);
} }
private SavingsAccountTransactionData(final Long savingsId, final Long officeId, final PaymentDetailData paymentDetailData, private SavingsAccountTransactionData(final Long savingsId, final Long officeId, final PaymentDetailData paymentDetailData,
final SavingsAccountTransactionEnumData savingsAccountTransactionType, final LocalDate transactionDate, final Date createdDate, 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.savingsAccountId = savingsId;
this.paymentDetailData = paymentDetailData; this.paymentDetailData = paymentDetailData;
this.transactionType = savingsAccountTransactionType; this.transactionType = savingsAccountTransactionType;
@@ -324,6 +328,7 @@ public final class SavingsAccountTransactionData implements Serializable {
this.submittedOnDate = createdDate; this.submittedOnDate = createdDate;
this.amount = amount; this.amount = amount;
this.isManualTransaction = isManualTransaction; this.isManualTransaction = isManualTransaction;
this.lienTransaction = lienTransaction;
this.id = null; this.id = null;
this.accountId = null; this.accountId = null;
this.accountNo = null; this.accountNo = null;
@@ -442,12 +447,13 @@ public final class SavingsAccountTransactionData implements Serializable {
final Money amount, final Map<TaxComponentData, BigDecimal> taxDetails) { final Money amount, final Map<TaxComponentData, BigDecimal> taxDetails) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.WITHHOLD_TAX; SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.WITHHOLD_TAX;
SavingsAccountTransactionEnumData transactionType = new SavingsAccountTransactionEnumData( SavingsAccountTransactionEnumData transactionType = new SavingsAccountTransactionEnumData(
savingsAccountTransactionType.getValue().longValue(), savingsAccountTransactionType.getCode(), savingsAccountTransactionType.getValue().longValue(), savingsAccountTransactionType.getCode(),
savingsAccountTransactionType.getValue().toString()); savingsAccountTransactionType.getValue().toString());
SavingsAccountTransactionData accountTransaction = new SavingsAccountTransactionData(amount.getAmount(), date, SavingsAccountTransactionData accountTransaction = new SavingsAccountTransactionData(amount.getAmount(), date,
savingsAccount.getId(), transactionType, isReversed, null, isManualTransaction); savingsAccount.getId(), transactionType, isReversed, null, isManualTransaction, lienTransaction);
updateTaxDetails(taxDetails, accountTransaction); updateTaxDetails(taxDetails, accountTransaction);
return accountTransaction; return accountTransaction;
} }
@@ -515,7 +521,8 @@ public final class SavingsAccountTransactionData implements Serializable {
} }
private SavingsAccountTransactionData(BigDecimal transactionAmount, LocalDate transactionDate, Long savingsAccountId, 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.id = null;
this.transactionType = transactionType; this.transactionType = transactionType;
this.accountId = null; this.accountId = null;
@@ -548,13 +555,15 @@ public final class SavingsAccountTransactionData implements Serializable {
this.isManualTransaction = isManualTransaction; this.isManualTransaction = isManualTransaction;
this.isReversal = null; this.isReversal = null;
this.originalTransactionId = null; this.originalTransactionId = null;
this.lienTransaction = lienTransaction;
this.releaseTransactionId = null; this.releaseTransactionId = null;
this.reasonForBlock = null; this.reasonForBlock = null;
} }
private SavingsAccountTransactionData(BigDecimal transactionAmount, LocalDate transactionDate, Long paymentTypeId, String accountNumber, private SavingsAccountTransactionData(BigDecimal transactionAmount, LocalDate transactionDate, Long paymentTypeId, String accountNumber,
String checkNumber, String routingCode, String receiptNumber, String bankNumber, Long savingsAccountId, 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.id = null;
this.transactionType = transactionType; this.transactionType = transactionType;
this.accountId = null; this.accountId = null;
@@ -587,6 +596,7 @@ public final class SavingsAccountTransactionData implements Serializable {
this.isManualTransaction = false; this.isManualTransaction = false;
this.isReversal = null; this.isReversal = null;
this.originalTransactionId = null; this.originalTransactionId = null;
this.lienTransaction = lienTransaction;
this.releaseTransactionId = null; this.releaseTransactionId = null;
this.reasonForBlock = null; this.reasonForBlock = null;
} }
@@ -594,7 +604,7 @@ public final class SavingsAccountTransactionData implements Serializable {
private SavingsAccountTransactionData(Integer id, BigDecimal transactionAmount, LocalDate transactionDate, Long paymentTypeId, private SavingsAccountTransactionData(Integer id, BigDecimal transactionAmount, LocalDate transactionDate, Long paymentTypeId,
String accountNumber, String checkNumber, String routingCode, String receiptNumber, String bankNumber, Long savingsAccountId, 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,
BigDecimal cumulativeBalance) { BigDecimal cumulativeBalance, final Boolean lienTransaction) {
this.id = null; this.id = null;
this.transactionType = transactionType; this.transactionType = transactionType;
this.accountId = null; this.accountId = null;
@@ -628,6 +638,7 @@ public final class SavingsAccountTransactionData implements Serializable {
this.isManualTransaction = false; this.isManualTransaction = false;
this.isReversal = null; this.isReversal = null;
this.originalTransactionId = null; this.originalTransactionId = null;
this.lienTransaction = lienTransaction;
this.releaseTransactionId = null; this.releaseTransactionId = null;
this.reasonForBlock = null; this.reasonForBlock = null;
} }
@@ -721,24 +732,27 @@ public final class SavingsAccountTransactionData implements Serializable {
final Collection<PaymentTypeData> paymentTypeOptions = null; final Collection<PaymentTypeData> paymentTypeOptions = null;
return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency, return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency,
amount, outstandingChargeAmount, runningBalance, reversed, submittedOnDate, paymentTypeOptions, interestedPostedAsOn, amount, outstandingChargeAmount, runningBalance, reversed, submittedOnDate, paymentTypeOptions, interestedPostedAsOn,
cumulativeBalance, balanceEndDate); cumulativeBalance, balanceEndDate, false);
} }
private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType, private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date, final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date,
final CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance, final CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance,
final boolean reversed, final LocalDate submittedOnDate, final Collection<PaymentTypeData> paymentTypeOptions, 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, 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, private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date, final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date,
final CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance, final CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance,
final boolean reversed, final Collection<PaymentTypeData> paymentTypeOptions, final LocalDate submittedOnDate, 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.id = id;
this.transactionDate = date; this.transactionDate = date;
this.transactionType = transactionType; this.transactionType = transactionType;
@@ -762,6 +776,7 @@ public final class SavingsAccountTransactionData implements Serializable {
this.isManualTransaction = false; this.isManualTransaction = false;
this.isReversal = null; this.isReversal = null;
this.originalTransactionId = null; this.originalTransactionId = null;
this.lienTransaction = lienTransaction;
this.releaseTransactionId = null; this.releaseTransactionId = null;
this.reasonForBlock = null; this.reasonForBlock = null;
} }
@@ -774,7 +789,7 @@ public final class SavingsAccountTransactionData implements Serializable {
final Collection<PaymentTypeData> paymentTypeOptions = null; final Collection<PaymentTypeData> paymentTypeOptions = null;
return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency, return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency,
amount, outstandingChargeAmount, runningBalance, reversed, transfer, paymentTypeOptions, interestedPostedAsOn, amount, outstandingChargeAmount, runningBalance, reversed, transfer, paymentTypeOptions, interestedPostedAsOn,
submittedByUsername, note); submittedByUsername, note, false);
} }
public static SavingsAccountTransactionData create(final Long id, final SavingsAccountTransactionEnumData transactionType, 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 CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance,
final boolean reversed, final AccountTransferData transfer, final LocalDate submittedOnDate, final boolean interestedPostedAsOn, final boolean reversed, final AccountTransferData transfer, final LocalDate submittedOnDate, final boolean interestedPostedAsOn,
final String submittedByUsername, final String note, final Boolean isReversal, final Long originalTransactionId, 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; final Collection<PaymentTypeData> paymentTypeOptions = null;
return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency, return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency,
amount, outstandingChargeAmount, runningBalance, reversed, transfer, paymentTypeOptions, submittedOnDate, amount, outstandingChargeAmount, runningBalance, reversed, transfer, paymentTypeOptions, submittedOnDate,
interestedPostedAsOn, submittedByUsername, note, isReversal, originalTransactionId, releaseTransactionId, reasonForBlock); interestedPostedAsOn, submittedByUsername, note, isReversal, originalTransactionId, lienTransaction, releaseTransactionId,
reasonForBlock);
} }
public static SavingsAccountTransactionData create(final Long id) { public static SavingsAccountTransactionData create(final Long id) {
final Collection<PaymentTypeData> paymentTypeOptions = null; final Collection<PaymentTypeData> paymentTypeOptions = null;
return new SavingsAccountTransactionData(id, null, null, null, null, null, null, null, null, null, false, null, paymentTypeOptions, return new SavingsAccountTransactionData(id, null, null, null, null, null, null, null, null, null, false, null, paymentTypeOptions,
null, false, null, null, null, null); null, false, null, null, null, null, false, null, null);
} }
public static SavingsAccountTransactionData template(final Long savingsId, final String savingsAccountNo, 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 boolean interestedPostedAsOn = false;
final String submittedByUsername = null; final String submittedByUsername = null;
final String note = null; final String note = null;
final Boolean lienTransaction = false;
return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, defaultLocalDate, return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, defaultLocalDate,
currency, amount, outstandingChargeAmount, runningBalance, reversed, null, null, interestedPostedAsOn, submittedByUsername, currency, amount, outstandingChargeAmount, runningBalance, reversed, null, null, interestedPostedAsOn, submittedByUsername,
note); note, lienTransaction);
} }
public static SavingsAccountTransactionData templateOnTop(final SavingsAccountTransactionData savingsAccountTransactionData, public static SavingsAccountTransactionData templateOnTop(final SavingsAccountTransactionData savingsAccountTransactionData,
@@ -821,17 +838,19 @@ public final class SavingsAccountTransactionData implements Serializable {
savingsAccountTransactionData.amount, savingsAccountTransactionData.outstandingChargeAmount, savingsAccountTransactionData.amount, savingsAccountTransactionData.outstandingChargeAmount,
savingsAccountTransactionData.runningBalance, savingsAccountTransactionData.reversed, savingsAccountTransactionData.runningBalance, savingsAccountTransactionData.reversed,
savingsAccountTransactionData.transfer, paymentTypeOptions, savingsAccountTransactionData.interestedPostedAsOn, savingsAccountTransactionData.transfer, paymentTypeOptions, savingsAccountTransactionData.interestedPostedAsOn,
savingsAccountTransactionData.submittedByUsername, savingsAccountTransactionData.note); savingsAccountTransactionData.submittedByUsername, savingsAccountTransactionData.note,
savingsAccountTransactionData.lienTransaction);
} }
private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType, private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date, final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date,
final CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance, final CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance,
final boolean reversed, final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions, final boolean reversed, final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions,
final 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, 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, 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 CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance,
final boolean reversed, final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions, final boolean reversed, final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions,
final LocalDate submittedOnDate, final boolean interestedPostedAsOn, final String submittedByUsername, final String note, final LocalDate submittedOnDate, final boolean interestedPostedAsOn, final String submittedByUsername, final String note,
final Boolean isReversal, final Long originalTransactionId, 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.id = id;
this.transactionType = transactionType; this.transactionType = transactionType;
this.paymentDetailData = paymentDetailData; this.paymentDetailData = paymentDetailData;
@@ -865,6 +885,7 @@ public final class SavingsAccountTransactionData implements Serializable {
this.isManualTransaction = false; this.isManualTransaction = false;
this.isReversal = isReversal; this.isReversal = isReversal;
this.originalTransactionId = originalTransactionId; this.originalTransactionId = originalTransactionId;
this.lienTransaction = lienTransaction;
this.releaseTransactionId = releaseTransactionId; this.releaseTransactionId = releaseTransactionId;
this.reasonForBlock = reasonForBlock; 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 PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate date,
final CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance, final CurrencyData currency, final BigDecimal amount, final BigDecimal outstandingChargeAmount, final BigDecimal runningBalance,
final boolean reversed, final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions, final boolean reversed, final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions,
final LocalDate submittedOnDate, final boolean interestedPostedAsOn, final BigDecimal cumulativeBalance) { final LocalDate submittedOnDate, final boolean interestedPostedAsOn, final BigDecimal cumulativeBalance,
final Boolean lienTransaction) {
this.id = id; this.id = id;
this.transactionType = transactionType; this.transactionType = transactionType;
this.paymentDetailData = paymentDetailData; this.paymentDetailData = paymentDetailData;
@@ -900,41 +922,7 @@ public final class SavingsAccountTransactionData implements Serializable {
this.isManualTransaction = false; this.isManualTransaction = false;
this.isReversal = null; this.isReversal = null;
this.originalTransactionId = null; this.originalTransactionId = null;
this.releaseTransactionId = null; this.lienTransaction = lienTransaction;
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.releaseTransactionId = null; this.releaseTransactionId = null;
this.reasonForBlock = null; this.reasonForBlock = null;
} }
@@ -953,7 +941,7 @@ public final class SavingsAccountTransactionData implements Serializable {
savingsAccountTransactionData.runningBalance, savingsAccountTransactionData.reversed, savingsAccountTransactionData.runningBalance, savingsAccountTransactionData.reversed,
savingsAccountTransactionData.transfer, savingsAccountTransactionData.paymentTypeOptions, savingsAccountTransactionData.transfer, savingsAccountTransactionData.paymentTypeOptions,
savingsAccountTransactionData.interestedPostedAsOn, savingsAccountTransactionData.submittedByUsername, savingsAccountTransactionData.interestedPostedAsOn, savingsAccountTransactionData.submittedByUsername,
savingsAccountTransactionData.note); savingsAccountTransactionData.note, savingsAccountTransactionData.lienTransaction);
} }
public void setId(final Long id) { 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.bankNumberParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.checkNumberParamName; 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.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.paymentTypeIdParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.receiptNumberParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.receiptNumberParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.routingCodeParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.routingCodeParamName;
@@ -44,6 +44,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.api.JsonCommand; 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.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils; 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.SavingsApiConstants;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount; import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler; import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
@@ -71,8 +70,7 @@ public class SavingsAccountTransactionDataValidator {
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
private static final Set<String> SAVINGS_ACCOUNT_HOLD_AMOUNT_REQUEST_DATA_PARAMETERS = new HashSet<>( private static final Set<String> SAVINGS_ACCOUNT_HOLD_AMOUNT_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(transactionDateParamName, SavingsApiConstants.dateFormatParamName, SavingsApiConstants.localeParamName, Arrays.asList(transactionDateParamName, SavingsApiConstants.dateFormatParamName, SavingsApiConstants.localeParamName,
transactionAmountParamName, lienParamName, SavingsApiConstants.reasonForBlockParamName)); transactionAmountParamName, lienAllowedParamName, SavingsApiConstants.reasonForBlockParamName));
private final ConfigurationDomainService configurationDomainService; private final ConfigurationDomainService configurationDomainService;
private final SavingsAccountAssembler savingAccountAssembler; 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) { final boolean backdatedTxnsAllowedTill) {
if (StringUtils.isBlank(json)) { if (StringUtils.isBlank(json)) {
@@ -249,21 +247,43 @@ public class SavingsAccountTransactionDataValidator {
.failWithCodeNoParameterAddedToErrorCode(SavingsApiConstants.ERROR_MSG_SAVINGS_ACCOUNT_NOT_ACTIVE); .failWithCodeNoParameterAddedToErrorCode(SavingsApiConstants.ERROR_MSG_SAVINGS_ACCOUNT_NOT_ACTIVE);
} }
Boolean lien = false; Boolean isEnforceMinRequiredBalanceEnabled = account.getEnforceMinRequiredBalance();
if (this.fromApiJsonHelper.parameterExists(lienParamName, element)) { Boolean isAccountLienEnabled = account.isLienAllowed();
lien = this.fromApiJsonHelper.extractBooleanNamed(lienParamName, element); Boolean isOverdraftEnabled = account.isAllowOverdraft();
}
if (account.getEnforceMinRequiredBalance()) { Boolean lienAllowed = false;
if (account.getWithdrawableBalance().compareTo(BigDecimal.ZERO) < 0) { if (BooleanUtils.isTrue(fromApiJsonHelper.extractBooleanNamed(lienAllowedParamName, element))) {
if (!lien) { lienAllowed = this.fromApiJsonHelper.extractBooleanNamed(lienAllowedParamName, element);
if (account.getWithdrawableBalanceWithoutMinimumBalance().compareTo(account.getMinRequiredBalance()) < 0) { if (isAccountLienEnabled) {
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("insufficient balance", account.getId()); 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; LocalDate lastTransactionDate = null;
if (!backdatedTxnsAllowedTill) { if (!backdatedTxnsAllowedTill) {
@@ -279,13 +299,6 @@ public class SavingsAccountTransactionDataValidator {
} }
throwExceptionIfValidationWarningsExist(dataValidationErrors); 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, public SavingsAccountTransaction validateReleaseAmountAndAssembleForm(final SavingsAccountTransaction holdTransaction,
@@ -312,7 +325,6 @@ public class SavingsAccountTransactionDataValidator {
throwExceptionIfValidationWarningsExist(dataValidationErrors); throwExceptionIfValidationWarningsExist(dataValidationErrors);
Date createdDate = new Date(); Date createdDate = new Date();
LocalDate transactionDate = DateUtils.getLocalDateOfTenant(); LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
SavingsAccountTransaction transaction = SavingsAccountTransaction.releaseAmount(holdTransaction, transactionDate, createdDate, SavingsAccountTransaction transaction = SavingsAccountTransaction.releaseAmount(holdTransaction, transactionDate, createdDate,
createdUser); createdUser);
return transaction; return transaction;
@@ -56,6 +56,8 @@ public final class SavingsProductData implements Serializable {
private final BigDecimal overdraftLimit; private final BigDecimal overdraftLimit;
private final BigDecimal minRequiredBalance; private final BigDecimal minRequiredBalance;
private final boolean enforceMinRequiredBalance; private final boolean enforceMinRequiredBalance;
private final BigDecimal maxAllowedLienLimit;
private final boolean lienAllowed;
private final BigDecimal minBalanceForInterestCalculation; private final BigDecimal minBalanceForInterestCalculation;
private final BigDecimal nominalAnnualInterestRateOverdraft; private final BigDecimal nominalAnnualInterestRateOverdraft;
private final BigDecimal minOverdraftForInterestCalculation; private final BigDecimal minOverdraftForInterestCalculation;
@@ -124,6 +126,8 @@ public final class SavingsProductData implements Serializable {
final BigDecimal overdraftLimit = null; final BigDecimal overdraftLimit = null;
final BigDecimal minRequiredBalance = null; final BigDecimal minRequiredBalance = null;
final boolean enforceMinRequiredBalance = false; final boolean enforceMinRequiredBalance = false;
final BigDecimal maxAllowedLienLimit = null;
final boolean lienAllowed = false;
final BigDecimal minBalanceForInterestCalculation = null; final BigDecimal minBalanceForInterestCalculation = null;
final BigDecimal nominalAnnualInterestRateOverdraft = null; final BigDecimal nominalAnnualInterestRateOverdraft = null;
final BigDecimal minOverdraftForInterestCalculation = null; final BigDecimal minOverdraftForInterestCalculation = null;
@@ -141,9 +145,9 @@ public final class SavingsProductData implements Serializable {
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, charges, chargeOptions,
penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit, penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit,
minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minRequiredBalance, enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation,
minOverdraftForInterestCalculation, withHoldTax, taxGroup, taxGroupOptions, isDormancyTrackingActive, daysToInactive, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, taxGroupOptions,
daysToDormancy, daysToEscheat, accountMappingForPayment); isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, accountMappingForPayment);
} }
public static SavingsProductData withCharges(final SavingsProductData product, final Collection<ChargeData> charges) { 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.lockinPeriodFrequencyTypeOptions, product.withdrawalFeeTypeOptions, product.paymentTypeOptions,
product.accountingRuleOptions, product.accountingMappingOptions, charges, product.chargeOptions, product.penaltyOptions, product.accountingRuleOptions, product.accountingMappingOptions, charges, product.chargeOptions, product.penaltyOptions,
product.feeToIncomeAccountMappings, product.penaltyToIncomeAccountMappings, product.allowOverdraft, product.overdraftLimit, product.feeToIncomeAccountMappings, product.penaltyToIncomeAccountMappings, product.allowOverdraft, product.overdraftLimit,
product.minRequiredBalance, product.enforceMinRequiredBalance, product.minBalanceForInterestCalculation, product.minRequiredBalance, product.enforceMinRequiredBalance, product.maxAllowedLienLimit, product.lienAllowed,
product.nominalAnnualInterestRateOverdraft, product.minOverdraftForInterestCalculation, product.withHoldTax, product.minBalanceForInterestCalculation, product.nominalAnnualInterestRateOverdraft,
product.taxGroup, product.taxGroupOptions, product.isDormancyTrackingActive, product.daysToInactive, product.daysToDormancy, product.minOverdraftForInterestCalculation, product.withHoldTax, product.taxGroup, product.taxGroupOptions,
product.daysToEscheat, product.accountMappingForPayment); 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, accountingRuleOptions, accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions,
existingProduct.feeToIncomeAccountMappings, existingProduct.penaltyToIncomeAccountMappings, existingProduct.allowOverdraft, existingProduct.feeToIncomeAccountMappings, existingProduct.penaltyToIncomeAccountMappings, existingProduct.allowOverdraft,
existingProduct.overdraftLimit, existingProduct.minRequiredBalance, existingProduct.enforceMinRequiredBalance, existingProduct.overdraftLimit, existingProduct.minRequiredBalance, existingProduct.enforceMinRequiredBalance,
existingProduct.minBalanceForInterestCalculation, existingProduct.nominalAnnualInterestRateOverdraft, existingProduct.maxAllowedLienLimit, existingProduct.lienAllowed, existingProduct.minBalanceForInterestCalculation,
existingProduct.minOverdraftForInterestCalculation, existingProduct.withHoldTax, existingProduct.taxGroup, taxGroupOptions, existingProduct.nominalAnnualInterestRateOverdraft, existingProduct.minOverdraftForInterestCalculation,
existingProduct.isDormancyTrackingActive, existingProduct.daysToInactive, existingProduct.daysToDormancy, existingProduct.withHoldTax, existingProduct.taxGroup, taxGroupOptions, existingProduct.isDormancyTrackingActive,
existingProduct.daysToEscheat, accountMappingForPayment); existingProduct.daysToInactive, existingProduct.daysToDormancy, existingProduct.daysToEscheat, accountMappingForPayment);
} }
public static SavingsProductData withAccountingDetails(final SavingsProductData existingProduct, public static SavingsProductData withAccountingDetails(final SavingsProductData existingProduct,
@@ -227,11 +232,12 @@ public final class SavingsProductData implements Serializable {
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions,
accountingRuleOptions, accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions, accountingRuleOptions, accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions,
feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, existingProduct.allowOverdraft, existingProduct.overdraftLimit, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, existingProduct.allowOverdraft, existingProduct.overdraftLimit,
existingProduct.minRequiredBalance, existingProduct.enforceMinRequiredBalance, existingProduct.minRequiredBalance, existingProduct.enforceMinRequiredBalance, existingProduct.maxAllowedLienLimit,
existingProduct.minBalanceForInterestCalculation, existingProduct.nominalAnnualInterestRateOverdraft, existingProduct.lienAllowed, existingProduct.minBalanceForInterestCalculation,
existingProduct.minOverdraftForInterestCalculation, existingProduct.withHoldTax, existingProduct.taxGroup, existingProduct.nominalAnnualInterestRateOverdraft, existingProduct.minOverdraftForInterestCalculation,
existingProduct.taxGroupOptions, existingProduct.isDormancyTrackingActive, existingProduct.daysToInactive, existingProduct.withHoldTax, existingProduct.taxGroup, existingProduct.taxGroupOptions,
existingProduct.daysToDormancy, existingProduct.daysToEscheat, existingProduct.accountMappingForPayment); 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, 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 EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers, final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers,
final EnumOptionData accountingType, final boolean allowOverdraft, final BigDecimal overdraftLimit, 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 BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation,
final boolean withHoldTax, final TaxGroupData taxGroup, final Boolean isDormancyTrackingActive, final Long daysToInactive, final boolean withHoldTax, final TaxGroupData taxGroup, final Boolean isDormancyTrackingActive, final Long daysToInactive,
final Long daysToDormancy, final Long daysToEscheat) { final Long daysToDormancy, final Long daysToEscheat) {
@@ -273,9 +280,9 @@ public final class SavingsProductData implements Serializable {
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, charges, chargeOptions,
penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit, penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit,
minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minRequiredBalance, enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation,
minOverdraftForInterestCalculation, withHoldTax, taxGroup, taxGroupOptions, isDormancyTrackingActive, daysToInactive, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, taxGroupOptions,
daysToDormancy, daysToEscheat, accountMappingForPayment); isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, accountMappingForPayment);
} }
public static SavingsProductData lookup(final Long id, final String name) { 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 minOverdraftForInterestCalculation = null;
final BigDecimal minRequiredBalance = null; final BigDecimal minRequiredBalance = null;
final boolean enforceMinRequiredBalance = false; final boolean enforceMinRequiredBalance = false;
final BigDecimal maxAllowedLienLimit = null;
final boolean lienAllowed = false;
final BigDecimal minBalanceForInterestCalculation = null; final BigDecimal minBalanceForInterestCalculation = null;
final boolean withHoldTax = false; final boolean withHoldTax = false;
final TaxGroupData taxGroup = null; final TaxGroupData taxGroup = null;
@@ -334,9 +343,9 @@ public final class SavingsProductData implements Serializable {
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, charges, chargeOptions,
penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit, penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit,
minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minRequiredBalance, enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation,
minOverdraftForInterestCalculation, withHoldTax, taxGroup, taxGroupOptions, isDormancyTrackingActive, daysToInactive, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, taxGroupOptions,
daysToDormancy, daysToEscheat, accountMappingForPayment); isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, accountMappingForPayment);
} }
public static SavingsProductData createForInterestPosting(final Long id, final EnumOptionData accountingRule) { public static SavingsProductData createForInterestPosting(final Long id, final EnumOptionData accountingRule) {
@@ -387,6 +396,8 @@ public final class SavingsProductData implements Serializable {
this.overdraftLimit = null; this.overdraftLimit = null;
this.minRequiredBalance = null; this.minRequiredBalance = null;
this.enforceMinRequiredBalance = false; this.enforceMinRequiredBalance = false;
this.maxAllowedLienLimit = null;
this.lienAllowed = false;
this.minBalanceForInterestCalculation = null; this.minBalanceForInterestCalculation = null;
this.nominalAnnualInterestRateOverdraft = null; this.nominalAnnualInterestRateOverdraft = null;
this.minOverdraftForInterestCalculation = null; this.minOverdraftForInterestCalculation = null;
@@ -418,10 +429,11 @@ public final class SavingsProductData implements Serializable {
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings, final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings,
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings, final boolean allowOverdraft, final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings, final boolean allowOverdraft,
final BigDecimal overdraftLimit, final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance, final BigDecimal overdraftLimit, final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance,
final BigDecimal minBalanceForInterestCalculation, final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal maxAllowedLienLimit, final boolean lienAllowed, final BigDecimal minBalanceForInterestCalculation,
final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax, final TaxGroupData taxGroup, final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation,
final Collection<TaxGroupData> taxGroupOptions, final Boolean isDormancyTrackingActive, final Long daysToInactive, final boolean withHoldTax, final TaxGroupData taxGroup, final Collection<TaxGroupData> taxGroupOptions,
final Long daysToDormancy, final Long daysToEscheat, final String accountMappingForPayment) { final Boolean isDormancyTrackingActive, final Long daysToInactive, final Long daysToDormancy, final Long daysToEscheat,
final String accountMappingForPayment) {
this.id = id; this.id = id;
this.name = name; this.name = name;
this.shortName = shortName; this.shortName = shortName;
@@ -468,6 +480,8 @@ public final class SavingsProductData implements Serializable {
this.overdraftLimit = overdraftLimit; this.overdraftLimit = overdraftLimit;
this.minRequiredBalance = minRequiredBalance; this.minRequiredBalance = minRequiredBalance;
this.enforceMinRequiredBalance = enforceMinRequiredBalance; this.enforceMinRequiredBalance = enforceMinRequiredBalance;
this.maxAllowedLienLimit = maxAllowedLienLimit;
this.lienAllowed = lienAllowed;
this.minBalanceForInterestCalculation = minBalanceForInterestCalculation; this.minBalanceForInterestCalculation = minBalanceForInterestCalculation;
this.nominalAnnualInterestRateOverdraft = nominalAnnualInterestRateOverdraft; this.nominalAnnualInterestRateOverdraft = nominalAnnualInterestRateOverdraft;
this.minOverdraftForInterestCalculation = minOverdraftForInterestCalculation; this.minOverdraftForInterestCalculation = minOverdraftForInterestCalculation;
@@ -563,6 +577,10 @@ public final class SavingsProductData implements Serializable {
return minRequiredBalance; return minRequiredBalance;
} }
public BigDecimal getMaxAllowedLienLimit() {
return maxAllowedLienLimit;
}
public Long getId() { public Long getId() {
return id; 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.interestCompoundingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName; 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.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.lockinPeriodFrequencyParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName; 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.minBalanceForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minOverdraftForInterestCalculationParamName; 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.minRequiredOpeningBalanceParamName;
@@ -62,6 +64,7 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.accounting.common.AccountingConstants.SavingProductAccountingParams; import org.apache.fineract.accounting.common.AccountingConstants.SavingProductAccountingParams;
import org.apache.fineract.accounting.common.AccountingRuleType; import org.apache.fineract.accounting.common.AccountingRuleType;
@@ -103,6 +106,7 @@ public class SavingsProductDataValidator {
daysToInactiveParamName, daysToEscheatParamName, allowOverdraftParamName, overdraftLimitParamName, daysToInactiveParamName, daysToEscheatParamName, allowOverdraftParamName, overdraftLimitParamName,
nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName, nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName,
SavingsApiConstants.minRequiredBalanceParamName, SavingsApiConstants.enforceMinRequiredBalanceParamName, SavingsApiConstants.minRequiredBalanceParamName, SavingsApiConstants.enforceMinRequiredBalanceParamName,
SavingsApiConstants.maxAllowedLienLimitParamName, SavingsApiConstants.lienAllowedParamName,
minBalanceForInterestCalculationParamName, withHoldTaxParamName, taxGroupIdParamName)); minBalanceForInterestCalculationParamName, withHoldTaxParamName, taxGroupIdParamName));
@Autowired @Autowired
@@ -340,9 +344,8 @@ public class SavingsProductDataValidator {
baseDataValidator.reset().parameter(minBalanceForInterestCalculationParamName).value(minBalanceForInterestCalculation) baseDataValidator.reset().parameter(minBalanceForInterestCalculationParamName).value(minBalanceForInterestCalculation)
.ignoreIfNull().zeroOrPositiveAmount(); .ignoreIfNull().zeroOrPositiveAmount();
} }
validateTaxWithHoldingParams(baseDataValidator, element, true); validateTaxWithHoldingParams(baseDataValidator, element, true);
validateLienParams(baseDataValidator, element);
throwExceptionIfValidationWarningsExist(dataValidationErrors); throwExceptionIfValidationWarningsExist(dataValidationErrors);
} }
@@ -545,7 +548,7 @@ public class SavingsProductDataValidator {
} }
validateTaxWithHoldingParams(baseDataValidator, element, false); validateTaxWithHoldingParams(baseDataValidator, element, false);
validateLienParams(baseDataValidator, element);
throwExceptionIfValidationWarningsExist(dataValidationErrors); 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, private void validateTaxWithHoldingParams(final DataValidatorBuilder baseDataValidator, final JsonElement element,
final boolean isCreate) { final boolean isCreate) {
if (this.fromApiJsonHelper.parameterExists(withHoldTaxParamName, element)) { 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.allowOverdraftParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName; 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.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.localeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName; 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.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.minOverdraftForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredBalanceParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredBalanceParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateOverdraftParamName; 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) @Column(name = "min_required_balance", scale = 6, precision = 19, nullable = true)
private BigDecimal minRequiredBalance; 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) @Column(name = "on_hold_funds_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal onHoldFunds; 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) @Column(name = "total_savings_amount_on_hold", scale = 6, precision = 19, nullable = true)
private BigDecimal savingsOnHoldAmount; private BigDecimal savingsOnHoldAmount;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "account", orphanRemoval = true, fetch = FetchType.LAZY) @OneToMany(cascade = CascadeType.ALL, mappedBy = "account", orphanRemoval = true, fetch = FetchType.LAZY)
protected List<InteropIdentifier> identifiers = new ArrayList<>(); protected List<InteropIdentifier> identifiers = new ArrayList<>();
@@ -354,15 +361,17 @@ public class SavingsAccount extends AbstractPersistableCustom {
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final boolean withdrawalFeeApplicableForTransfer, final Set<SavingsAccountCharge> savingsAccountCharges, final boolean withdrawalFeeApplicableForTransfer, final Set<SavingsAccountCharge> savingsAccountCharges,
final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance, final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance,
final BigDecimal minRequiredBalance, final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minRequiredBalance, final BigDecimal maxAllowedLienLimit, final boolean lienAllowed,
final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax) { final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation,
final boolean withHoldTax) {
final SavingsAccountStatusType status = SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL; final SavingsAccountStatusType status = SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL;
return new SavingsAccount(client, group, product, fieldOfficer, accountNo, externalId, status, accountType, submittedOnDate, return new SavingsAccount(client, group, product, fieldOfficer, accountNo, externalId, status, accountType, submittedOnDate,
submittedBy, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, submittedBy, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
withdrawalFeeApplicableForTransfer, savingsAccountCharges, allowOverdraft, overdraftLimit, enforceMinRequiredBalance, 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, 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, this(client, group, product, fieldOfficer, accountNo, externalId, status, accountType, submittedOnDate, submittedBy,
nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
withdrawalFeeApplicableForTransfer, savingsAccountCharges, allowOverdraft, overdraftLimit, false, null, null, null, withdrawalFeeApplicableForTransfer, savingsAccountCharges, allowOverdraft, overdraftLimit, false, null, null, false, null,
withHoldTax); null, withHoldTax);
} }
protected SavingsAccount(final Client client, final Group group, final SavingsProduct product, final Staff savingsOfficer, 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 Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final boolean withdrawalFeeApplicableForTransfer, final Set<SavingsAccountCharge> savingsAccountCharges, final boolean withdrawalFeeApplicableForTransfer, final Set<SavingsAccountCharge> savingsAccountCharges,
final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance, final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance,
final BigDecimal minRequiredBalance, final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minRequiredBalance, final BigDecimal maxAllowedLienLimit, final boolean lienAllowed,
final BigDecimal minOverdraftForInterestCalculation, boolean withHoldTax) { final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation, boolean withHoldTax) {
this.client = client; this.client = client;
this.group = group; this.group = group;
this.product = product; this.product = product;
@@ -434,6 +443,8 @@ public class SavingsAccount extends AbstractPersistableCustom {
this.enforceMinRequiredBalance = enforceMinRequiredBalance; this.enforceMinRequiredBalance = enforceMinRequiredBalance;
this.minRequiredBalance = minRequiredBalance; this.minRequiredBalance = minRequiredBalance;
this.lienAllowed = lienAllowed;
this.maxAllowedLienLimit = maxAllowedLienLimit;
this.minBalanceForInterestCalculation = product.minBalanceForInterestCalculation(); this.minBalanceForInterestCalculation = product.minBalanceForInterestCalculation();
// this.savingsOfficerHistory = null; // this.savingsOfficerHistory = null;
this.withHoldTax = withHoldTax; this.withHoldTax = withHoldTax;
@@ -1751,6 +1762,18 @@ public class SavingsAccount extends AbstractPersistableCustom {
actualChanges.put(localeParamName, localeAsInput); actualChanges.put(localeParamName, localeAsInput);
this.minRequiredBalance = newValue; 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)) { if (command.isChangeInBooleanParameterNamed(withHoldTaxParamName, this.withHoldTax)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName); final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName);
@@ -1950,6 +1973,10 @@ public class SavingsAccount extends AbstractPersistableCustom {
return this.enforceMinRequiredBalance; return this.enforceMinRequiredBalance;
} }
public Boolean getLienAllowed() {
return this.lienAllowed;
}
public void unassignSavingsOfficer() { public void unassignSavingsOfficer() {
this.savingsOfficer = null; this.savingsOfficer = null;
} }
@@ -3429,6 +3456,11 @@ public class SavingsAccount extends AbstractPersistableCustom {
.subtract(this.getSavingsHoldAmount()); .subtract(this.getSavingsHoldAmount());
} }
public BigDecimal getWithdrawableBalanceWithLien() {
return getAccountBalance().subtract(minRequiredBalanceDerived(getCurrency()).getAmount()).subtract(this.getOnHoldFunds())
.subtract(this.getSavingsHoldAmount());
}
public BigDecimal getWithdrawableBalanceWithoutMinimumBalance() { public BigDecimal getWithdrawableBalanceWithoutMinimumBalance() {
return getAccountBalance().subtract(this.getOnHoldFunds()).subtract(this.getSavingsHoldAmount()); return getAccountBalance().subtract(this.getOnHoldFunds()).subtract(this.getSavingsHoldAmount());
} }
@@ -3833,6 +3865,14 @@ public class SavingsAccount extends AbstractPersistableCustom {
return this.enforceMinRequiredBalance; return this.enforceMinRequiredBalance;
} }
public BigDecimal getMaxAllowedLienLimit() {
return this.maxAllowedLienLimit;
}
public boolean isLienAllowed() {
return this.lienAllowed;
}
public BigDecimal getMinBalanceForInterestCalculation() { public BigDecimal getMinBalanceForInterestCalculation() {
return this.minBalanceForInterestCalculation; 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.interestCalculationTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName; 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.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.lockinPeriodFrequencyParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName; 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.minOverdraftForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredBalanceParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredBalanceParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName;
@@ -294,6 +296,20 @@ public class SavingsAccountAssembler {
minRequiredBalance = product.minRequiredBalance(); 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(); boolean withHoldTax = product.withHoldTax();
if (command.parameterExists(withHoldTaxParamName)) { if (command.parameterExists(withHoldTaxParamName)) {
withHoldTax = command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName); withHoldTax = command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName);
@@ -306,8 +322,8 @@ public class SavingsAccountAssembler {
externalId, accountType, submittedOnDate, submittedBy, interestRate, interestCompoundingPeriodType, externalId, accountType, submittedOnDate, submittedBy, interestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, iswithdrawalFeeApplicableForTransfer, charges, allowOverdraft, lockinPeriodFrequency, lockinPeriodFrequencyType, iswithdrawalFeeApplicableForTransfer, charges, allowOverdraft,
overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, nominalAnnualInterestRateOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, maxAllowedLienLimit, lienAllowed,
minOverdraftForInterestCalculation, withHoldTax); nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax);
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper); account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
account.validateNewApplicationState(DateUtils.getLocalDateOfTenant(), SAVINGS_ACCOUNT_RESOURCE_NAME); account.validateNewApplicationState(DateUtils.getLocalDateOfTenant(), SAVINGS_ACCOUNT_RESOURCE_NAME);
@@ -423,8 +439,9 @@ public class SavingsAccountAssembler {
product.interestPostingPeriodType(), product.interestCalculationType(), product.interestCalculationDaysInYearType(), product.interestPostingPeriodType(), product.interestCalculationType(), product.interestCalculationDaysInYearType(),
product.minRequiredOpeningBalance(), product.lockinPeriodFrequency(), product.lockinPeriodFrequencyType(), product.minRequiredOpeningBalance(), product.lockinPeriodFrequency(), product.lockinPeriodFrequencyType(),
product.isWithdrawalFeeApplicableForTransfer(), charges, product.isAllowOverdraft(), product.overdraftLimit(), product.isWithdrawalFeeApplicableForTransfer(), charges, product.isAllowOverdraft(), product.overdraftLimit(),
product.isMinRequiredBalanceEnforced(), product.minRequiredBalance(), product.nominalAnnualInterestRateOverdraft(), product.isMinRequiredBalanceEnforced(), product.minRequiredBalance(), product.maxAllowedLienLimit(),
product.minOverdraftForInterestCalculation(), product.withHoldTax()); product.isLienAllowed(), product.nominalAnnualInterestRateOverdraft(), product.minOverdraftForInterestCalculation(),
product.withHoldTax());
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper); account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
account.validateNewApplicationState(DateUtils.getLocalDateOfTenant(), SAVINGS_ACCOUNT_RESOURCE_NAME); account.validateNewApplicationState(DateUtils.getLocalDateOfTenant(), SAVINGS_ACCOUNT_RESOURCE_NAME);
@@ -24,6 +24,7 @@ import java.time.format.DateTimeFormatter;
import java.util.Set; import java.util.Set;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail; import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
import org.apache.fineract.portfolio.savings.SavingsTransactionBooleanValues; import org.apache.fineract.portfolio.savings.SavingsTransactionBooleanValues;
import org.apache.fineract.useradministration.domain.AppUser;
public interface SavingsAccountDomainService { public interface SavingsAccountDomainService {
@@ -43,4 +44,7 @@ public interface SavingsAccountDomainService {
SavingsAccountTransaction handleReversal(SavingsAccount account, SavingsAccountTransaction savingsAccountTransaction, SavingsAccountTransaction handleReversal(SavingsAccount account, SavingsAccountTransaction savingsAccountTransaction,
boolean backdatedTxnsAllowedTill); 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.ApplicationCurrency;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper; import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BusinessEntity; import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BusinessEntity;
import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BusinessEvents; import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BusinessEvents;
@@ -219,6 +220,18 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
return deposit; 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 @Override
public SavingsAccountTransaction handleDividendPayout(final SavingsAccount account, final LocalDate transactionDate, public SavingsAccountTransaction handleDividendPayout(final SavingsAccount account, final LocalDate transactionDate,
final BigDecimal transactionAmount, final boolean backdatedTxnsAllowedTill) { final BigDecimal transactionAmount, final boolean backdatedTxnsAllowedTill) {
@@ -140,6 +140,9 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
@Column(name = "original_transaction_id") @Column(name = "original_transaction_id")
private Long originalTxnId; private Long originalTxnId;
@Column(name = "is_lien_transaction")
private Boolean lienTransaction;
SavingsAccountTransaction() { SavingsAccountTransaction() {
this.dateOf = null; this.dateOf = null;
this.typeOf = 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 PaymentDetail paymentDetail, final LocalDate date, final Money amount, Date createdDate, final AppUser appUser) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.DEPOSIT.getValue(), date, 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, public static SavingsAccountTransaction deposit(final SavingsAccount savingsAccount, final Office office,
@@ -159,54 +163,61 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
final SavingsAccountTransactionType savingsAccountTransactionType) { final SavingsAccountTransactionType savingsAccountTransactionType) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, savingsAccountTransactionType.getValue(), date, 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, public static SavingsAccountTransaction withdrawal(final SavingsAccount savingsAccount, final Office office,
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, Date createdDate, final AppUser appUser) { final PaymentDetail paymentDetail, final LocalDate date, final Money amount, Date createdDate, final AppUser appUser) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.WITHDRAWAL.getValue(), 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, public static SavingsAccountTransaction interestPosting(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final boolean isManualTransaction) { final Money amount, final boolean isManualTransaction) {
final boolean isReversed = false; final boolean isReversed = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.INTEREST_POSTING.getValue(), date, 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, public static SavingsAccountTransaction overdraftInterest(final SavingsAccount savingsAccount, final Office office,
final LocalDate date, final Money amount, final boolean isManualTransaction) { final LocalDate date, final Money amount, final boolean isManualTransaction) {
final boolean isReversed = false; final boolean isReversed = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.OVERDRAFT_INTEREST.getValue(), date, 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, public static SavingsAccountTransaction withdrawalFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final AppUser appUser) { final Money amount, final AppUser appUser) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue(), date, amount, 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, public static SavingsAccountTransaction annualFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final AppUser appUser) { final Money amount, final AppUser appUser) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.ANNUAL_FEE.getValue(), date, amount, 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, public static SavingsAccountTransaction charge(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final AppUser appUser) { final Money amount, final AppUser appUser) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.PAY_CHARGE.getValue(), date, amount, 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, 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 Money amount, final AppUser appUser) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.WAIVE_CHARGES.getValue(), date, amount, 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, 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 isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null; final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.INITIATE_TRANSFER.getValue(), date, new Date(), 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, 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 isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null; final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.APPROVE_TRANSFER.getValue(), date, new Date(), 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, 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 isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null; final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.WITHDRAW_TRANSFER.getValue(), date, new Date(), 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, public static SavingsAccountTransaction withHoldTax(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final Map<TaxComponent, BigDecimal> taxDetails) { final Money amount, final Map<TaxComponent, BigDecimal> taxDetails) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
SavingsAccountTransaction accountTransaction = new SavingsAccountTransaction(savingsAccount, office, 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); updateTaxDetails(taxDetails, accountTransaction);
return accountTransaction; return accountTransaction;
} }
@@ -269,9 +286,10 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
final boolean accountTransaction) { final boolean accountTransaction) {
final boolean isReversed = false; final boolean isReversed = false;
final PaymentDetail paymentDetail = null; final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, savingsAccount.office(), paymentDetail, return new SavingsAccountTransaction(savingsAccount, savingsAccount.office(), paymentDetail,
SavingsAccountTransactionType.ESCHEAT.getValue(), date, new Date(), savingsAccount.getSummary().getAccountBalance(), 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, 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) { public static SavingsAccountTransaction copyTransaction(SavingsAccountTransaction accountTransaction) {
return new SavingsAccountTransaction(accountTransaction.savingsAccount, accountTransaction.office, accountTransaction.paymentDetail, return new SavingsAccountTransaction(accountTransaction.savingsAccount, accountTransaction.office, accountTransaction.paymentDetail,
accountTransaction.typeOf, accountTransaction.transactionLocalDate(), accountTransaction.createdDate, 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, private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final Integer typeOf,
final LocalDate transactionLocalDate, final Money amount, final boolean isReversed, final AppUser appUser, final LocalDate transactionLocalDate, final Money amount, final boolean isReversed, final AppUser appUser,
final boolean isManualTransaction) { final boolean isManualTransaction, final Boolean lienTransaction) {
this(savingsAccount, office, null, typeOf, transactionLocalDate, new Date(), amount, isReversed, appUser, isManualTransaction); this(savingsAccount, office, null, typeOf, transactionLocalDate, new Date(), amount, isReversed, appUser, isManualTransaction,
lienTransaction);
} }
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final PaymentDetail paymentDetail, private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final PaymentDetail paymentDetail,
final Integer typeOf, final LocalDate transactionLocalDate, final Date createdDate, final Money amount, 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, 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, private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final PaymentDetail paymentDetail,
final Integer typeOf, final LocalDate transactionLocalDate, final Date createdDate, final BigDecimal amount, 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.savingsAccount = savingsAccount;
this.office = office; this.office = office;
this.typeOf = typeOf; this.typeOf = typeOf;
@@ -316,6 +336,7 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
this.createdDate = createdDate; this.createdDate = createdDate;
this.appUser = appUser; this.appUser = appUser;
this.isManualTransaction = isManualTransaction; this.isManualTransaction = isManualTransaction;
this.lienTransaction = lienTransaction;
} }
private SavingsAccountTransaction(final Integer transactionTypeEnum, final LocalDate transactionDate, final BigDecimal amount, 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, 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 isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.AMOUNT_HOLD.getValue(), 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, public static SavingsAccountTransaction releaseAmount(SavingsAccountTransaction accountTransaction, LocalDate transactionDate,
Date createdDate, final AppUser appUser) { Date createdDate, final AppUser appUser) {
return new SavingsAccountTransaction(accountTransaction.savingsAccount, accountTransaction.office, accountTransaction.paymentDetail, return new SavingsAccountTransaction(accountTransaction.savingsAccount, accountTransaction.office, accountTransaction.paymentDetail,
SavingsAccountTransactionType.AMOUNT_RELEASE.getValue(), transactionDate, createdDate, accountTransaction.amount, 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) { 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.interestCompoundingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName; 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.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.localeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName; 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.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.minBalanceForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minOverdraftForInterestCalculationParamName; 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.minRequiredBalanceParamName;
@@ -179,6 +181,12 @@ public class SavingsProduct extends AbstractPersistableCustom {
@Column(name = "min_required_balance", scale = 6, precision = 19, nullable = true) @Column(name = "min_required_balance", scale = 6, precision = 19, nullable = true)
private BigDecimal minRequiredBalance; 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) @Column(name = "min_balance_for_interest_calculation", scale = 6, precision = 19, nullable = true)
private BigDecimal minBalanceForInterestCalculation; private BigDecimal minBalanceForInterestCalculation;
@@ -209,17 +217,17 @@ public class SavingsProduct extends AbstractPersistableCustom {
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set<Charge> charges, final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set<Charge> charges,
final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance, final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance,
final BigDecimal minRequiredBalance, final BigDecimal minBalanceForInterestCalculation, final BigDecimal minRequiredBalance, final boolean lienAllowed, final BigDecimal maxAllowedLienLimit,
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation, boolean withHoldTax, final BigDecimal minBalanceForInterestCalculation, final BigDecimal nominalAnnualInterestRateOverdraft,
TaxGroup taxGroup, final Boolean isDormancyTrackingActive, final Long daysToInactive, final Long daysToDormancy, final BigDecimal minOverdraftForInterestCalculation, boolean withHoldTax, TaxGroup taxGroup,
final Long daysToEscheat) { final Boolean isDormancyTrackingActive, final Long daysToInactive, final Long daysToDormancy, final Long daysToEscheat) {
return new SavingsProduct(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, return new SavingsProduct(name, shortName, description, currency, interestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges,
allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, minBalanceForInterestCalculation, allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, lienAllowed, maxAllowedLienLimit,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, isDormancyTrackingActive, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax,
daysToInactive, daysToDormancy, daysToEscheat); taxGroup, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
} }
protected SavingsProduct() { protected SavingsProduct() {
@@ -238,7 +246,7 @@ public class SavingsProduct extends AbstractPersistableCustom {
this(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, this(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges, allowOverdraft, overdraftLimit, 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, 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 Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set<Charge> charges, final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set<Charge> charges,
final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance, final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance,
final BigDecimal minRequiredBalance, BigDecimal minBalanceForInterestCalculation, final BigDecimal minRequiredBalance, final boolean lienAllowed, final BigDecimal maxAllowedLienLimit,
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation, BigDecimal minBalanceForInterestCalculation, final BigDecimal nominalAnnualInterestRateOverdraft,
final boolean withHoldTax, final TaxGroup taxGroup, final Boolean isDormancyTrackingActive, final Long daysToInactive, final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax, final TaxGroup taxGroup,
final Long daysToDormancy, final Long daysToEscheat) { final Boolean isDormancyTrackingActive, final Long daysToInactive, final Long daysToDormancy, final Long daysToEscheat) {
this.name = name; this.name = name;
this.shortName = shortName; this.shortName = shortName;
@@ -293,6 +301,8 @@ public class SavingsProduct extends AbstractPersistableCustom {
this.enforceMinRequiredBalance = enforceMinRequiredBalance; this.enforceMinRequiredBalance = enforceMinRequiredBalance;
this.minRequiredBalance = minRequiredBalance; this.minRequiredBalance = minRequiredBalance;
this.lienAllowed = lienAllowed;
this.maxAllowedLienLimit = maxAllowedLienLimit;
this.minBalanceForInterestCalculation = minBalanceForInterestCalculation; this.minBalanceForInterestCalculation = minBalanceForInterestCalculation;
this.withHoldTax = withHoldTax; this.withHoldTax = withHoldTax;
this.taxGroup = taxGroup; this.taxGroup = taxGroup;
@@ -540,6 +550,19 @@ public class SavingsProduct extends AbstractPersistableCustom {
this.minRequiredBalance = newValue; 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, if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(minBalanceForInterestCalculationParamName,
this.minBalanceForInterestCalculation)) { this.minBalanceForInterestCalculation)) {
final BigDecimal newValue = command final BigDecimal newValue = command
@@ -689,6 +712,14 @@ public class SavingsProduct extends AbstractPersistableCustom {
return this.enforceMinRequiredBalance; return this.enforceMinRequiredBalance;
} }
public BigDecimal maxAllowedLienLimit() {
return this.maxAllowedLienLimit;
}
public boolean isLienAllowed() {
return this.lienAllowed;
}
public Set<Charge> charges() { public Set<Charge> charges() {
return this.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.interestCompoundingPeriodTypeParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName; 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.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.lockinPeriodFrequencyParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName; 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.minBalanceForInterestCalculationParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minOverdraftForInterestCalculationParamName; 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.minRequiredBalanceParamName;
@@ -170,6 +172,16 @@ public class SavingsProductAssembler {
if (command.parameterExists(minRequiredBalanceParamName)) { if (command.parameterExists(minRequiredBalanceParamName)) {
minRequiredBalance = command.bigDecimalValueOfParameterNamed(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 final BigDecimal minBalanceForInterestCalculation = command
.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minBalanceForInterestCalculationParamName); .bigDecimalValueOfParameterNamedDefaultToNullIfZero(minBalanceForInterestCalculationParamName);
@@ -184,9 +196,9 @@ public class SavingsProductAssembler {
return SavingsProduct.createNew(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, return SavingsProduct.createNew(name, shortName, description, currency, interestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, iswithdrawalFeeApplicableForTransfer, accountingRuleType, charges, lockinPeriodFrequency, lockinPeriodFrequencyType, iswithdrawalFeeApplicableForTransfer, accountingRuleType, charges,
allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, minBalanceForInterestCalculation, allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, lienAllowed, maxAllowedLienLimit,
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup, isDormancyTrackingActive, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax,
daysToInactive, daysToDormancy, daysToEscheat); taxGroup, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat);
} }
public Set<Charge> assembleListOfSavingsProductCharges(final JsonCommand command, final String savingsProductCurrencyCode) { 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_balance_for_interest_calculation as minBalanceForInterestCalculation,");
sqlBuilder.append("sa.min_required_balance as minRequiredBalance, "); sqlBuilder.append("sa.min_required_balance as minRequiredBalance, ");
sqlBuilder.append("sa.enforce_min_required_balance as enforceMinRequiredBalance, "); 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.on_hold_funds_derived as onHoldFunds, ");
sqlBuilder.append("sa.withhold_tax as withHoldTax, "); sqlBuilder.append("sa.withhold_tax as withHoldTax, ");
sqlBuilder.append("sa.total_withhold_tax_derived as totalWithholdTax, "); sqlBuilder.append("sa.total_withhold_tax_derived as totalWithholdTax, ");
@@ -575,14 +577,17 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final BigDecimal minRequiredBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredBalance"); final BigDecimal minRequiredBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredBalance");
final boolean enforceMinRequiredBalance = rs.getBoolean("enforceMinRequiredBalance"); 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, savingsAccountData = SavingsAccountData.instance(id, accountNo, depositType, externalId, null, null, null, null,
productId, null, null, null, status, subStatus, null, timeline, currency, nominalAnnualInterestRate, productId, null, null, null, status, subStatus, null, timeline, currency, nominalAnnualInterestRate,
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
withdrawalFeeForTransfers, summary, allowOverdraft, overdraftLimit, minRequiredBalance, withdrawalFeeForTransfers, summary, allowOverdraft, overdraftLimit, minRequiredBalance,
enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft, enforceMinRequiredBalance, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation, onHoldFunds,
minOverdraftForInterestCalculation, withHoldTax, taxGroupData, lastActiveTransactionDate, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroupData,
isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat, onHoldAmount); lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat,
onHoldAmount);
savingsAccountData.setClientData(clientData); savingsAccountData.setClientData(clientData);
savingsAccountData.setGroupGeneralData(groupGeneralData); 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_balance_for_interest_calculation as minBalanceForInterestCalculation,");
sqlBuilder.append("sa.min_required_balance as minRequiredBalance, "); sqlBuilder.append("sa.min_required_balance as minRequiredBalance, ");
sqlBuilder.append("sa.enforce_min_required_balance as enforceMinRequiredBalance, "); 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.on_hold_funds_derived as onHoldFunds, ");
sqlBuilder.append("sa.withhold_tax as withHoldTax, "); sqlBuilder.append("sa.withhold_tax as withHoldTax, ");
sqlBuilder.append("sa.total_withhold_tax_derived as totalWithholdTax, "); sqlBuilder.append("sa.total_withhold_tax_derived as totalWithholdTax, ");
@@ -991,6 +998,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final BigDecimal minRequiredBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredBalance"); final BigDecimal minRequiredBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredBalance");
final boolean enforceMinRequiredBalance = rs.getBoolean("enforceMinRequiredBalance"); 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"); * final BigDecimal annualFeeAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "annualFeeAmount");
* *
@@ -1057,9 +1067,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
withdrawalFeeForTransfers, summary, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, withdrawalFeeForTransfers, summary, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
withHoldTax, taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, minOverdraftForInterestCalculation, withHoldTax, taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive,
daysToEscheat, onHoldAmount); daysToInactive, daysToDormancy, daysToEscheat, onHoldAmount);
} }
} }
@@ -1320,7 +1330,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
sqlBuilder.append(" au.username as submittedByUsername, "); sqlBuilder.append(" au.username as submittedByUsername, ");
sqlBuilder.append(" nt.note as transactionNote, "); sqlBuilder.append(" nt.note as transactionNote, ");
sqlBuilder.append("tr.running_balance_derived as runningBalance, tr.is_reversed as reversed,"); sqlBuilder.append("tr.running_balance_derived as runningBalance, tr.is_reversed as reversed,");
sqlBuilder.append("tr.is_reversal as isReversal, tr.original_transaction_id as originalTransactionId,"); sqlBuilder.append(
"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.id as fromTransferId, fromtran.is_reversed as fromTransferReversed,");
sqlBuilder.append("fromtran.transaction_date as fromTransferDate, fromtran.amount as fromTransferAmount,"); sqlBuilder.append("fromtran.transaction_date as fromTransferDate, fromtran.amount as fromTransferAmount,");
sqlBuilder.append("fromtran.description as fromTransferDescription,"); sqlBuilder.append("fromtran.description as fromTransferDescription,");
@@ -1368,6 +1379,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final boolean reversed = rs.getBoolean("reversed"); final boolean reversed = rs.getBoolean("reversed");
final boolean isReversal = rs.getBoolean("isReversal"); final boolean isReversal = rs.getBoolean("isReversal");
final Long originalTransactionId = rs.getLong("originalTransactionId"); final Long originalTransactionId = rs.getLong("originalTransactionId");
final Boolean lienTransaction = rs.getBoolean("lienTransaction");
final Long savingsId = rs.getLong("savingsId"); final Long savingsId = rs.getLong("savingsId");
final String accountNo = rs.getString("accountNo"); final String accountNo = rs.getString("accountNo");
@@ -1422,7 +1434,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final String note = rs.getString("transactionNote"); final String note = rs.getString("transactionNote");
return SavingsAccountTransactionData.create(id, transactionType, paymentDetailData, savingsId, accountNo, date, currency, return SavingsAccountTransactionData.create(id, transactionType, paymentDetailData, savingsId, accountNo, date, currency,
amount, outstandingChargeAmount, runningBalance, reversed, transfer, submittedOnDate, postInterestAsOn, amount, outstandingChargeAmount, runningBalance, reversed, transfer, submittedOnDate, postInterestAsOn,
submittedByUsername, note, 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.annual_fee_on_day as annualFeeOnDay ");
sqlBuilder.append("sp.min_required_balance as minRequiredBalance, "); 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("from m_savings_product sp ");
sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code "); 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 "); 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 BigDecimal minRequiredBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredBalance");
final boolean enforceMinRequiredBalance = rs.getBoolean("enforceMinRequiredBalance"); 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, final BigDecimal minBalanceForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
"minBalanceForInterestCalculation"); "minBalanceForInterestCalculation");
@@ -1639,9 +1655,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
nominalAnnualIterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, nominalAnnualIterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
withdrawalFeeForTransfers, summary, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, withdrawalFeeForTransfers, summary, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
withHoldTax, taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive, daysToInactive, daysToDormancy, minOverdraftForInterestCalculation, withHoldTax, taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive,
daysToEscheat, savingsAmountOnHold); 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.amountParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargeIdParamName; 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.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.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.withHoldTaxParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawBalanceParamName; 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 AppUser submittedBy = this.context.authenticatedUser();
final boolean backdatedTxnsAllowedTill = this.savingAccountAssembler.getPivotConfigStatus(); final boolean backdatedTxnsAllowedTill = this.savingAccountAssembler.getPivotConfigStatus();
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId, backdatedTxnsAllowedTill); final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId, backdatedTxnsAllowedTill);
final LocalDate transactionDate = command.localDateValueOfParameterNamed(transactionDateParamName);
final boolean lienAllowed = command.booleanObjectValueOfParameterNamed(lienAllowedParamName);
checkClientOrGroupActive(account); checkClientOrGroupActive(account);
@@ -1823,11 +1827,11 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
runningBalance = runningBalance.minus(amount); 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); account.holdAmount(amount);
SavingsAccountTransaction transaction = this.savingsAccountTransactionDataValidator.validateHoldAndAssembleForm(command.json(),
account, submittedBy, backdatedTxnsAllowedTill);
transaction.updateRunningBalance(runningBalance); transaction.updateRunningBalance(runningBalance);
final String reasonForBlock = command.stringValueOfParameterNamed(SavingsApiConstants.reasonForBlockParamName); 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_overdraft_for_interest_calculation as minOverdraftForInterestCalculation, ");
sqlBuilder.append("sp.min_required_balance as minRequiredBalance, "); 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("sp.min_balance_for_interest_calculation as minBalanceForInterestCalculation,"); sqlBuilder.append("sp.min_balance_for_interest_calculation as minBalanceForInterestCalculation,");
sqlBuilder.append("sp.accounting_type as accountingType, "); sqlBuilder.append("sp.accounting_type as accountingType, ");
sqlBuilder.append("sp.withhold_tax as withHoldTax,"); sqlBuilder.append("sp.withhold_tax as withHoldTax,");
@@ -203,6 +205,8 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
final BigDecimal minRequiredBalance = rs.getBigDecimal("minRequiredBalance"); final BigDecimal minRequiredBalance = rs.getBigDecimal("minRequiredBalance");
final boolean enforceMinRequiredBalance = rs.getBoolean("enforceMinRequiredBalance"); 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 BigDecimal minBalanceForInterestCalculation = rs.getBigDecimal("minBalanceForInterestCalculation");
final boolean withHoldTax = rs.getBoolean("withHoldTax"); final boolean withHoldTax = rs.getBoolean("withHoldTax");
@@ -221,9 +225,9 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
return SavingsProductData.instance(id, name, shortName, description, currency, nominalAnnualInterestRate, return SavingsProductData.instance(id, name, shortName, description, currency, nominalAnnualInterestRate,
compoundingInterestPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, compoundingInterestPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers,
accountingRuleType, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, accountingRuleType, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, maxAllowedLienLimit,
minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, lienAllowed, minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation,
taxGroupData, isDormancyTrackingActive, daysToInactive, daysToDormancy, daysToEscheat); 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/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/0008_loan_charge_add_external_id.xml" relativeToChangelogFile="true"/>
<include file="parts/0009_hold_reason_savings_account.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> </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); 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, private Integer createSavingsProduct(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final String minOpenningBalance, String minBalanceForInterestCalculation, String minRequiredBalance, final String minOpenningBalance, String minBalanceForInterestCalculation, String minRequiredBalance,
String enforceMinRequiredBalance, final boolean allowOverdraft, final String taxGroupId, boolean withDormancy) { String enforceMinRequiredBalance, final boolean allowOverdraft, final String taxGroupId, boolean withDormancy) {
@@ -2177,26 +2239,23 @@ public class ClientSavingsIntegrationTest {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Test @Test
public void testSavingsAccountLienStatus() { public void testSavingsAccountLienAllowedAtProductLevelWithEnforceBalance() {
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
SavingsAccountHelper savingsAccountHelperValidationError = new SavingsAccountHelper(this.requestSpec, SavingsAccountHelper savingsAccountHelperValidationError = new SavingsAccountHelper(this.requestSpec,
new ResponseSpecBuilder().build()); new ResponseSpecBuilder().build());
final ResponseSpecification errorResponse = new ResponseSpecBuilder().expectStatusCode(403).build(); final ResponseSpecification errorResponse = new ResponseSpecBuilder().expectStatusCode(403).build();
final SavingsAccountHelper validationErrorHelper = new SavingsAccountHelper(this.requestSpec, errorResponse); 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); final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID); ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
final String minBalanceForInterestCalculation = null; final String minBalanceForInterestCalculation = null;
final String minRequiredBalance = null; final boolean enforceMinRequiredBalance = true;
final String enforceMinRequiredBalance = "true";
final boolean allowOverdraft = false; final boolean allowOverdraft = false;
final boolean lienAllowed = true;
final Integer savingsProductID = createSavingsProduct(this.requestSpec, this.responseSpec, MINIMUM_OPENING_BALANCE, final Integer savingsProductID = createSavingsProduct(this.requestSpec, this.responseSpec, MINIMUM_OPENING_BALANCE,
minBalanceForInterestCalculation, minRequiredBalance, enforceMinRequiredBalance, allowOverdraft); minBalanceForInterestCalculation, enforceMinRequiredBalance, allowOverdraft, lienAllowed);
Assertions.assertNotNull(savingsProductID); Assertions.assertNotNull(savingsProductID);
final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplication(clientID, savingsProductID, ACCOUNT_TYPE_INDIVIDUAL); final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplication(clientID, savingsProductID, ACCOUNT_TYPE_INDIVIDUAL);
@@ -2211,38 +2270,264 @@ public class ClientSavingsIntegrationTest {
savingsStatusHashMap = this.savingsAccountHelper.activateSavings(savingsId); savingsStatusHashMap = this.savingsAccountHelper.activateSavings(savingsId);
SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap); SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
List<HashMap> error = (List) savingsAccountHelperValidationError.holdAmountInSavingsAccount(savingsId, "1500", false, List<HashMap> error = (List) savingsAccountHelperValidationError.holdAmountInSavingsAccount(savingsId, "2000", false,
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_ERROR); // lien: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 lienHoldTransactionId = (Integer) this.savingsAccountHelper.holdAmountInSavingsAccount(savingsId, "2000", true,
Integer holdTransactionId = (Integer) this.savingsAccountHelper.holdAmountInSavingsAccount(savingsId, "1500", true,
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID); SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
HashMap summary = this.savingsAccountHelper.getSavingsSummary(savingsId); 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"); assertEquals(balance, summary.get("availableBalance"), "Verifying available Balance is -1000");
Integer depositTransactionId = (Integer) this.savingsAccountHelper.depositToSavingsAccount(savingsId, "1200",
Integer depositTransactionId = (Integer) this.savingsAccountHelper.depositToSavingsAccount(savingsId, "1000",
SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID); SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID);
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy"); DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
Calendar todaysDate = Calendar.getInstance(); Calendar todaysDate = Calendar.getInstance();
final String TRANSACTION_DATE = dateFormat.format(todaysDate.getTime()); final String TRANSACTION_DATE = dateFormat.format(todaysDate.getTime());
ArrayList<HashMap> savingsAccountErrorData = (ArrayList<HashMap>) validationErrorHelper.withdrawalFromSavingsAccount(savingsId, 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", assertEquals("error.msg.savingsaccount.transaction.insufficient.account.balance",
savingsAccountErrorData.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE)); 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); CommonConstants.RESPONSE_RESOURCE_ID);
Assertions.assertNotNull(withdrawTransactionId); 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 * incorrect savings account balance when charge transaction is reversed during an overdraft recalculate Daily
* Balances * Balances
@@ -377,11 +377,12 @@ public class SavingsAccountHelper {
getActivatedSavingsAsJSON(), isBlock); 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) { String jsonAttributeToGetback) {
LOG.info("--------------------------------- SAVINGS TRANSACTION HOLD AMOUNT--------------------------------"); LOG.info("--------------------------------- SAVINGS TRANSACTION HOLD AMOUNT--------------------------------");
return performSavingActions(createSavingsTransactionURL(HOLD_AMOUNT_SAVINGS_COMMAND, savingsID), 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) { public Integer releaseAmount(final Integer savingsId, final Integer transactionId) {
@@ -459,13 +460,13 @@ public class SavingsAccountHelper {
return savingsAccountWithdrawalJson; 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<>(); final HashMap<String, Object> map = new HashMap<>();
map.put("locale", CommonConstants.LOCALE); map.put("locale", CommonConstants.LOCALE);
map.put("dateFormat", CommonConstants.DATE_FORMAT); map.put("dateFormat", CommonConstants.DATE_FORMAT);
map.put("transactionDate", transactionDate); map.put("transactionDate", transactionDate);
map.put("transactionAmount", amount); map.put("transactionAmount", amount);
map.put("lien", lien); map.put("lienAllowed", lienAllowed);
map.put("reasonForBlock", "unUsualActivity"); map.put("reasonForBlock", "unUsualActivity");
String savingsAccountWithdrawalJson = new Gson().toJson(map); String savingsAccountWithdrawalJson = new Gson().toJson(map);
LOG.info(savingsAccountWithdrawalJson); LOG.info(savingsAccountWithdrawalJson);
@@ -86,6 +86,8 @@ public class SavingsProductHelper {
private String overdraftLimit = null; private String overdraftLimit = null;
private String minRequiredBalance = null; private String minRequiredBalance = null;
private String enforceMinRequiredBalance = "false"; private String enforceMinRequiredBalance = "false";
private String lienAllowed = "false";
private String maxAllowedLienLimit = null;
private Boolean withHoldTax = false; private Boolean withHoldTax = false;
private String taxGroupId = null; private String taxGroupId = null;
private boolean isDormancyTrackingActive = false; private boolean isDormancyTrackingActive = false;
@@ -129,6 +131,8 @@ public class SavingsProductHelper {
map.put("overdraftLimit", this.overdraftLimit); map.put("overdraftLimit", this.overdraftLimit);
map.put("minRequiredBalance", this.minRequiredBalance); map.put("minRequiredBalance", this.minRequiredBalance);
map.put("enforceMinRequiredBalance", this.enforceMinRequiredBalance); map.put("enforceMinRequiredBalance", this.enforceMinRequiredBalance);
map.put("lienAllowed", this.lienAllowed);
map.put("maxAllowedLienLimit", this.maxAllowedLienLimit);
map.put("withHoldTax", this.withHoldTax.toString()); map.put("withHoldTax", this.withHoldTax.toString());
if (withHoldTax) { if (withHoldTax) {
@@ -216,16 +220,22 @@ public class SavingsProductHelper {
return this; return this;
} }
public SavingsProductHelper withMinRequiredBalance(String minBalance) { public SavingsProductHelper withMinRequiredBalance(final String minRequiredBalance) {
this.minRequiredBalance = minBalance; this.minRequiredBalance = minRequiredBalance;
return this; return this;
} }
public SavingsProductHelper withEnforceMinRequiredBalance(String enforceMinRequiredBalance) { public SavingsProductHelper withEnforceMinRequiredBalance(final String enforceMinRequiredBalance) {
this.enforceMinRequiredBalance = enforceMinRequiredBalance; this.enforceMinRequiredBalance = enforceMinRequiredBalance;
return this; return this;
} }
public SavingsProductHelper withLienAllowed(final String maxAllowedLienLimit) {
this.lienAllowed = "true";
this.maxAllowedLienLimit = maxAllowedLienLimit;
return this;
}
public SavingsProductHelper withOverDraft(final String overdraftLimit) { public SavingsProductHelper withOverDraft(final String overdraftLimit) {
this.allowOverdraft = "true"; this.allowOverdraft = "true";
this.overdraftLimit = overdraftLimit; this.overdraftLimit = overdraftLimit;