From 4a4cbf11be00b651d29937e774544282b73d5c09 Mon Sep 17 00:00:00 2001 From: rrpawar96 Date: Fri, 29 Oct 2021 16:24:40 +0530 Subject: [PATCH] FINERACT-1374:Withdrawal-Fee-Scheme --- .../portfolio/charge/data/ChargeData.java | 77 ++++++++++----- .../portfolio/charge/domain/Charge.java | 93 +++++++++++++++++- ...initionCommandFromApiJsonDeserializer.java | 57 +++++++++++ .../ChargeReadPlatformServiceImpl.java | 12 ++- ...WritePlatformServiceJpaRepositoryImpl.java | 5 +- .../data/SavingsAccountChargeData.java | 35 ++++++- .../savings/domain/SavingsAccount.java | 96 ++++++++++++++++++- .../savings/domain/SavingsAccountCharge.java | 43 +++++++++ ...sAccountChargeReadPlatformServiceImpl.java | 9 +- ...WritePlatformServiceJpaRepositoryImpl.java | 4 + .../V271_1__withdrawal_charges_discount.sql | 40 ++++++++ 11 files changed, 428 insertions(+), 43 deletions(-) create mode 100644 fineract-provider/src/main/resources/sql/migrations/core_db/V271_1__withdrawal_charges_discount.sql diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/data/ChargeData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/data/ChargeData.java index 684d88ef2..d070c3a5e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/data/ChargeData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/data/ChargeData.java @@ -30,6 +30,7 @@ import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.portfolio.charge.domain.ChargeTimeType; import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData; +import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData; import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData; import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountChargeData; import org.apache.fineract.portfolio.tax.data.TaxGroupData; @@ -43,6 +44,10 @@ public final class ChargeData implements Comparable, Serializable { private final String name; private final boolean active; private final boolean penalty; + private final boolean freeWithdrawal; + private final Integer freeWithdrawalChargeFrequency; + private final Integer restartFrequency; + private final Integer restartFrequencyEnum; private final CurrencyData currency; private final BigDecimal amount; private final EnumOptionData chargeTimeType; @@ -94,8 +99,8 @@ public final class ChargeData implements Comparable, Serializable { final GLAccountData account = null; final TaxGroupData taxGroupData = null; - return new ChargeData(null, null, null, null, null, null, null, null, false, false, taxGroupData, currencyOptions, - chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions, chargePaymentModeOptions, + return new ChargeData(null, null, null, null, null, null, null, null, false, false, false, null, null, null, taxGroupData, + currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions, chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, null, null, null, null, null, feeFrequencyOptions, account, incomeOrLiabilityAccountOptions, taxGroupOptions, shareChargeCalculationTypeOptions, @@ -104,7 +109,8 @@ public final class ChargeData implements Comparable, Serializable { public static ChargeData withTemplate(final ChargeData charge, final ChargeData template) { return new ChargeData(charge.id, charge.name, charge.amount, charge.currency, charge.chargeTimeType, charge.chargeAppliesTo, - charge.chargeCalculationType, charge.chargePaymentMode, charge.penalty, charge.active, charge.taxGroup, + charge.chargeCalculationType, charge.chargePaymentMode, charge.penalty, charge.active, charge.freeWithdrawal, + charge.freeWithdrawalChargeFrequency, charge.restartFrequency, charge.restartFrequencyEnum, charge.taxGroup, template.currencyOptions, template.chargeCalculationTypeOptions, template.chargeAppliesToOptions, template.chargeTimeTypeOptions, template.chargePaymetModeOptions, template.loanChargeCalculationTypeOptions, template.loanChargeTimeTypeOptions, template.savingsChargeCalculationTypeOptions, template.savingsChargeTimeTypeOptions, @@ -118,7 +124,8 @@ public final class ChargeData implements Comparable, Serializable { public static ChargeData instance(final Long id, final String name, final BigDecimal amount, final CurrencyData currency, final EnumOptionData chargeTimeType, final EnumOptionData chargeAppliesTo, final EnumOptionData chargeCalculationType, final EnumOptionData chargePaymentMode, final MonthDay feeOnMonthDay, final Integer feeInterval, final boolean penalty, - final boolean active, final BigDecimal minCap, final BigDecimal maxCap, final EnumOptionData feeFrequency, + final boolean active, final boolean freeWithdrawal, final Integer freeWithdrawalChargeFrequency, final Integer restartFrequency, + final Integer restartFrequencyEnum, final BigDecimal minCap, final BigDecimal maxCap, final EnumOptionData feeFrequency, final GLAccountData accountData, TaxGroupData taxGroupData) { final Collection currencyOptions = null; @@ -141,12 +148,13 @@ public final class ChargeData implements Comparable, Serializable { final List expenseAccountOptions = null; final List assetAccountOptions = null; return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode, - penalty, active, taxGroupData, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions, - chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, - savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, - clientChargeTimeTypeOptions, feeOnMonthDay, feeInterval, minCap, maxCap, feeFrequency, feeFrequencyOptions, accountData, - incomeOrLiabilityAccountOptions, taxGroupOptions, shareChargeCalculationTypeOptions, shareChargeTimeTypeOptions, - accountMappingForChargeConfig, expenseAccountOptions, assetAccountOptions); + penalty, active, freeWithdrawal, freeWithdrawalChargeFrequency, restartFrequency, restartFrequencyEnum, taxGroupData, + currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions, chargePaymentModeOptions, + loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions, + savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, feeOnMonthDay, feeInterval, + minCap, maxCap, feeFrequency, feeFrequencyOptions, accountData, incomeOrLiabilityAccountOptions, taxGroupOptions, + shareChargeCalculationTypeOptions, shareChargeTimeTypeOptions, accountMappingForChargeConfig, expenseAccountOptions, + assetAccountOptions); } public static ChargeData lookup(final Long id, final String name, final boolean isPenalty) { @@ -160,6 +168,12 @@ public final class ChargeData implements Comparable, Serializable { final Integer feeInterval = null; final Boolean penalty = isPenalty; final Boolean active = false; + final Boolean freeWithdrawal = false; + final Integer freeWithdrawalChargeFrequency = null; + final Integer restartFrequency = null; + final Integer restartFrequencyEnum = null; + final Boolean isPaymentType = false; + final PaymentTypeData paymentTypeOptions = null; final BigDecimal minCap = null; final BigDecimal maxCap = null; final Collection currencyOptions = null; @@ -186,25 +200,27 @@ public final class ChargeData implements Comparable, Serializable { final List assetAccountOptions = null; return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode, - penalty, active, taxGroupData, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions, - chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, - savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, - clientChargeTimeTypeOptions, feeOnMonthDay, feeInterval, minCap, maxCap, feeFrequency, feeFrequencyOptions, account, - incomeOrLiabilityAccountOptions, taxGroupOptions, shareChargeCalculationTypeOptions, shareChargeTimeTypeOptions, - accountMappingForChargeConfig, expenseAccountOptions, assetAccountOptions); + penalty, active, freeWithdrawal, freeWithdrawalChargeFrequency, restartFrequency, restartFrequencyEnum, taxGroupData, + currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions, chargePaymentModeOptions, + loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions, + savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, feeOnMonthDay, feeInterval, + minCap, maxCap, feeFrequency, feeFrequencyOptions, account, incomeOrLiabilityAccountOptions, taxGroupOptions, + shareChargeCalculationTypeOptions, shareChargeTimeTypeOptions, accountMappingForChargeConfig, expenseAccountOptions, + assetAccountOptions); } private ChargeData(final Long id, final String name, final BigDecimal amount, final CurrencyData currency, final EnumOptionData chargeTimeType, final EnumOptionData chargeAppliesTo, final EnumOptionData chargeCalculationType, - final EnumOptionData chargePaymentMode, final boolean penalty, final boolean active, final TaxGroupData taxGroupData, - final Collection currencyOptions, final List chargeCalculationTypeOptions, - final List chargeAppliesToOptions, final List chargeTimeTypeOptions, - final List chargePaymentModeOptions, final List loansChargeCalculationTypeOptions, - final List loansChargeTimeTypeOptions, final List savingsChargeCalculationTypeOptions, - final List savingsChargeTimeTypeOptions, final List clientChargeCalculationTypeOptions, - final List clientChargeTimeTypeOptions, final MonthDay feeOnMonthDay, final Integer feeInterval, - final BigDecimal minCap, final BigDecimal maxCap, final EnumOptionData feeFrequency, - final List feeFrequencyOptions, final GLAccountData account, + final EnumOptionData chargePaymentMode, final boolean penalty, final boolean active, final boolean freeWithdrawal, + final Integer freeWithdrawalChargeFrequency, final Integer restartFrequency, final Integer restartFrequencyEnum, + final TaxGroupData taxGroupData, final Collection currencyOptions, + final List chargeCalculationTypeOptions, final List chargeAppliesToOptions, + final List chargeTimeTypeOptions, final List chargePaymentModeOptions, + final List loansChargeCalculationTypeOptions, final List loansChargeTimeTypeOptions, + final List savingsChargeCalculationTypeOptions, final List savingsChargeTimeTypeOptions, + final List clientChargeCalculationTypeOptions, final List clientChargeTimeTypeOptions, + final MonthDay feeOnMonthDay, final Integer feeInterval, final BigDecimal minCap, final BigDecimal maxCap, + final EnumOptionData feeFrequency, final List feeFrequencyOptions, final GLAccountData account, final Map> incomeOrLiabilityAccountOptions, final Collection taxGroupOptions, final List shareChargeCalculationTypeOptions, final List shareChargeTimeTypeOptions, final String accountMappingForChargeConfig, final List expenseAccountOptions, @@ -221,6 +237,10 @@ public final class ChargeData implements Comparable, Serializable { this.feeOnMonthDay = feeOnMonthDay; this.penalty = penalty; this.active = active; + this.freeWithdrawal = freeWithdrawal; + this.freeWithdrawalChargeFrequency = freeWithdrawalChargeFrequency; + this.restartFrequency = restartFrequency; + this.restartFrequencyEnum = restartFrequencyEnum; this.minCap = minCap; this.maxCap = maxCap; this.currencyOptions = currencyOptions; @@ -294,12 +314,17 @@ public final class ChargeData implements Comparable, Serializable { final Collection chargeOptions = null; final LocalDate dueAsOfDate = null; final Boolean isActive = null; + final Boolean isFreeWithdrawal = null; + final Integer freeWithdrawalChargeFrequency = null; + final Integer restartFrequency = null; + final Integer restartFrequencyEnum = null; + final LocalDate inactivationDate = null; return SavingsAccountChargeData.instance(savingsChargeId, this.id, savingsAccountId, this.name, this.currency, this.amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding, this.chargeTimeType, dueAsOfDate, this.chargeCalculationType, percentage, amountPercentageAppliedTo, chargeOptions, this.penalty, this.feeOnMonthDay, this.feeInterval, isActive, - inactivationDate); + isFreeWithdrawal, freeWithdrawalChargeFrequency, restartFrequency, restartFrequencyEnum, inactivationDate); } public ShareAccountChargeData toShareAccountChargeData() { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/domain/Charge.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/domain/Charge.java index 20788025d..670db812a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/domain/Charge.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/domain/Charge.java @@ -48,6 +48,7 @@ import org.apache.fineract.portfolio.charge.exception.ChargeDueAtDisbursementCan import org.apache.fineract.portfolio.charge.exception.ChargeMustBePenaltyException; import org.apache.fineract.portfolio.charge.exception.ChargeParameterUpdateNotSupportedException; import org.apache.fineract.portfolio.charge.service.ChargeEnumerations; +import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; import org.apache.fineract.portfolio.tax.data.TaxGroupData; import org.apache.fineract.portfolio.tax.domain.TaxGroup; @@ -103,6 +104,18 @@ public class Charge extends AbstractPersistableCustom { @Column(name = "fee_frequency", nullable = true) private Integer feeFrequency; + @Column(name = "is_free_withdrawal", nullable = false) + private boolean enableFreeWithdrawal; + + @Column(name = "free_withdrawal_charge_frequency", nullable = true) + private Integer freeWithdrawalFrequency; + + @Column(name = "restart_frequency", nullable = true) + private Integer restartFrequency; + + @Column(name = "restart_frequency_enum", nullable = true) + private Integer restartFrequencyEnum; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "income_or_liability_account_id") private GLAccount account; @@ -112,6 +125,7 @@ public class Charge extends AbstractPersistableCustom { private TaxGroup taxGroup; public static Charge fromJson(final JsonCommand command, final GLAccount account, final TaxGroup taxGroup) { + final String name = command.stringValueOfParameterNamed("name"); final BigDecimal amount = command.bigDecimalValueOfParameterNamed("amount"); final String currencyCode = command.stringValueOfParameterNamed("currencyCode"); @@ -132,8 +146,23 @@ public class Charge extends AbstractPersistableCustom { final BigDecimal maxCap = command.bigDecimalValueOfParameterNamed("maxCap"); final Integer feeFrequency = command.integerValueOfParameterNamed("feeFrequency"); + boolean enableFreeWithdrawalCharge = false; + enableFreeWithdrawalCharge = command.booleanPrimitiveValueOfParameterNamed("enableFreeWithdrawalCharge"); + + Integer freeWithdrawalFrequency = null; + Integer restartCountFrequency = null; + PeriodFrequencyType countFrequencyType = null; + + if (enableFreeWithdrawalCharge) { + freeWithdrawalFrequency = command.integerValueOfParameterNamed("freeWithdrawalFrequency"); + restartCountFrequency = command.integerValueOfParameterNamed("restartCountFrequency"); + + countFrequencyType = PeriodFrequencyType.fromInt(command.integerValueOfParameterNamed("countFrequencyType")); + } + return new Charge(name, amount, currencyCode, chargeAppliesTo, chargeTimeType, chargeCalculationType, penalty, active, paymentMode, - feeOnMonthDay, feeInterval, minCap, maxCap, feeFrequency, account, taxGroup); + feeOnMonthDay, feeInterval, minCap, maxCap, feeFrequency, enableFreeWithdrawalCharge, freeWithdrawalFrequency, + restartCountFrequency, countFrequencyType, account, taxGroup); } protected Charge() {} @@ -141,7 +170,9 @@ public class Charge extends AbstractPersistableCustom { private Charge(final String name, final BigDecimal amount, final String currencyCode, final ChargeAppliesTo chargeAppliesTo, final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculationType, final boolean penalty, final boolean active, final ChargePaymentMode paymentMode, final MonthDay feeOnMonthDay, final Integer feeInterval, final BigDecimal minCap, - final BigDecimal maxCap, final Integer feeFrequency, final GLAccount account, final TaxGroup taxGroup) { + final BigDecimal maxCap, final Integer feeFrequency, final boolean enableFreeWithdrawalCharge, + final Integer freeWithdrawalFrequency, final Integer restartFrequency, final PeriodFrequencyType restartFrequencyEnum, + final GLAccount account, final TaxGroup taxGroup) { this.name = name; this.amount = amount; this.currencyCode = currencyCode; @@ -186,6 +217,13 @@ public class Charge extends AbstractPersistableCustom { "savings.charge.calculation.type.percentage.allowed.only.for.withdrawal.or.NoActivity"); } + if (enableFreeWithdrawalCharge) { + this.enableFreeWithdrawal = enableFreeWithdrawalCharge; + this.freeWithdrawalFrequency = freeWithdrawalFrequency; + this.restartFrequency = restartFrequency; + this.restartFrequencyEnum = restartFrequencyEnum.getValue(); + } + } else if (isLoanCharge()) { if (penalty && (chargeTime.isTimeOfDisbursement() || chargeTime.isTrancheDisbursement())) { @@ -201,6 +239,7 @@ public class Charge extends AbstractPersistableCustom { .failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.time.for.loan"); } } + if (isPercentageOfApprovedAmount()) { this.minCap = minCap; this.maxCap = maxCap; @@ -291,6 +330,22 @@ public class Charge extends AbstractPersistableCustom { return this.maxCap; } + public boolean isEnableFreeWithdrawal() { + return this.enableFreeWithdrawal; + } + + public Integer getFrequencyFreeWithdrawalCharge() { + return this.freeWithdrawalFrequency; + } + + public Integer getRestartFrequency() { + return this.restartFrequency; + } + + public Integer getRestartFrequencyEnum() { + return this.restartFrequencyEnum; + } + public Map update(final JsonCommand command) { final Map actualChanges = new LinkedHashMap<>(7); @@ -355,6 +410,35 @@ public class Charge extends AbstractPersistableCustom { } } + final String freeWithdrawalFrequencyParamName = "freeWithdrawalFrequency"; + if (command.isChangeInIntegerParameterNamed(freeWithdrawalFrequencyParamName, this.freeWithdrawalFrequency)) { + final Integer enableFreeWithdrawalChargeNewValue = command.integerValueOfParameterNamed(freeWithdrawalFrequencyParamName); + actualChanges.put(freeWithdrawalFrequencyParamName, enableFreeWithdrawalChargeNewValue); + this.freeWithdrawalFrequency = enableFreeWithdrawalChargeNewValue; + } + + final String restartCountFrequencyParamName = "restartCountFrequency"; + if (command.isChangeInIntegerParameterNamed(restartCountFrequencyParamName, this.restartFrequency)) { + final Integer restartCountFrequencyNewValue = command.integerValueOfParameterNamed(restartCountFrequencyParamName); + actualChanges.put(restartCountFrequencyParamName, restartCountFrequencyNewValue); + this.restartFrequency = restartCountFrequencyNewValue; + } + + final String countFrequencyTypeParamName = "countFrequencyType"; + if (command.isChangeInIntegerParameterNamed(countFrequencyTypeParamName, this.restartFrequencyEnum)) { + final Integer countFrequencyTypeNewValue = command.integerValueOfParameterNamed(countFrequencyTypeParamName); + actualChanges.put(countFrequencyTypeParamName, countFrequencyTypeNewValue); + this.restartFrequencyEnum = ChargeTimeType.fromInt(countFrequencyTypeNewValue).getValue(); + } + + final String enableFreeWithdrawalChargeParamName = "enableFreeWithdrawalCharge"; + if (command.isChangeInBooleanParameterNamed(enableFreeWithdrawalChargeParamName, this.enableFreeWithdrawal)) { + final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(enableFreeWithdrawalChargeParamName); + actualChanges.put(enableFreeWithdrawalChargeParamName, newValue); + this.enableFreeWithdrawal = newValue; + + } + final String chargeAppliesToParamName = "chargeAppliesTo"; if (command.isChangeInIntegerParameterNamed(chargeAppliesToParamName, this.chargeAppliesTo)) { /* @@ -531,8 +615,9 @@ public class Charge extends AbstractPersistableCustom { final CurrencyData currency = new CurrencyData(this.currencyCode, null, 0, 0, null, null); return ChargeData.instance(getId(), this.name, this.amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, - chargePaymentmode, getFeeOnMonthDay(), this.feeInterval, this.penalty, this.active, this.minCap, this.maxCap, - feeFrequencyType, accountData, taxGroupData); + chargePaymentmode, getFeeOnMonthDay(), this.feeInterval, this.penalty, this.active, this.enableFreeWithdrawal, + this.freeWithdrawalFrequency, this.restartFrequency, this.restartFrequencyEnum, this.minCap, this.maxCap, feeFrequencyType, + accountData, taxGroupData); } public Integer getChargePaymentMode() { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/serialization/ChargeDefinitionCommandFromApiJsonDeserializer.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/serialization/ChargeDefinitionCommandFromApiJsonDeserializer.java index 1fa940a80..e5d35e863 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/serialization/ChargeDefinitionCommandFromApiJsonDeserializer.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/serialization/ChargeDefinitionCommandFromApiJsonDeserializer.java @@ -54,6 +54,7 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer { private final Set supportedParameters = new HashSet<>(Arrays.asList("name", "amount", "locale", "currencyCode", "currencyOptions", "chargeAppliesTo", "chargeTimeType", "chargeCalculationType", "chargeCalculationTypeOptions", "penalty", "active", "chargePaymentMode", "feeOnMonthDay", "feeInterval", "monthDayFormat", "minCap", "maxCap", "feeFrequency", + "enableFreeWithdrawalCharge", "freeWithdrawalFrequency", "restartCountFrequency", "countFrequencyType", ChargesApiConstants.glAccountIdParamName, ChargesApiConstants.taxGroupIdParamName)); private final FromJsonHelper fromApiJsonHelper; @@ -91,6 +92,28 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer { final Integer feeFrequency = this.fromApiJsonHelper.extractIntegerNamed("feeFrequency", element, Locale.getDefault()); baseDataValidator.reset().parameter("feeFrequency").value(feeFrequency).inMinMaxRange(0, 3); + if (this.fromApiJsonHelper.parameterExists("enableFreeWithdrawalCharge", element)) { + + final Boolean enableFreeWithdrawalCharge = this.fromApiJsonHelper.extractBooleanNamed("enableFreeWithdrawalCharge", element); + baseDataValidator.reset().parameter("enableFreeWithdrawalCharge").value(enableFreeWithdrawalCharge).notNull(); + + if (enableFreeWithdrawalCharge) { + + final Integer freeWithdrawalFrequency = this.fromApiJsonHelper.extractIntegerNamed("freeWithdrawalFrequency", element, + Locale.getDefault()); + baseDataValidator.reset().parameter("freeWithdrawalFrequency").value(freeWithdrawalFrequency).integerGreaterThanZero(); + + final Integer restartCountFrequency = this.fromApiJsonHelper.extractIntegerNamed("restartCountFrequency", element, + Locale.getDefault()); + baseDataValidator.reset().parameter("restartCountFrequency").value(restartCountFrequency).integerGreaterThanZero(); + + final Integer countFrequencyType = this.fromApiJsonHelper.extractIntegerNamed("countFrequencyType", element, + Locale.getDefault()); + baseDataValidator.reset().parameter("countFrequencyType").value(countFrequencyType); + + } + } + if (feeFrequency != null) { baseDataValidator.reset().parameter("feeInterval").value(feeInterval).notNull(); } @@ -155,6 +178,7 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer { baseDataValidator.reset().parameter("chargeCalculationType").value(chargeCalculationType) .isOneOfTheseValues(ChargeCalculationType.validValuesForSavings()); } + } else if (appliesTo.isClientCharge()) { // client applicable validation final Integer chargeTimeType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed("chargeTimeType", element); @@ -277,6 +301,39 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer { .isOneOfTheseValues(ChargeAppliesTo.validValues()); } + Boolean enableFreeWithdrawalCharge = false; + if (this.fromApiJsonHelper.parameterExists("enableFreeWithdrawalCharge", element)) { + enableFreeWithdrawalCharge = this.fromApiJsonHelper.extractBooleanNamed("enableFreeWithdrawalCharge", element); + baseDataValidator.reset().parameter("enableFreeWithdrawalCharge").value(enableFreeWithdrawalCharge).notNull(); + + if (enableFreeWithdrawalCharge) { + + final Integer freeWithdrawalFrequency = this.fromApiJsonHelper.extractIntegerNamed("freeWithdrawalFrequency", element, + Locale.getDefault()); + baseDataValidator.reset().parameter("freeWithdrawalFrequency").value(freeWithdrawalFrequency).integerGreaterThanZero(); + + final Integer restartCountFrequency = this.fromApiJsonHelper.extractIntegerNamed("restartCountFrequency", element, + Locale.getDefault()); + baseDataValidator.reset().parameter("restartCountFrequency").value(restartCountFrequency).integerGreaterThanZero(); + + final Integer countFrequencyType = this.fromApiJsonHelper.extractIntegerNamed("countFrequencyType", element, + Locale.getDefault()); + baseDataValidator.reset().parameter("countFrequencyType").value(countFrequencyType); + } + } + + if (this.fromApiJsonHelper.parameterExists("chargeAppliesTo", element)) { + final Integer chargeAppliesTo = this.fromApiJsonHelper.extractIntegerSansLocaleNamed("chargeAppliesTo", element); + baseDataValidator.reset().parameter("chargeAppliesTo").value(chargeAppliesTo).notNull() + .isOneOfTheseValues(ChargeAppliesTo.validValues()); + } + + if (this.fromApiJsonHelper.parameterExists("chargeAppliesTo", element)) { + final Integer chargeAppliesTo = this.fromApiJsonHelper.extractIntegerSansLocaleNamed("chargeAppliesTo", element); + baseDataValidator.reset().parameter("chargeAppliesTo").value(chargeAppliesTo).notNull() + .isOneOfTheseValues(ChargeAppliesTo.validValues()); + } + if (this.fromApiJsonHelper.parameterExists("chargeTimeType", element)) { final Integer chargeTimeType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed("chargeTimeType", element); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformServiceImpl.java index 9bf07033a..26f46eab5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformServiceImpl.java @@ -287,7 +287,8 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService + "c.charge_applies_to_enum as chargeAppliesTo, c.charge_time_enum as chargeTime, " + "c.charge_payment_mode_enum as chargePaymentMode, " + "c.charge_calculation_enum as chargeCalculation, c.is_penalty as penalty, " - + "c.is_active as active, oc.name as currencyName, oc.decimal_places as currencyDecimalPlaces, " + + "c.is_active as active, c.is_free_withdrawal as isFreeWithdrawal, c.free_withdrawal_charge_frequency as freeWithdrawalChargeFrequency, c.restart_frequency as restartFrequency, c.restart_frequency_enum as restartFrequencyEnum," + + "oc.name as currencyName, oc.decimal_places as currencyDecimalPlaces, " + "oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, " + "oc.internationalized_name_code as currencyNameCode, c.fee_on_day as feeOnDay, c.fee_on_month as feeOnMonth, " + "c.fee_interval as feeInterval, c.fee_frequency as feeFrequency,c.min_cap as minCap,c.max_cap as maxCap, " @@ -372,9 +373,14 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService taxGroupData = TaxGroupData.lookup(taxGroupId, taxGroupName); } + final boolean isFreeWithdrawal = rs.getBoolean("isFreeWithdrawal"); + final int freeWithdrawalChargeFrequency = rs.getInt("freeWithdrawalChargeFrequency"); + final int restartFrequency = rs.getInt("restartFrequency"); + final int restartFrequencyEnum = rs.getInt("restartFrequencyEnum"); + return ChargeData.instance(id, name, amount, currency, chargeTimeType, chargeAppliesToType, chargeCalculationType, - chargePaymentMode, feeOnMonthDay, feeInterval, penalty, active, minCap, maxCap, feeFrequencyType, glAccountData, - taxGroupData); + chargePaymentMode, feeOnMonthDay, feeInterval, penalty, active, isFreeWithdrawal, freeWithdrawalChargeFrequency, + restartFrequency, restartFrequencyEnum, minCap, maxCap, feeFrequencyType, glAccountData, taxGroupData); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeWritePlatformServiceJpaRepositoryImpl.java index 9a611dec5..22959c044 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeWritePlatformServiceJpaRepositoryImpl.java @@ -42,6 +42,7 @@ import org.apache.fineract.portfolio.charge.exception.ChargeNotFoundException; import org.apache.fineract.portfolio.charge.serialization.ChargeDefinitionCommandFromApiJsonDeserializer; import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository; +import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepositoryWrapper; import org.apache.fineract.portfolio.tax.domain.TaxGroup; import org.apache.fineract.portfolio.tax.domain.TaxGroupRepositoryWrapper; import org.slf4j.Logger; @@ -67,13 +68,14 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP private final FineractEntityAccessUtil fineractEntityAccessUtil; private final GLAccountRepositoryWrapper glAccountRepository; private final TaxGroupRepositoryWrapper taxGroupRepository; + private final PaymentTypeRepositoryWrapper paymentTyperepositoryWrapper; @Autowired public ChargeWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final ChargeDefinitionCommandFromApiJsonDeserializer fromApiJsonDeserializer, final ChargeRepository chargeRepository, final LoanProductRepository loanProductRepository, final RoutingDataSource dataSource, final FineractEntityAccessUtil fineractEntityAccessUtil, final GLAccountRepositoryWrapper glAccountRepository, - final TaxGroupRepositoryWrapper taxGroupRepository) { + final TaxGroupRepositoryWrapper taxGroupRepository, final PaymentTypeRepositoryWrapper paymentTyperepositoryWrapper) { this.context = context; this.fromApiJsonDeserializer = fromApiJsonDeserializer; this.dataSource = dataSource; @@ -83,6 +85,7 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP this.fineractEntityAccessUtil = fineractEntityAccessUtil; this.glAccountRepository = glAccountRepository; this.taxGroupRepository = taxGroupRepository; + this.paymentTyperepositoryWrapper = paymentTyperepositoryWrapper; } @Transactional diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountChargeData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountChargeData.java index 4ea79eb6a..715f3f462 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountChargeData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountChargeData.java @@ -74,6 +74,14 @@ public class SavingsAccountChargeData implements Serializable { private final Boolean isActive; + private final Boolean isFreeWithdrawal; + + private final Integer freeWithdrawalChargeFrequency; + + private final Integer restartFrequency; + + private final Integer restartFrequencyEnum; + private final LocalDate inactivationDate; private final Collection chargeOptions; @@ -101,6 +109,10 @@ public class SavingsAccountChargeData implements Serializable { this.isActive = null; this.inactivationDate = null; this.chargeOptions = null; + this.isFreeWithdrawal = null; + this.freeWithdrawalChargeFrequency = null; + this.restartFrequency = null; + this.restartFrequencyEnum = null; } public static SavingsAccountChargeData template(final Collection chargeOptions) { @@ -123,11 +135,17 @@ public class SavingsAccountChargeData implements Serializable { final MonthDay feeOnMonthDay = null; final Integer feeInterval = null; final Boolean isActive = null; + final Boolean isFreeWithdrawal = null; + final Integer freeWithdrawalChargeFrequency = null; + final Integer restartFrequency = null; + final Integer restartFrequencyEnum = null; + final LocalDate inactivationDate = null; return new SavingsAccountChargeData(id, chargeId, accountId, name, chargeTimeType, dueAsOfDate, chargeCalculationType, percentage, amountPercentageAppliedTo, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding, chargeOptions, - penalty, feeOnMonthDay, feeInterval, isActive, inactivationDate); + penalty, feeOnMonthDay, feeInterval, isActive, isFreeWithdrawal, freeWithdrawalChargeFrequency, restartFrequency, + restartFrequencyEnum, inactivationDate); } public static SavingsAccountChargeData instance(final Long id, final Long chargeId, final Long accountId, final String name, @@ -135,11 +153,14 @@ public class SavingsAccountChargeData implements Serializable { final BigDecimal amountWrittenOff, final BigDecimal amountOutstanding, final EnumOptionData chargeTimeType, final LocalDate dueAsOfDate, final EnumOptionData chargeCalculationType, final BigDecimal percentage, final BigDecimal amountPercentageAppliedTo, final Collection chargeOptions, final boolean penalty, - final MonthDay feeOnMonthDay, final Integer feeInterval, final Boolean isActive, final LocalDate inactivationDate) { + final MonthDay feeOnMonthDay, final Integer feeInterval, final Boolean isActive, final Boolean isFreeWithdrawal, + final Integer freeWithdrawalChargeFrequency, final Integer restartFrequency, final Integer restartFrequencyEnum, + final LocalDate inactivationDate) { return new SavingsAccountChargeData(id, chargeId, accountId, name, chargeTimeType, dueAsOfDate, chargeCalculationType, percentage, amountPercentageAppliedTo, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding, chargeOptions, - penalty, feeOnMonthDay, feeInterval, isActive, inactivationDate); + penalty, feeOnMonthDay, feeInterval, isActive, isFreeWithdrawal, freeWithdrawalChargeFrequency, restartFrequency, + restartFrequencyEnum, inactivationDate); } private SavingsAccountChargeData(final Long id, final Long chargeId, final Long accountId, final String name, @@ -147,7 +168,9 @@ public class SavingsAccountChargeData implements Serializable { final BigDecimal percentage, final BigDecimal amountPercentageAppliedTo, final CurrencyData currency, final BigDecimal amount, final BigDecimal amountPaid, final BigDecimal amountWaived, final BigDecimal amountWrittenOff, final BigDecimal amountOutstanding, final Collection chargeOptions, final boolean penalty, - final MonthDay feeOnMonthDay, final Integer feeInterval, final Boolean isActive, final LocalDate inactivationDate) { + final MonthDay feeOnMonthDay, final Integer feeInterval, final Boolean isActive, final Boolean isFreeWithdrawal, + final Integer freeWithdrawalChargeFrequency, final Integer restartFrequency, final Integer restartFrequencyEnum, + final LocalDate inactivationDate) { this.id = id; this.chargeId = chargeId; this.accountId = accountId; @@ -170,6 +193,10 @@ public class SavingsAccountChargeData implements Serializable { this.feeInterval = feeInterval; this.isActive = isActive; this.inactivationDate = inactivationDate; + this.isFreeWithdrawal = isFreeWithdrawal; + this.freeWithdrawalChargeFrequency = freeWithdrawalChargeFrequency; + this.restartFrequency = restartFrequency; + this.restartFrequencyEnum = restartFrequencyEnum; } private BigDecimal getAmountOrPercentage() { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java index 3ffbc25d4..bc0301e50 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java @@ -36,6 +36,7 @@ import com.google.gson.JsonArray; import java.math.BigDecimal; import java.math.MathContext; import java.time.LocalDate; +import java.time.YearMonth; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.ArrayList; @@ -50,6 +51,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.DiscriminatorColumn; @@ -92,6 +94,7 @@ import org.apache.fineract.portfolio.charge.exception.SavingsAccountChargeNotFou import org.apache.fineract.portfolio.client.domain.Client; import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; import org.apache.fineract.portfolio.group.domain.Group; +import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail; import org.apache.fineract.portfolio.savings.DepositAccountType; import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType; import org.apache.fineract.portfolio.savings.SavingsApiConstants; @@ -1038,7 +1041,8 @@ public class SavingsAccount extends AbstractPersistableCustom { if (applyWithdrawFee) { // auto pay withdrawal fee - payWithdrawalFee(transactionDTO.getTransactionAmount(), transactionDTO.getTransactionDate(), transactionDTO.getAppUser()); + payWithdrawalFee(transactionDTO.getTransactionAmount(), transactionDTO.getTransactionDate(), transactionDTO.getAppUser(), + transactionDTO.getPaymentDetail()); } final Money transactionAmountMoney = Money.of(this.currency, transactionDTO.getTransactionAmount()); @@ -1066,15 +1070,99 @@ public class SavingsAccount extends AbstractPersistableCustom { return result; } - private void payWithdrawalFee(final BigDecimal transactionAmount, final LocalDate transactionDate, final AppUser user) { + private void payWithdrawalFee(final BigDecimal transactionAmount, final LocalDate transactionDate, final AppUser user, + final PaymentDetail paymentDetail) { + for (SavingsAccountCharge charge : this.charges()) { + if (charge.isWithdrawalFee() && charge.isActive()) { - charge.updateWithdralFeeAmount(transactionAmount); - this.payCharge(charge, charge.getAmountOutstanding(this.getCurrency()), transactionDate, user); + if (charge.getFreeWithdrawalCount() == null) { + charge.setFreeWithdrawalCount(0); + } + + if (charge.isEnableFreeWithdrawal()) { + resetFreeChargeDaysCount(charge, transactionAmount, transactionDate, user); + + } else { + charge.updateWithdralFeeAmount(transactionAmount); + this.payCharge(charge, charge.getAmountOutstanding(this.getCurrency()), transactionDate, user); + } + } + + } + } + + private void resetFreeChargeDaysCount(SavingsAccountCharge charge, final BigDecimal transactionAmount, final LocalDate transactionDate, + final AppUser user) { + Date resetDate = charge.getResetChargeDate(); + + Integer restartPeriod = charge.getRestartFrequency(); + if (charge.getRestartFrequencyEnum() == 2) { // calculation for months + Date currentDate = new Date(); + LocalDate localDate = currentDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + Integer currentMonth = localDate.getMonthValue(); + + LocalDate resetLocalDate = null; + if (resetDate == null) { + resetLocalDate = this.activatedOnDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + } else { + resetLocalDate = resetDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + } + Integer resetMonth = resetLocalDate.getMonthValue(); + + LocalDate gapIntervalMonth = resetLocalDate.plusMonths(restartPeriod); + Integer gapMonth = gapIntervalMonth.getMonthValue(); + + Integer resetYear = resetLocalDate.getYear(); + Integer currentYear = localDate.getYear(); + + YearMonth gapYearMonth = YearMonth.from(gapIntervalMonth); + YearMonth localYearMonth = YearMonth.from(localDate); + if (localYearMonth.isBefore(gapYearMonth)) { + countValidation(charge, transactionAmount, transactionDate, user); + } else { + discountCharge(1, charge); + } + } else { // calculation for days + Long completedDays = null; + Long days = null; + + if (resetDate == null) { + completedDays = DateUtils.getDateOfTenant().getTime() - this.activatedOnDate.getTime(); + days = TimeUnit.DAYS.convert(completedDays, TimeUnit.MILLISECONDS); + } else { + completedDays = DateUtils.getDateOfTenant().getTime() - resetDate.getTime(); + days = TimeUnit.DAYS.convert(completedDays, TimeUnit.MILLISECONDS); + } + + Integer totalDays = days.intValue(); + + if (totalDays < restartPeriod) { + countValidation(charge, transactionAmount, transactionDate, user); + } else { + discountCharge(1, charge); } } } + private void countValidation(SavingsAccountCharge charge, final BigDecimal transactionAmount, final LocalDate transactionDate, + final AppUser user) { + if (charge.getFreeWithdrawalCount() < charge.getFrequencyFreeWithdrawalCharge()) { + final Integer count = charge.getFreeWithdrawalCount() + 1; + charge.setFreeWithdrawalCount(count); + charge.updateNoWithdrawalFee(); + } else { + charge.updateWithdralFeeAmount(transactionAmount); + this.payCharge(charge, charge.getAmountOutstanding(this.getCurrency()), transactionDate, user); + } + } + + private void discountCharge(Integer freeWithdrawalCount, SavingsAccountCharge charge) { + charge.setFreeWithdrawalCount(freeWithdrawalCount); + charge.setDiscountDueDate(DateUtils.getDateOfTenant()); + charge.updateNoWithdrawalFee(); + } + public boolean isBeforeLastPostingPeriod(final LocalDate transactionDate) { boolean transactionBeforeLastInterestPosting = false; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountCharge.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountCharge.java index 0ae6e4b26..f0e840197 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountCharge.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountCharge.java @@ -94,6 +94,13 @@ public class SavingsAccountCharge extends AbstractPersistableCustom { @Column(name = "charge_calculation_enum") private Integer chargeCalculation; + @Column(name = "free_withdrawal_count", nullable = true) + private Integer freeWithdrawalCount; + + @Temporal(TemporalType.DATE) + @Column(name = "charge_reset_date", nullable = true) + private Date chargeResetDate; + @Column(name = "calculation_percentage", scale = 6, precision = 19, nullable = true) private BigDecimal percentage; @@ -666,6 +673,38 @@ public class SavingsAccountCharge extends AbstractPersistableCustom { return this.charge; } + public boolean isEnableFreeWithdrawal() { + return charge.isEnableFreeWithdrawal(); + } + + public Integer getFrequencyFreeWithdrawalCharge() { // number of times free withdrawal allowed + return charge.getFrequencyFreeWithdrawalCharge(); + } + + public Integer getRestartFrequency() { // numeric value of which numeric-period, count should restart + return charge.getRestartFrequency(); + } + + public Integer getRestartFrequencyEnum() { // enum day/week/month for restarting the count. + return charge.getRestartFrequencyEnum(); + } + + public Integer getFreeWithdrawalCount() { + return freeWithdrawalCount; + } + + public void setFreeWithdrawalCount(Integer freeWithdrawalCount) { + this.freeWithdrawalCount = freeWithdrawalCount; + } + + public Date getResetChargeDate() { + return chargeResetDate; + } + + public void setDiscountDueDate(final Date date) { + this.chargeResetDate = date; + } + public SavingsAccount savingsAccount() { return this.savingsAccount; } @@ -758,6 +797,10 @@ public class SavingsAccountCharge extends AbstractPersistableCustom { return amountOutstanding = calculateWithdralFeeAmount(transactionAmount); } + public BigDecimal updateNoWithdrawalFee() { + return amountOutstanding = BigDecimal.ZERO; + } + public void updateToNextDueDateFrom(final LocalDate startingDate) { if (isAnnualFee() || isMonthlyFee() || isWeeklyFee()) { this.dueDate = Date.from(getNextDueDateFrom(startingDate).atStartOfDay(ZoneId.systemDefault()).toInstant()); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountChargeReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountChargeReadPlatformServiceImpl.java index 9416d743b..0d68fc0ed 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountChargeReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountChargeReadPlatformServiceImpl.java @@ -82,6 +82,7 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou + "sc.fee_on_month as feeOnMonth, " + "sc.fee_on_day as feeOnDay, sc.fee_interval as feeInterval, " + "sc.charge_calculation_enum as chargeCalculation, " + "sc.is_active as isActive, sc.inactivated_on_date as inactivationDate, " + + "c.is_free_withdrawal as isFreeWithdrawal, c.free_withdrawal_charge_frequency as freeWithdrawalChargeFrequency, c.restart_frequency as restartFrequency, c.restart_frequency_enum as restartFrequencyEnum, " + "c.currency_code as currencyCode, oc.name as currencyName, " + "oc.decimal_places as currencyDecimalPlaces, oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, " + "oc.internationalized_name_code as currencyNameCode from m_charge c " @@ -133,11 +134,17 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou final Boolean isActive = rs.getBoolean("isActive"); final LocalDate inactivationDate = JdbcSupport.getLocalDate(rs, "inactivationDate"); + final Boolean isFreeWithdrawal = rs.getBoolean("isFreeWithdrawal"); + final Integer freeWithdrawalChargeFrequency = JdbcSupport.getInteger(rs, "freeWithdrawalChargeFrequency"); + final Integer restartFrequency = JdbcSupport.getInteger(rs, "restartFrequency"); + final Integer restartFrequencyEnum = JdbcSupport.getInteger(rs, "restartFrequencyEnum"); + final Collection chargeOptions = null; return SavingsAccountChargeData.instance(id, chargeId, accountId, name, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding, chargeTimeType, dueAsOfDate, chargeCalculationType, percentageOf, - amountPercentageAppliedTo, chargeOptions, penalty, feeOnMonthDay, feeInterval, isActive, inactivationDate); + amountPercentageAppliedTo, chargeOptions, penalty, feeOnMonthDay, feeInterval, isActive, isFreeWithdrawal, + freeWithdrawalChargeFrequency, restartFrequency, restartFrequencyEnum, inactivationDate); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java index b3f0b1f9c..e03b897f0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java @@ -1025,6 +1025,10 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi } final SavingsAccountCharge savingsAccountCharge = SavingsAccountCharge.createNewFromJson(savingsAccount, chargeDefinition, command); + if (chargeDefinition.isEnableFreeWithdrawal()) { + savingsAccountCharge.setFreeWithdrawalCount(0); + } + if (savingsAccountCharge.getDueLocalDate() != null) { // transaction date should not be on a holiday or non working day if (!this.configurationDomainService.allowTransactionsOnHolidayEnabled() diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V271_1__withdrawal_charges_discount.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V271_1__withdrawal_charges_discount.sql new file mode 100644 index 000000000..76c0db17a --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V271_1__withdrawal_charges_discount.sql @@ -0,0 +1,40 @@ +-- +-- 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. +-- + + +ALTER TABLE `m_charge` ADD COLUMN `is_free_withdrawal` INT NOT NULL DEFAULT '0' AFTER `fee_frequency`; + + +ALTER TABLE `m_charge` + ADD COLUMN `free_withdrawal_charge_frequency` INT NULL DEFAULT '0' AFTER `is_free_withdrawal`; + + +ALTER TABLE `m_charge` + ADD COLUMN `restart_frequency` INT NULL DEFAULT '0' AFTER `free_withdrawal_charge_frequency`; + + +ALTER TABLE `m_charge` + ADD COLUMN `restart_frequency_enum` INT NULL DEFAULT '0' AFTER `restart_frequency`; + + +ALTER TABLE `m_savings_account_charge` + ADD COLUMN `free_withdrawal_count` INT NULL DEFAULT '0' AFTER `fee_interval`; + +ALTER TABLE `m_savings_account_charge` + ADD COLUMN `charge_reset_date` DATE NULL DEFAULT NULL AFTER `free_withdrawal_count`;