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 d070c3a5e..1b4c47a33 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 @@ -48,6 +48,8 @@ public final class ChargeData implements Comparable, Serializable { private final Integer freeWithdrawalChargeFrequency; private final Integer restartFrequency; private final Integer restartFrequencyEnum; + private final boolean isPaymentType; + private final PaymentTypeData paymentTypeOptions; private final CurrencyData currency; private final BigDecimal amount; private final EnumOptionData chargeTimeType; @@ -99,34 +101,36 @@ 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, 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, - shareChargeTimeTypeOptions, accountMappingForChargeConfig, expenseAccountOptions, assetAccountOptions); + return new ChargeData(null, null, null, null, null, null, null, null, false, false, false, null, null, null, false, null, + taxGroupData, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions, + chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, + savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, + clientChargeTimeTypeOptions, null, null, null, null, null, feeFrequencyOptions, account, incomeOrLiabilityAccountOptions, + taxGroupOptions, shareChargeCalculationTypeOptions, shareChargeTimeTypeOptions, accountMappingForChargeConfig, + expenseAccountOptions, assetAccountOptions); } 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.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, - template.clientChargeCalculationTypeOptions, template.clientChargeTimeTypeOptions, charge.feeOnMonthDay, charge.feeInterval, - charge.minCap, charge.maxCap, charge.feeFrequency, template.feeFrequencyOptions, charge.incomeOrLiabilityAccount, - template.incomeOrLiabilityAccountOptions, template.taxGroupOptions, template.shareChargeCalculationTypeOptions, - template.shareChargeTimeTypeOptions, template.accountMappingForChargeConfig, template.expenseAccountOptions, - template.assetAccountOptions); + charge.freeWithdrawalChargeFrequency, charge.restartFrequency, charge.restartFrequencyEnum, charge.isPaymentType, + charge.paymentTypeOptions, charge.taxGroup, template.currencyOptions, template.chargeCalculationTypeOptions, + template.chargeAppliesToOptions, template.chargeTimeTypeOptions, template.chargePaymetModeOptions, + template.loanChargeCalculationTypeOptions, template.loanChargeTimeTypeOptions, template.savingsChargeCalculationTypeOptions, + template.savingsChargeTimeTypeOptions, template.clientChargeCalculationTypeOptions, template.clientChargeTimeTypeOptions, + charge.feeOnMonthDay, charge.feeInterval, charge.minCap, charge.maxCap, charge.feeFrequency, template.feeFrequencyOptions, + charge.incomeOrLiabilityAccount, template.incomeOrLiabilityAccountOptions, template.taxGroupOptions, + template.shareChargeCalculationTypeOptions, template.shareChargeTimeTypeOptions, template.accountMappingForChargeConfig, + template.expenseAccountOptions, template.assetAccountOptions); } 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 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 Integer restartFrequencyEnum, final boolean isPaymentType, final PaymentTypeData paymentTypeOptions, + final BigDecimal minCap, final BigDecimal maxCap, final EnumOptionData feeFrequency, final GLAccountData accountData, + TaxGroupData taxGroupData) { final Collection currencyOptions = null; final List chargeCalculationTypeOptions = null; @@ -148,13 +152,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, 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); + penalty, active, freeWithdrawal, freeWithdrawalChargeFrequency, restartFrequency, restartFrequencyEnum, isPaymentType, + paymentTypeOptions, 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) { @@ -200,27 +204,28 @@ public final class ChargeData implements Comparable, Serializable { final List assetAccountOptions = null; return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode, - 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); + penalty, active, freeWithdrawal, freeWithdrawalChargeFrequency, restartFrequency, restartFrequencyEnum, isPaymentType, + paymentTypeOptions, 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 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 boolean isPaymentType, final PaymentTypeData paymentTypeOptions, 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, @@ -241,6 +246,8 @@ public final class ChargeData implements Comparable, Serializable { this.freeWithdrawalChargeFrequency = freeWithdrawalChargeFrequency; this.restartFrequency = restartFrequency; this.restartFrequencyEnum = restartFrequencyEnum; + this.isPaymentType = isPaymentType; + this.paymentTypeOptions = paymentTypeOptions; this.minCap = minCap; this.maxCap = maxCap; this.currencyOptions = currencyOptions; 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 670db812a..ea1eda4c8 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 @@ -49,6 +49,8 @@ import org.apache.fineract.portfolio.charge.exception.ChargeMustBePenaltyExcepti 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.paymenttype.data.PaymentTypeData; +import org.apache.fineract.portfolio.paymenttype.domain.PaymentType; import org.apache.fineract.portfolio.tax.data.TaxGroupData; import org.apache.fineract.portfolio.tax.domain.TaxGroup; @@ -116,6 +118,13 @@ public class Charge extends AbstractPersistableCustom { @Column(name = "restart_frequency_enum", nullable = true) private Integer restartFrequencyEnum; + @Column(name = "is_payment_type", nullable = false) + private boolean enablePaymentType; + + @ManyToOne + @JoinColumn(name = "payment_type_id", nullable = false) + private PaymentType paymentType; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "income_or_liability_account_id") private GLAccount account; @@ -124,7 +133,8 @@ public class Charge extends AbstractPersistableCustom { @JoinColumn(name = "tax_group_id") private TaxGroup taxGroup; - public static Charge fromJson(final JsonCommand command, final GLAccount account, final TaxGroup taxGroup) { + public static Charge fromJson(final JsonCommand command, final GLAccount account, final TaxGroup taxGroup, + final PaymentType paymentType) { final String name = command.stringValueOfParameterNamed("name"); final BigDecimal amount = command.bigDecimalValueOfParameterNamed("amount"); @@ -149,6 +159,9 @@ public class Charge extends AbstractPersistableCustom { boolean enableFreeWithdrawalCharge = false; enableFreeWithdrawalCharge = command.booleanPrimitiveValueOfParameterNamed("enableFreeWithdrawalCharge"); + boolean enablePaymentType = false; + enablePaymentType = command.booleanPrimitiveValueOfParameterNamed("enablePaymentType"); + Integer freeWithdrawalFrequency = null; Integer restartCountFrequency = null; PeriodFrequencyType countFrequencyType = null; @@ -162,7 +175,7 @@ public class Charge extends AbstractPersistableCustom { return new Charge(name, amount, currencyCode, chargeAppliesTo, chargeTimeType, chargeCalculationType, penalty, active, paymentMode, feeOnMonthDay, feeInterval, minCap, maxCap, feeFrequency, enableFreeWithdrawalCharge, freeWithdrawalFrequency, - restartCountFrequency, countFrequencyType, account, taxGroup); + restartCountFrequency, countFrequencyType, account, taxGroup, enablePaymentType, paymentType); } protected Charge() {} @@ -172,7 +185,7 @@ public class Charge extends AbstractPersistableCustom { final ChargePaymentMode paymentMode, final MonthDay feeOnMonthDay, final Integer feeInterval, final BigDecimal minCap, final BigDecimal maxCap, final Integer feeFrequency, final boolean enableFreeWithdrawalCharge, final Integer freeWithdrawalFrequency, final Integer restartFrequency, final PeriodFrequencyType restartFrequencyEnum, - final GLAccount account, final TaxGroup taxGroup) { + final GLAccount account, final TaxGroup taxGroup, final boolean enablePaymentType, final PaymentType paymentType) { this.name = name; this.amount = amount; this.currencyCode = currencyCode; @@ -224,6 +237,14 @@ public class Charge extends AbstractPersistableCustom { this.restartFrequencyEnum = restartFrequencyEnum.getValue(); } + if (enablePaymentType) { + if (paymentType != null) { + + this.enablePaymentType = enablePaymentType; + this.paymentType = paymentType; + } + } + } else if (isLoanCharge()) { if (penalty && (chargeTime.isTimeOfDisbursement() || chargeTime.isTrancheDisbursement())) { @@ -334,6 +355,10 @@ public class Charge extends AbstractPersistableCustom { return this.enableFreeWithdrawal; } + public boolean isEnablePaymentType() { + return this.enablePaymentType; + } + public Integer getFrequencyFreeWithdrawalCharge() { return this.freeWithdrawalFrequency; } @@ -346,6 +371,22 @@ public class Charge extends AbstractPersistableCustom { return this.restartFrequencyEnum; } + public PaymentType getPaymentType() { + return this.paymentType; + } + + public void setPaymentType(PaymentType paymentType) { + this.paymentType = paymentType; + } + + private Long getPaymentTypeId() { + Long paymentTypeId = null; + if (this.paymentType != null) { + paymentTypeId = this.paymentType.getId(); + } + return paymentTypeId; + } + public Map update(final JsonCommand command) { final Map actualChanges = new LinkedHashMap<>(7); @@ -439,6 +480,19 @@ public class Charge extends AbstractPersistableCustom { } + final String enablePaymentTypeParamName = "enablePaymentType"; + if (command.isChangeInBooleanParameterNamed(enablePaymentTypeParamName, this.enablePaymentType)) { + final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(enablePaymentTypeParamName); + actualChanges.put(enablePaymentTypeParamName, newValue); + this.enablePaymentType = newValue; + } + + final String paymentTypeParamName = "paymentTypeId"; + if (command.isChangeInLongParameterNamed(paymentTypeParamName, getPaymentTypeId())) { + final Long newValue = command.longValueOfParameterNamed(paymentTypeParamName); + actualChanges.put(paymentTypeParamName, newValue); + } + final String chargeAppliesToParamName = "chargeAppliesTo"; if (command.isChangeInIntegerParameterNamed(chargeAppliesToParamName, this.chargeAppliesTo)) { /* @@ -613,11 +667,16 @@ public class Charge extends AbstractPersistableCustom { taxGroupData = TaxGroupData.lookup(taxGroup.getId(), taxGroup.getName()); } + PaymentTypeData paymentTypeData = null; + if (this.paymentType != null) { + paymentTypeData = PaymentTypeData.instance(paymentType.getId(), paymentType.getPaymentName()); + } + 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.enableFreeWithdrawal, - this.freeWithdrawalFrequency, this.restartFrequency, this.restartFrequencyEnum, this.minCap, this.maxCap, feeFrequencyType, - accountData, taxGroupData); + this.freeWithdrawalFrequency, this.restartFrequency, this.restartFrequencyEnum, this.enablePaymentType, paymentTypeData, + 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 e5d35e863..eee9ff988 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,8 +54,8 @@ 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)); + "enableFreeWithdrawalCharge", "freeWithdrawalFrequency", "restartCountFrequency", "countFrequencyType", "paymentTypeId", + "enablePaymentType", ChargesApiConstants.glAccountIdParamName, ChargesApiConstants.taxGroupIdParamName)); private final FromJsonHelper fromApiJsonHelper; @@ -114,6 +114,17 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer { } } + if (this.fromApiJsonHelper.parameterExists("enablePaymentType", element)) { + + final boolean enablePaymentType = this.fromApiJsonHelper.extractBooleanNamed("enablePaymentType", element); + baseDataValidator.reset().parameter("enablePaymentType").value(enablePaymentType).notNull(); + + if (enablePaymentType) { + final Integer paymentTypeId = this.fromApiJsonHelper.extractIntegerNamed("paymentTypeId", element, Locale.getDefault()); + baseDataValidator.reset().parameter("paymentTypeId").value(paymentTypeId).integerGreaterThanZero(); + } + } + if (feeFrequency != null) { baseDataValidator.reset().parameter("feeInterval").value(feeInterval).notNull(); } @@ -320,6 +331,17 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer { Locale.getDefault()); baseDataValidator.reset().parameter("countFrequencyType").value(countFrequencyType); } + + Boolean enablePaymentType = false; + if (this.fromApiJsonHelper.parameterExists("enablePaymentType", element)) { + enablePaymentType = this.fromApiJsonHelper.extractBooleanNamed("enablePaymentType", element); + baseDataValidator.reset().parameter("enablePaymentType").value(enablePaymentType).notNull(); + + if (enablePaymentType) { + final Integer paymentTypeId = this.fromApiJsonHelper.extractIntegerNamed("paymentTypeId", element, Locale.getDefault()); + baseDataValidator.reset().parameter("paymentTypeId").value(paymentTypeId).integerGreaterThanZero(); + } + } } if (this.fromApiJsonHelper.parameterExists("chargeAppliesTo", 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 26f46eab5..cf9ab4e5a 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 @@ -43,6 +43,7 @@ import org.apache.fineract.portfolio.charge.domain.ChargeTimeType; import org.apache.fineract.portfolio.charge.exception.ChargeNotFoundException; import org.apache.fineract.portfolio.common.service.CommonEnumerations; import org.apache.fineract.portfolio.common.service.DropdownReadPlatformService; +import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData; import org.apache.fineract.portfolio.tax.data.TaxGroupData; import org.apache.fineract.portfolio.tax.service.TaxReadPlatformService; import org.springframework.beans.factory.annotation.Autowired; @@ -293,10 +294,10 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService + "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, " + "c.income_or_liability_account_id as glAccountId , acc.name as glAccountName, acc.gl_code as glCode, " - + "tg.id as taxGroupId, tg.name as taxGroupName " + "from m_charge c " - + "join m_organisation_currency oc on c.currency_code = oc.code " + + "tg.id as taxGroupId, c.is_payment_type as isPaymentType, pt.id as paymentTypeId, pt.value as paymentTypeName, tg.name as taxGroupName " + + "from m_charge c " + "join m_organisation_currency oc on c.currency_code = oc.code " + " LEFT JOIN acc_gl_account acc on acc.id = c.income_or_liability_account_id " - + " LEFT JOIN m_tax_group tg on tg.id = c.tax_group_id "; + + " LEFT JOIN m_tax_group tg on tg.id = c.tax_group_id " + " LEFT JOIN m_payment_type pt on pt.id = c.payment_type_id "; } public String loanProductChargeSchema() { @@ -378,9 +379,19 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService final int restartFrequency = rs.getInt("restartFrequency"); final int restartFrequencyEnum = rs.getInt("restartFrequencyEnum"); + final boolean isPaymentType = rs.getBoolean("isPaymentType"); + final Long paymentTypeId = JdbcSupport.getLong(rs, "paymentTypeId"); + + final String paymentTypeName = rs.getString("paymentTypeName"); + PaymentTypeData paymentTypeData = null; + if (paymentTypeId != null) { + paymentTypeData = PaymentTypeData.instance(paymentTypeId, paymentTypeName); + } + return ChargeData.instance(id, name, amount, currency, chargeTimeType, chargeAppliesToType, chargeCalculationType, chargePaymentMode, feeOnMonthDay, feeInterval, penalty, active, isFreeWithdrawal, freeWithdrawalChargeFrequency, - restartFrequency, restartFrequencyEnum, minCap, maxCap, feeFrequencyType, glAccountData, taxGroupData); + restartFrequency, restartFrequencyEnum, isPaymentType, paymentTypeData, 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 22959c044..e11d44a72 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,8 @@ 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.paymentdetail.PaymentDetailConstants; +import org.apache.fineract.portfolio.paymenttype.domain.PaymentType; import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepositoryWrapper; import org.apache.fineract.portfolio.tax.domain.TaxGroup; import org.apache.fineract.portfolio.tax.domain.TaxGroupRepositoryWrapper; @@ -110,7 +112,16 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP taxGroup = this.taxGroupRepository.findOneWithNotFoundDetection(taxGroupId); } - final Charge charge = Charge.fromJson(command, glAccount, taxGroup); + final boolean enablePaymentType = command.booleanPrimitiveValueOfParameterNamed("enablePaymentType"); + PaymentType paymentType = null; + if (enablePaymentType) { + final Long paymentTypeId = command.longValueOfParameterNamed(PaymentDetailConstants.paymentTypeParamName); + if (paymentTypeId != null) { + paymentType = this.paymentTyperepositoryWrapper.findOneWithNotFoundDetection(paymentTypeId); + } + } + + final Charge charge = Charge.fromJson(command, glAccount, taxGroup, paymentType); this.chargeRepository.save(charge); // check if the office specific products are enabled. If yes, then @@ -179,6 +190,21 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP chargeForUpdate.setAccount(newIncomeAccount); } + final String paymentTypeIdParamName = "paymentTypeId"; + if (changes.containsKey(paymentTypeIdParamName)) { + + final Integer paymentTypeIdNewValue = command.integerValueOfParameterNamed(paymentTypeIdParamName); + + PaymentType paymentType = null; + if (paymentTypeIdNewValue != null) { + final Long paymentTypeId = paymentTypeIdNewValue.longValue(); + + paymentType = this.paymentTyperepositoryWrapper.findOneWithNotFoundDetection(paymentTypeId); + chargeForUpdate.setPaymentType(paymentType); + } + + } + if (changes.containsKey(ChargesApiConstants.taxGroupIdParamName)) { final Long newValue = command.longValueOfParameterNamed(ChargesApiConstants.taxGroupIdParamName); TaxGroup taxGroup = null; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/domain/PaymentType.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/domain/PaymentType.java index aaac576ec..1207eba64 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/domain/PaymentType.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/domain/PaymentType.java @@ -97,4 +97,8 @@ public class PaymentType extends AbstractPersistableCustom { return isCashPayment; } + public String getPaymentName() { + return name; + } + } 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 b88295542..a33c8e341 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 @@ -1076,14 +1076,27 @@ public class SavingsAccount extends AbstractPersistableCustom { for (SavingsAccountCharge charge : this.charges()) { if (charge.isWithdrawalFee() && charge.isActive()) { + if (charge.getFreeWithdrawalCount() == null) { charge.setFreeWithdrawalCount(0); } - if (charge.isEnableFreeWithdrawal()) { + if (charge.isEnablePaymentType() && charge.isEnableFreeWithdrawal()) { // discount transaction to + // specific paymentType + if (paymentDetail.getPaymentType().getPaymentName().equals(charge.getCharge().getPaymentType().getPaymentName())) { + resetFreeChargeDaysCount(charge, transactionAmount, transactionDate, user); + } + } else if (charge.isEnablePaymentType()) { // normal charge-transaction to specific paymentType + if (paymentDetail.getPaymentType().getPaymentName().equals(charge.getCharge().getPaymentType().getPaymentName())) { + charge.updateWithdralFeeAmount(transactionAmount); + this.payCharge(charge, charge.getAmountOutstanding(this.getCurrency()), transactionDate, user); + } + } else if (!charge.isEnablePaymentType() && charge.isEnableFreeWithdrawal()) { // discount transaction + // irrespective of + // PaymentTypes. resetFreeChargeDaysCount(charge, transactionAmount, transactionDate, user); - } else { + } else { // normal-withdraw charge.updateWithdralFeeAmount(transactionAmount); this.payCharge(charge, charge.getAmountOutstanding(this.getCurrency()), transactionDate, user); } 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 f0e840197..4e35d0b1d 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 @@ -677,6 +677,10 @@ public class SavingsAccountCharge extends AbstractPersistableCustom { return charge.isEnableFreeWithdrawal(); } + public boolean isEnablePaymentType() { + return charge.isEnablePaymentType(); + } + public Integer getFrequencyFreeWithdrawalCharge() { // number of times free withdrawal allowed return charge.getFrequencyFreeWithdrawalCharge(); } diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V271_2__withdrawal_charges_paymenttype.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V271_2__withdrawal_charges_paymenttype.sql new file mode 100644 index 000000000..8a77ba434 --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V271_2__withdrawal_charges_paymenttype.sql @@ -0,0 +1,27 @@ +-- +-- 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_payment_type` INT NULL DEFAULT '0' AFTER `restart_frequency_enum`; + +ALTER TABLE `m_charge` + ADD COLUMN `payment_type_id` INT(11) NULL DEFAULT NULL AFTER `is_payment_type`; + +ALTER TABLE `m_charge` +ADD CONSTRAINT `FK_m_payment_detail_m_payment_types` FOREIGN KEY (`payment_type_id`) REFERENCES `m_payment_type` (`id`); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java index e3acbaa26..cb843e44d 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java @@ -42,6 +42,7 @@ import java.util.Locale; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.integrationtests.common.ClientHelper; import org.apache.fineract.integrationtests.common.CommonConstants; +import org.apache.fineract.integrationtests.common.PaymentTypeHelper; import org.apache.fineract.integrationtests.common.SchedulerJobHelper; import org.apache.fineract.integrationtests.common.TaxComponentHelper; import org.apache.fineract.integrationtests.common.TaxGroupHelper; @@ -2127,4 +2128,86 @@ public class ClientSavingsIntegrationTest { assertEquals(balance, summary.get("accountBalance"), "Verifying opening Balance is -300"); } + + @Test + public void testSavingsAccountWithdrawalChargesOnPaymentTypes() { + + this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); + + final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec); + ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID); + + final String minBalanceForInterestCalculation = null; + final String minRequiredBalance = null; + final String enforceMinRequiredBalance = "false"; + final boolean allowOverdraft = false; + final String MINIMUM_OPENING_BALANCE = "10000"; + final String withdrawalAmountOne = "1000"; + final String withdrawalAmountTwo = "2000"; + final Integer withdrawalChargeOne = 10; + final Integer withdrawalChargeTwo = 20; + + final Integer savingsProductID = createSavingsProduct(this.requestSpec, this.responseSpec, MINIMUM_OPENING_BALANCE, + minBalanceForInterestCalculation, minRequiredBalance, enforceMinRequiredBalance, allowOverdraft); + 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); + + String name = PaymentTypeHelper.randomNameGenerator("P_T", 5); + String description = PaymentTypeHelper.randomNameGenerator("PT_Desc", 15); + Boolean isCashPayment = false; + Integer position = 1; + + Integer paymentTypeIdOne = PaymentTypeHelper.createPaymentType(requestSpec, responseSpec, name, description, isCashPayment, + position); + Assertions.assertNotNull(paymentTypeIdOne); + + final Integer chargeIdOne = ChargesHelper.createCharges(this.requestSpec, this.responseSpec, + ChargesHelper.paymentTypeCharge(withdrawalChargeOne, true, paymentTypeIdOne)); + Assertions.assertNotNull(chargeIdOne); + + this.savingsAccountHelper.addChargesForSavings(savingsId, chargeIdOne, false, BigDecimal.valueOf(withdrawalChargeOne)); + + Integer withdrawTransactionIdOne = (Integer) this.savingsAccountHelper.withdrawalFromSavingsAccountWithPaymentType(savingsId, + withdrawalAmountOne, SavingsAccountHelper.TRANSACTION_DATE, paymentTypeIdOne.toString(), + CommonConstants.RESPONSE_RESOURCE_ID); + + Float balance = Float.parseFloat("8990"); + // Withdraw charge from paymentType 1 is 10 So balance should be 10,000(deposit)-1000(wd)-"10(charge)" = 8990 + + HashMap summary = this.savingsAccountHelper.getSavingsSummary(savingsId); + assertEquals(balance, summary.get("accountBalance"), "Verifying Balance after withdrawal charge "); + + String paymentTypeNameTwo = PaymentTypeHelper.randomNameGenerator("P_T", 5); + + Integer paymentTypeIdTwo = PaymentTypeHelper.createPaymentType(requestSpec, responseSpec, paymentTypeNameTwo, description, + isCashPayment, position); + Assertions.assertNotNull(paymentTypeIdTwo); + + final Integer chargeIdTwo = ChargesHelper.createCharges(this.requestSpec, this.responseSpec, + ChargesHelper.paymentTypeCharge(withdrawalChargeTwo, true, paymentTypeIdTwo)); + Assertions.assertNotNull(chargeIdTwo); + + this.savingsAccountHelper.addChargesForSavings(savingsId, chargeIdTwo, false, BigDecimal.valueOf(withdrawalChargeTwo)); + + Integer withdrawTransactionIdTwo = (Integer) this.savingsAccountHelper.withdrawalFromSavingsAccountWithPaymentType(savingsId, + withdrawalAmountTwo, SavingsAccountHelper.TRANSACTION_DATE, paymentTypeIdTwo.toString(), + CommonConstants.RESPONSE_RESOURCE_ID); + + Float balanceAfterChargeTwo = Float.parseFloat("6970"); + // Withdraw charge from paymentType 2 is 20 So balance should be 8990(balance)-2000(wd)-"20(charge)" = 6970 + + HashMap summaryTwo = this.savingsAccountHelper.getSavingsSummary(savingsId); + assertEquals(balanceAfterChargeTwo, summaryTwo.get("accountBalance"), "Verifying Balance after withdrawal charge two "); + } } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/charges/ChargesHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/charges/ChargesHelper.java index 32ce65be5..b2401d2e0 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/charges/ChargesHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/charges/ChargesHelper.java @@ -188,6 +188,27 @@ public final class ChargesHelper { return chargesCreateJson; } + public static String paymentTypeCharge(Integer amount, final boolean enablePaymentType, final Integer paymentTypeId) { + return paymentTypeChargeJSON(amount, enablePaymentType, paymentTypeId); + } + + public static String paymentTypeChargeJSON(Integer amount, final boolean enablePaymentType, final Integer paymentTypeId) { + final HashMap map = new HashMap<>(); + map.put("active", ChargesHelper.ACTIVE); + map.put("amount", amount); + map.put("chargeAppliesTo", ChargesHelper.CHARGE_APPLIES_TO_SAVINGS); + map.put("chargeCalculationType", ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT); + map.put("currencyCode", ChargesHelper.CURRENCY_CODE); + map.put("locale", CommonConstants.LOCALE); + map.put("monthDayFormat", ChargesHelper.MONTH_DAY_FORMAT); + map.put("name", Utils.randomNameGenerator("Charge_Savings_", 6)); + map.put("chargeTimeType", CHARGE_WITHDRAWAL_FEE); + map.put("enablePaymentType", enablePaymentType); + map.put("paymentTypeId", paymentTypeId); + String paymentTypeJson = new Gson().toJson(map); + return paymentTypeJson; + } + public static String getLoanSpecifiedDueDateJSON() { return getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, ChargesHelper.AMOUNT, ChargesHelper.PENALTY); } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsAccountHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsAccountHelper.java index 0507f3e92..f07695011 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsAccountHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsAccountHelper.java @@ -253,6 +253,13 @@ public class SavingsAccountHelper { getSavingsTransactionJSON(amount, date), jsonAttributeToGetback); } + public Object withdrawalFromSavingsAccountWithPaymentType(final Integer savingsId, final String amount, String date, String paymentType, + String jsonAttributeToGetback) { + LOG.info("\n--------------------------------- SAVINGS TRANSACTION WITHDRAWAL WITH PAYMENT TYPE--------------------------------"); + return performSavingActions(createSavingsTransactionURL(WITHDRAW_SAVINGS_COMMAND, savingsId), + getSavingsTransactionPaymentTypeJSON(amount, date, paymentType), jsonAttributeToGetback); + } + public Integer updateSavingsAccountTransaction(final Integer savingsId, final Integer transactionId, final String amount) { LOG.info("\n--------------------------------- MODIFY SAVINGS TRANSACTION --------------------------------"); return (Integer) performSavingActions(createAdjustTransactionURL(MODIFY_TRASACTION_COMMAND, savingsId, transactionId), @@ -432,6 +439,18 @@ public class SavingsAccountHelper { return savingsAccountWithdrawalJson; } + private String getSavingsTransactionPaymentTypeJSON(final String amount, final String transactionDate, final String paymentTypeId) { + final HashMap map = new HashMap<>(); + map.put("locale", CommonConstants.LOCALE); + map.put("dateFormat", CommonConstants.DATE_FORMAT); + map.put("transactionDate", transactionDate); + map.put("transactionAmount", amount); + map.put("paymentTypeId", paymentTypeId); + String savingsAccountWithdrawalJson = new Gson().toJson(map); + LOG.info(savingsAccountWithdrawalJson); + return savingsAccountWithdrawalJson; + } + private String getCalculatedInterestForSavingsApplicationAsJSON() { final HashMap map = new HashMap<>(); String savingsAccountCalculatedInterestJson = new Gson().toJson(map);