diff --git a/mifosng-db/migrations/core_db/V26__add-support-for-withdrawal-fees-on-savings.sql b/mifosng-db/migrations/core_db/V26__add-support-for-withdrawal-fees-on-savings.sql new file mode 100644 index 000000000..f5aeba923 --- /dev/null +++ b/mifosng-db/migrations/core_db/V26__add-support-for-withdrawal-fees-on-savings.sql @@ -0,0 +1,12 @@ +ALTER TABLE `m_savings_product` +ADD COLUMN `withdrawal_fee_amount` DECIMAL(19,6) NULL DEFAULT NULL AFTER `lockin_period_frequency_enum`, +ADD COLUMN `withdrawal_fee_type_enum` SMALLINT(5) NULL DEFAULT NULL AFTER `withdrawal_fee_amount`; + + +ALTER TABLE `m_savings_account` +ADD COLUMN `withdrawal_fee_amount` DECIMAL(19,6) NULL DEFAULT NULL AFTER `lockin_period_frequency_enum`, +ADD COLUMN `withdrawal_fee_type_enum` SMALLINT(5) NULL DEFAULT NULL AFTER `withdrawal_fee_amount`; + + +ALTER TABLE `m_savings_account` +ADD COLUMN `total_withdrawal_fees_derived` DECIMAL(19,6) NULL DEFAULT NULL AFTER `total_withdrawals_derived`; \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/exception/GeneralPlatformDomainRuleException.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/exception/GeneralPlatformDomainRuleException.java new file mode 100644 index 000000000..78fe3956c --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/exception/GeneralPlatformDomainRuleException.java @@ -0,0 +1,18 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.infrastructure.core.exception; + +/** + * A {@link RuntimeException} thrown when valid api request end up violating + * some domain rule. + */ +public final class GeneralPlatformDomainRuleException extends AbstractPlatformDomainRuleException { + + public GeneralPlatformDomainRuleException(final String globalisationMessageCode, final String defaultUserMessage, + final Object... defaultUserMessageArgs) { + super(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeCalculationType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeCalculationType.java index 6bf4084a5..d53232968 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeCalculationType.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeCalculationType.java @@ -16,7 +16,7 @@ public enum ChargeCalculationType { private final Integer value; private final String code; - private ChargeCalculationType(Integer value, String code) { + private ChargeCalculationType(final Integer value, final String code) { this.value = value; this.code = code; } @@ -44,9 +44,6 @@ public enum ChargeCalculationType { case 4: chargeCalculationType = PERCENT_OF_INTEREST; break; - default: - chargeCalculationType = INVALID; - break; } return chargeCalculationType; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountsApiResource.java index b532b9940..7bb69f3a4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsAccountsApiResource.java @@ -145,6 +145,7 @@ public class SavingsAccountsApiResource { Collection interestCalculationTypeOptions = null; Collection interestCalculationDaysInYearTypeOptions = null; Collection lockinPeriodFrequencyTypeOptions = null; + Collection withdrawalFeeTypeOptions = null; final Set associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); if (!associationParameters.isEmpty()) { @@ -170,11 +171,12 @@ public class SavingsAccountsApiResource { interestCompoundingPostingTypeOptions = this.dropdownReadPlatformService.retrieveInterestPostingPeriodTypeOptions(); interestCalculationTypeOptions = this.dropdownReadPlatformService.retrieveInterestCalculationTypeOptions(); lockinPeriodFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveLockinPeriodFrequencyTypeOptions(); + withdrawalFeeTypeOptions = this.dropdownReadPlatformService.retrievewithdrawalFeeTypeOptions(); } return SavingsAccountData.withTemplateOptions(savingsAccount, productOptions, interestCompoundingPeriodTypeOptions, interestCompoundingPostingTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, - lockinPeriodFrequencyTypeOptions, transactions); + lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions); } @PUT diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsApiConstants.java index 8ab1b5c15..9d41516e4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsApiConstants.java @@ -39,6 +39,8 @@ public class SavingsApiConstants { public static final String minRequiredOpeningBalanceParamName = "minRequiredOpeningBalance"; public static final String lockinPeriodFrequencyParamName = "lockinPeriodFrequency"; public static final String lockinPeriodFrequencyTypeParamName = "lockinPeriodFrequencyType"; + public static final String withdrawalFeeAmountParamName = "withdrawalFeeAmount"; + public static final String withdrawalFeeTypeParamName = "withdrawalFeeType"; // transaction parameters public static final String transactionDateParamName = "transactionDate"; @@ -48,7 +50,7 @@ public class SavingsApiConstants { nameParamName, descriptionParamName, currencyCodeParamName, digitsAfterDecimalParamName, nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, - lockinPeriodFrequencyTypeParamName)); + lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName)); /** * These parameters will match the class level parameters of @@ -59,15 +61,16 @@ public class SavingsApiConstants { nameParamName, descriptionParamName, "currency", digitsAfterDecimalParamName, nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, - lockinPeriodFrequencyTypeParamName, "currencyOptions", "interestCompoundingPeriodTypeOptions", - "interestPostingPeriodTypeOptions", "interestCalculationTypeOptions", "interestCalculationDaysInYearTypeOptions", - "lockinPeriodFrequencyTypeOptions")); + lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, "currencyOptions", + "interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions", "interestCalculationTypeOptions", + "interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions")); public static final Set SAVINGS_ACCOUNT_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, accountNoParamName, externalIdParamName, clientIdParamName, groupIdParamName, productIdParamName, activeParamName, activationDateParamName, nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, - minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName)); + minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, + withdrawalFeeAmountParamName, withdrawalFeeTypeParamName)); /** * These parameters will match the class level parameters of @@ -78,9 +81,10 @@ public class SavingsApiConstants { accountNoParamName, externalIdParamName, statusParamName, activationDateParamName, clientIdParamName, "clientName", groupIdParamName, "groupName", "savingsProductId", "savingsProductName", "currency", nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, - minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, "summary", - "transactions", "productOptions", "interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions", - "interestCalculationTypeOptions", "interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions")); + minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, + withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, "summary", "transactions", "productOptions", + "interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions", "interestCalculationTypeOptions", + "interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions")); public static final Set SAVINGS_ACCOUNT_TRANSACTION_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList( localeParamName, dateFormatParamName, transactionDateParamName, transactionAmountParamName)); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsProductsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsProductsApiResource.java index 8febadbb0..510e1fd29 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsProductsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/api/SavingsProductsApiResource.java @@ -180,16 +180,18 @@ public class SavingsProductsApiResource { final Collection lockinPeriodFrequencyTypeOptions = this.dropdownReadPlatformService .retrieveLockinPeriodFrequencyTypeOptions(); + final Collection withdrawalFeeTypeOptions = this.dropdownReadPlatformService.retrievewithdrawalFeeTypeOptions(); + SavingsProductData savingsProductToReturn = null; if (savingsProduct != null) { savingsProductToReturn = SavingsProductData.withTemplate(savingsProduct, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, - lockinPeriodFrequencyTypeOptions); + lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions); } else { savingsProductToReturn = SavingsProductData.template(currency, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, - lockinPeriodFrequencyTypeOptions); + lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions); } return savingsProductToReturn; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountData.java index 050b7d8b7..28b78057c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountData.java @@ -37,6 +37,8 @@ public class SavingsAccountData { private final BigDecimal minRequiredOpeningBalance; private final Integer lockinPeriodFrequency; private final EnumOptionData lockinPeriodFrequencyType; + private final BigDecimal withdrawalFeeAmount; + private final EnumOptionData withdrawalFeeType; // associations private final SavingsAccountSummaryData summary; @@ -56,6 +58,8 @@ public class SavingsAccountData { private final Collection interestCalculationDaysInYearTypeOptions; @SuppressWarnings("unused") private final Collection lockinPeriodFrequencyTypeOptions; + @SuppressWarnings("unused") + private final Collection withdrawalFeeTypeOptions; public static SavingsAccountData instance(final Long id, final String accountNo, final String externalId, final SavingsAccountStatusEnumData status, final LocalDate activationDate, final Long groupId, final String groupName, @@ -63,7 +67,8 @@ public class SavingsAccountData { final BigDecimal interestRate, final EnumOptionData interestCompoundingPeriodType, final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, - final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final SavingsAccountSummaryData summary) { + final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount, + final EnumOptionData withdrawalFeeType, final SavingsAccountSummaryData summary) { final Collection productOptions = null; final Collection interestCompoundingPeriodTypeOptions = null; @@ -71,13 +76,16 @@ public class SavingsAccountData { final Collection interestCalculationTypeOptions = null; final Collection interestCalculationDaysInYearTypeOptions = null; final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + final Collection transactions = null; return new SavingsAccountData(id, accountNo, externalId, status, activationDate, groupId, groupName, clientId, clientName, productId, productName, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, - lockinPeriodFrequencyType, summary, productOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, - interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, transactions); + lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, summary, transactions, productOptions, + interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions); } public static SavingsAccountData withTemplateOptions(final SavingsAccountData account, @@ -85,15 +93,17 @@ public class SavingsAccountData { final Collection interestPostingPeriodTypeOptions, final Collection interestCalculationTypeOptions, final Collection interestCalculationDaysInYearTypeOptions, - final Collection lockinPeriodFrequencyTypeOptions, final Collection transactions) { + final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions, + final Collection transactions) { return new SavingsAccountData(account.id, account.accountNo, account.externalId, account.status, account.activationDate, account.groupId, account.groupName, account.clientId, account.clientName, account.savingsProductId, account.savingsProductName, account.currency, account.nominalAnnualInterestRate, account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType, account.interestCalculationDaysInYearType, - account.minRequiredOpeningBalance, account.lockinPeriodFrequency, account.lockinPeriodFrequencyType, account.summary, - productOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, - interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, transactions); + account.minRequiredOpeningBalance, account.lockinPeriodFrequency, account.lockinPeriodFrequencyType, + account.withdrawalFeeAmount, account.withdrawalFeeType, account.summary, transactions, productOptions, + interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions); } public static SavingsAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId, @@ -115,20 +125,26 @@ public class SavingsAccountData { final BigDecimal minRequiredOpeningBalance = null; final Integer lockinPeriodFrequency = null; final EnumOptionData lockinPeriodFrequencyType = null; + final BigDecimal withdrawalFeeAmount = null; + final EnumOptionData withdrawalFeeType = null; + final SavingsAccountSummaryData summary = null; + final Collection transactions = null; + final Collection productOptions = null; final Collection interestCompoundingPeriodTypeOptions = null; final Collection interestPostingPeriodTypeOptions = null; final Collection interestCalculationTypeOptions = null; final Collection interestCalculationDaysInYearTypeOptions = null; final Collection lockinPeriodFrequencyTypeOptions = null; - final Collection transactions = null; + final Collection withdrawalFeeTypeOptions = null; return new SavingsAccountData(id, accountNo, externalId, status, activationDate, groupId, groupName, clientId, clientName, productId, productName, currency, nominalAnnualInterestRate, interestPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, - lockinPeriodFrequencyType, summary, productOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, - interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, transactions); + lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, summary, transactions, productOptions, + interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions); } private SavingsAccountData(final Long id, final String accountNo, final String externalId, final SavingsAccountStatusEnumData status, @@ -137,12 +153,13 @@ public class SavingsAccountData { final EnumOptionData interestPeriodType, final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, - final EnumOptionData lockinPeriodFrequencyType, final SavingsAccountSummaryData summary, + final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount, final EnumOptionData withdrawalFeeType, + final SavingsAccountSummaryData summary, final Collection transactions, final Collection productOptions, final Collection interestCompoundingPeriodTypeOptions, final Collection interestPostingPeriodTypeOptions, final Collection interestCalculationTypeOptions, final Collection interestCalculationDaysInYearTypeOptions, - final Collection lockinPeriodFrequencyTypeOptions, final Collection transactions) { + final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions) { this.id = id; this.accountNo = accountNo; this.externalId = externalId; @@ -163,7 +180,11 @@ public class SavingsAccountData { this.minRequiredOpeningBalance = minRequiredOpeningBalance; this.lockinPeriodFrequency = lockinPeriodFrequency; this.lockinPeriodFrequencyType = lockinPeriodFrequencyType; + this.withdrawalFeeAmount = withdrawalFeeAmount; + this.withdrawalFeeType = withdrawalFeeType; + this.summary = summary; + this.transactions = transactions; this.productOptions = productOptions; this.interestCompoundingPeriodTypeOptions = interestCompoundingPeriodTypeOptions; @@ -171,6 +192,6 @@ public class SavingsAccountData { this.interestCalculationTypeOptions = interestCalculationTypeOptions; this.interestCalculationDaysInYearTypeOptions = interestCalculationDaysInYearTypeOptions; this.lockinPeriodFrequencyTypeOptions = lockinPeriodFrequencyTypeOptions; - this.transactions = transactions; + this.withdrawalFeeTypeOptions = withdrawalFeeTypeOptions; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountDataValidator.java index 5a9c2c683..f4038c65d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountDataValidator.java @@ -15,6 +15,8 @@ import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockin import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.minRequiredOpeningBalanceParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nominalAnnualInterestRateParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.productIdParamName; +import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeAmountParamName; +import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeTypeParamName; import java.lang.reflect.Type; import java.math.BigDecimal; @@ -159,6 +161,18 @@ public class SavingsAccountDataValidator { } } + if (this.fromApiJsonHelper.parameterExists(withdrawalFeeAmountParamName, element)) { + + final BigDecimal withdrawalFeeAmount = fromApiJsonHelper + .extractBigDecimalWithLocaleNamed(withdrawalFeeAmountParamName, element); + baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(withdrawalFeeAmount).positiveAmount(); + } + + if (this.fromApiJsonHelper.parameterExists(withdrawalFeeTypeParamName, element)) { + final Integer withdrawalFeeType = fromApiJsonHelper.extractIntegerSansLocaleNamed(withdrawalFeeTypeParamName, element); + baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType).isOneOfTheseValues(1, 2); + } + throwExceptionIfValidationWarningsExist(dataValidationErrors); } @@ -290,6 +304,18 @@ public class SavingsAccountDataValidator { } } + if (this.fromApiJsonHelper.parameterExists(withdrawalFeeAmountParamName, element)) { + + final BigDecimal withdrawalFeeAmount = fromApiJsonHelper + .extractBigDecimalWithLocaleNamed(withdrawalFeeAmountParamName, element); + baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(withdrawalFeeAmount).notNull().positiveAmount(); + } + + if (this.fromApiJsonHelper.parameterExists(withdrawalFeeTypeParamName, element)) { + final Integer withdrawalFeeType = fromApiJsonHelper.extractIntegerSansLocaleNamed(withdrawalFeeTypeParamName, element); + baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType).isOneOfTheseValues(1, 2); + } + throwExceptionIfValidationWarningsExist(dataValidationErrors); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountSummaryData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountSummaryData.java index 39756f590..8844f4c18 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountSummaryData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountSummaryData.java @@ -18,15 +18,18 @@ public class SavingsAccountSummaryData { private final CurrencyData currency; private final BigDecimal totalDeposits; private final BigDecimal totalWithdrawals; + private final BigDecimal totalWithdrawalFees; private final BigDecimal totalInterestEarned; private final BigDecimal totalInterestPosted; private final BigDecimal accountBalance; public SavingsAccountSummaryData(final CurrencyData currency, final BigDecimal totalDeposits, final BigDecimal totalWithdrawals, - final BigDecimal totalInterestEarned, final BigDecimal totalInterestPosted, final BigDecimal accountBalance) { + final BigDecimal totalWithdrawalFees, final BigDecimal totalInterestEarned, final BigDecimal totalInterestPosted, + final BigDecimal accountBalance) { this.currency = currency; this.totalDeposits = totalDeposits; this.totalWithdrawals = totalWithdrawals; + this.totalWithdrawalFees = totalWithdrawalFees; this.totalInterestEarned = totalInterestEarned; this.totalInterestPosted = totalInterestPosted; this.accountBalance = accountBalance; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductData.java index 294df78ce..ef7a50326 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductData.java @@ -28,6 +28,8 @@ public class SavingsProductData { private final BigDecimal minRequiredOpeningBalance; private final Integer lockinPeriodFrequency; private final EnumOptionData lockinPeriodFrequencyType; + private final BigDecimal withdrawalFeeAmount; + private final EnumOptionData withdrawalFeeType; // template @SuppressWarnings("unused") @@ -42,6 +44,8 @@ public class SavingsProductData { private final Collection interestCalculationDaysInYearTypeOptions; @SuppressWarnings("unused") private final Collection lockinPeriodFrequencyTypeOptions; + @SuppressWarnings("unused") + private final Collection withdrawalFeeTypeOptions; public static SavingsProductData template(final CurrencyData currency, final EnumOptionData interestCompoundingPeriodType, final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, @@ -50,7 +54,7 @@ public class SavingsProductData { final Collection interestPostingPeriodTypeOptions, final Collection interestCalculationTypeOptions, final Collection interestCalculationDaysInYearTypeOptions, - final Collection lockinPeriodFrequencyTypeOptions) { + final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions) { final Long id = null; final String name = null; @@ -59,12 +63,14 @@ public class SavingsProductData { final BigDecimal minRequiredOpeningBalance = null; final Integer lockinPeriodFrequency = null; final EnumOptionData lockinPeriodFrequencyType = null; + final BigDecimal withdrawalFeeAmount = null; + final EnumOptionData withdrawalFeeType = null; return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, - lockinPeriodFrequency, lockinPeriodFrequencyType, currencyOptions, interestCompoundingPeriodTypeOptions, - interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, - lockinPeriodFrequencyTypeOptions); + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, currencyOptions, + interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions); } /** @@ -76,22 +82,24 @@ public class SavingsProductData { final Collection interestPostingPeriodTypeOptions, final Collection interestCalculationTypeOptions, final Collection interestCalculationDaysInYearTypeOptions, - final Collection lockinPeriodFrequencyTypeOptions) { + final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions) { return new SavingsProductData(existingProduct.id, existingProduct.name, existingProduct.description, existingProduct.currency, existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType, existingProduct.interestPostingPeriodType, existingProduct.interestCalculationType, existingProduct.interestCalculationDaysInYearType, existingProduct.minRequiredOpeningBalance, - existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType, currencyOptions, - interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, - interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions); + existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType, existingProduct.withdrawalFeeAmount, + existingProduct.withdrawalFeeType, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, + interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, + withdrawalFeeTypeOptions); } public static SavingsProductData instance(final Long id, final String name, final String description, final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestCompoundingPeriodType, final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, - final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType) { + final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount, + final EnumOptionData withdrawalFeeType) { final Collection currencyOptions = null; final Collection interestCompoundingPeriodTypeOptions = null; @@ -99,12 +107,13 @@ public class SavingsProductData { final Collection interestCalculationTypeOptions = null; final Collection interestCalculationDaysInYearTypeOptions = null; final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, - lockinPeriodFrequency, lockinPeriodFrequencyType, currencyOptions, interestCompoundingPeriodTypeOptions, - interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, - lockinPeriodFrequencyTypeOptions); + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, currencyOptions, + interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions); } public static SavingsProductData lookup(final Long id, final String name) { @@ -119,6 +128,8 @@ public class SavingsProductData { final BigDecimal minRequiredOpeningBalance = null; final Integer lockinPeriodFrequency = null; final EnumOptionData lockinPeriodFrequencyType = null; + final BigDecimal withdrawalFeeAmount = null; + final EnumOptionData withdrawalFeeType = null; final Collection currencyOptions = null; final Collection interestCompoundingPeriodTypeOptions = null; @@ -126,24 +137,27 @@ public class SavingsProductData { final Collection interestCalculationTypeOptions = null; final Collection interestCalculationDaysInYearTypeOptions = null; final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, - lockinPeriodFrequency, lockinPeriodFrequencyType, currencyOptions, interestCompoundingPeriodTypeOptions, - interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, - lockinPeriodFrequencyTypeOptions); + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, currencyOptions, + interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, + interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions); } private SavingsProductData(final Long id, final String name, final String description, final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestCompoundingPeriodType, final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, - final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, - final Collection currencyOptions, final Collection interestCompoundingPeriodTypeOptions, + final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, + final BigDecimal withdrawalFeeAmount, final EnumOptionData withdrawalFeeType, + final Collection currencyOptions, + final Collection interestCompoundingPeriodTypeOptions, final Collection interestPostingPeriodTypeOptions, final Collection interestCalculationTypeOptions, final Collection interestCalculationDaysInYearTypeOptions, - final Collection lockinPeriodFrequencyTypeOptions) { + final Collection lockinPeriodFrequencyTypeOptions, final Collection withdrawalFeeTypeOptions) { this.id = id; this.name = name; this.description = description; @@ -156,6 +170,8 @@ public class SavingsProductData { this.minRequiredOpeningBalance = minRequiredOpeningBalance; this.lockinPeriodFrequency = lockinPeriodFrequency; this.lockinPeriodFrequencyType = lockinPeriodFrequencyType; + this.withdrawalFeeAmount = withdrawalFeeAmount; + this.withdrawalFeeType = withdrawalFeeType; this.currencyOptions = currencyOptions; this.interestCompoundingPeriodTypeOptions = interestCompoundingPeriodTypeOptions; @@ -163,5 +179,6 @@ public class SavingsProductData { this.interestCalculationTypeOptions = interestCalculationTypeOptions; this.interestCalculationDaysInYearTypeOptions = interestCalculationDaysInYearTypeOptions; this.lockinPeriodFrequencyTypeOptions = lockinPeriodFrequencyTypeOptions; + this.withdrawalFeeTypeOptions = withdrawalFeeTypeOptions; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductDataValidator.java index c95541b2a..95724be3e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsProductDataValidator.java @@ -14,6 +14,8 @@ import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockin import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.minRequiredOpeningBalanceParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nameParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nominalAnnualInterestRateParamName; +import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeAmountParamName; +import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeTypeParamName; import java.lang.reflect.Type; import java.math.BigDecimal; @@ -67,8 +69,10 @@ public class SavingsProductDataValidator { final Integer digitsAfterDecimal = fromApiJsonHelper.extractIntegerSansLocaleNamed(digitsAfterDecimalParamName, element); baseDataValidator.reset().parameter(digitsAfterDecimalParamName).value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6); - final BigDecimal nominalAnnualInterestRate = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(nominalAnnualInterestRateParamName, element); - baseDataValidator.reset().parameter(nominalAnnualInterestRateParamName).value(nominalAnnualInterestRate).notNull().zeroOrPositiveAmount(); + final BigDecimal nominalAnnualInterestRate = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(nominalAnnualInterestRateParamName, + element); + baseDataValidator.reset().parameter(nominalAnnualInterestRateParamName).value(nominalAnnualInterestRate).notNull() + .zeroOrPositiveAmount(); final Integer interestCompoundingPeriodType = fromApiJsonHelper.extractIntegerSansLocaleNamed( interestCompoundingPeriodTypeParamName, element); @@ -121,6 +125,17 @@ public class SavingsProductDataValidator { } } + if (this.fromApiJsonHelper.parameterExists(withdrawalFeeTypeParamName, element)) { + final Integer withdrawalFeeType = fromApiJsonHelper.extractIntegerSansLocaleNamed(withdrawalFeeTypeParamName, element); + baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType).isOneOfTheseValues(1, 2); + + if (withdrawalFeeType != null) { + final BigDecimal withdrawalFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(withdrawalFeeAmountParamName, + element); + baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(withdrawalFeeAmount).notNull().positiveAmount(); + } + } + throwExceptionIfValidationWarningsExist(dataValidationErrors); } @@ -195,14 +210,22 @@ public class SavingsProductDataValidator { baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).inMinMaxRange(1, 3); } + if (this.fromApiJsonHelper.parameterExists(withdrawalFeeAmountParamName, element)) { + final BigDecimal withdrawalFeeAmount = fromApiJsonHelper + .extractBigDecimalWithLocaleNamed(withdrawalFeeAmountParamName, element); + baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(withdrawalFeeAmount).ignoreIfNull().positiveAmount(); + } + + if (this.fromApiJsonHelper.parameterExists(withdrawalFeeTypeParamName, element)) { + final Integer withdrawalFeeType = fromApiJsonHelper.extractIntegerSansLocaleNamed(withdrawalFeeTypeParamName, element); + baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType).ignoreIfNull() + .isOneOfTheseValues(1, 2); + } + throwExceptionIfValidationWarningsExist(dataValidationErrors); } private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { - if (!dataValidationErrors.isEmpty()) { - // - throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", - dataValidationErrors); - } + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java index 1c70ebe0e..763d1dacb 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java @@ -136,6 +136,12 @@ public class SavingsAccount extends AbstractPersistable { @Column(name = "lockedin_until_date_derived", nullable = true) private Date lockedInUntilDate; + @Column(name = "withdrawal_fee_amount", scale = 6, precision = 19, nullable = true) + private BigDecimal withdrawalFeeAmount; + + @Column(name = "withdrawal_fee_type_enum", nullable = true) + private Integer withdrawalFeeType; + @Embedded private SavingsAccountSummary summary; @@ -158,13 +164,14 @@ public class SavingsAccount extends AbstractPersistable { final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, - final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType) { + final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, + final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType) { final SavingsAccountStatusType status = SavingsAccountStatusType.UNACTIVATED; final LocalDate activationDate = null; return new SavingsAccount(client, group, product, accountNo, externalId, status, activationDate, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, - minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType); + minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType); } private SavingsAccount(final Client client, final Group group, final SavingsProduct product, final String accountNo, @@ -172,7 +179,8 @@ public class SavingsAccount extends AbstractPersistable { final BigDecimal nominalAnnualInterestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, - final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType) { + final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, + final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType) { this.client = client; this.group = group; this.product = product; @@ -198,6 +206,10 @@ public class SavingsAccount extends AbstractPersistable { if (lockinPeriodFrequencyType != null) { this.lockinPeriodFrequencyType = lockinPeriodFrequencyType.getValue(); } + this.withdrawalFeeAmount = withdrawalFeeAmount; + if (withdrawalFeeType != null) { + this.withdrawalFeeType = withdrawalFeeType.getValue(); + } this.summary = new SavingsAccountSummary(); } @@ -313,7 +325,7 @@ public class SavingsAccount extends AbstractPersistable { } } } - + if (recalucateDailyBalanceDetails) { // no openingBalance concept supported yet but probably will to // allow @@ -411,6 +423,8 @@ public class SavingsAccount extends AbstractPersistable { this.summary.updateFromInterestPeriodSummaries(currency, compoundingPeriods); + this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); + return compoundingPeriods; } @@ -788,16 +802,38 @@ public class SavingsAccount extends AbstractPersistable { throw new PlatformApiDataValidationException(dataValidationErrors); } - final Money amount = Money.of(this.currency, transactionAmount); + final Money transactionAmountMoney = Money.of(this.currency, transactionAmount); - if (isNotEnoughFundsToWithdraw(amount)) { + if (isNotEnoughFundsToWithdraw(transactionAmountMoney)) { // throw new InsufficientAccountBalanceException("transactionAmount", getAccountBalance(), transactionAmount); } - final SavingsAccountTransaction transaction = SavingsAccountTransaction.withdrawal(this, transactionDate, amount); + final SavingsAccountTransaction transaction = SavingsAccountTransaction.withdrawal(this, transactionDate, transactionAmountMoney); this.transactions.add(transaction); + if (isAutomaticWithdrawalFee()) { + + SavingsAccountTransaction withdrawalFeeTransaction = null; + Money feeAmount = null; + switch (SavingsWithdrawalFeesType.fromInt(this.withdrawalFeeType)) { + case INVALID: + break; + case FLAT: + feeAmount = Money.of(this.currency, this.withdrawalFeeAmount); + withdrawalFeeTransaction = SavingsAccountTransaction.fee(this, transactionDate, feeAmount); + this.transactions.add(withdrawalFeeTransaction); + break; + case PERCENT_OF_AMOUNT: + final BigDecimal feeAmountDecimal = transactionAmount.multiply(this.withdrawalFeeAmount).divide( + BigDecimal.valueOf(100l)); + feeAmount = Money.of(this.currency, feeAmountDecimal); + withdrawalFeeTransaction = SavingsAccountTransaction.fee(this, transactionDate, feeAmount); + this.transactions.add(withdrawalFeeTransaction); + break; + } + } + this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); final LocalDate today = DateUtils.getLocalDateOfTenant(); @@ -806,6 +842,10 @@ public class SavingsAccount extends AbstractPersistable { return transaction; } + private boolean isAutomaticWithdrawalFee() { + return this.withdrawalFeeType != null; + } + private boolean isAccountLocked(final LocalDate transactionDate) { boolean isLocked = false; final boolean accountHasLockedInSetting = this.lockedInUntilDate != null; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java index 35a3bc8b9..d3550da88 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountAssembler.java @@ -15,6 +15,8 @@ import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockin import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyTypeParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.minRequiredOpeningBalanceParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nominalAnnualInterestRateParamName; +import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeAmountParamName; +import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeTypeParamName; import java.math.BigDecimal; import java.util.Locale; @@ -153,9 +155,27 @@ public class SavingsAccountAssembler { lockinPeriodFrequencyType = product.lockinPeriodFrequencyType(); } + BigDecimal withdrawalFeeAmount = null; + if (command.parameterExists(withdrawalFeeAmountParamName)) { + withdrawalFeeAmount = command.bigDecimalValueOfParameterNamed(withdrawalFeeAmountParamName); + } else { + withdrawalFeeAmount = product.withdrawalFeeAmount(); + } + + SavingsWithdrawalFeesType withdrawalFeeType = null; + if (command.parameterExists(withdrawalFeeAmountParamName)) { + final Integer withdrawalFeeTypeValue = command.integerValueOfParameterNamed(withdrawalFeeTypeParamName); + if (withdrawalFeeTypeValue != null) { + withdrawalFeeType = SavingsWithdrawalFeesType.fromInt(withdrawalFeeTypeValue); + } + } else { + withdrawalFeeType = product.withdrawalFeeType(); + } + + final SavingsAccount account = SavingsAccount.createNewAccount(client, group, product, accountNo, externalId, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, - minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType); + minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType); account.setHelpers(this.savingsAccountTransactionSummaryWrapper); if (active) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountSummary.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountSummary.java index af83e5a0d..4bd0d8243 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountSummary.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountSummary.java @@ -29,6 +29,9 @@ public final class SavingsAccountSummary { @Column(name = "total_interest_posted_derived", scale = 6, precision = 19) private BigDecimal totalInterestPosted; + @Column(name = "total_withdrawal_fees_derived", scale = 6, precision = 19) + private BigDecimal totalWithdrawalFees; + @Column(name = "account_balance_derived", scale = 6, precision = 19) private BigDecimal accountBalance = BigDecimal.ZERO; @@ -42,9 +45,10 @@ public final class SavingsAccountSummary { this.totalDeposits = wrapper.calculateTotalDeposits(currency, transactions); this.totalWithdrawals = wrapper.calculateTotalWithdrawals(currency, transactions); this.totalInterestPosted = wrapper.calculateTotalInterestPosted(currency, transactions); + this.totalWithdrawalFees = wrapper.calculateTotalWithdrawalFees(currency, transactions); this.accountBalance = Money.of(currency, this.totalDeposits).plus(this.totalInterestPosted).minus(this.totalWithdrawals) - .getAmount(); + .minus(this.totalWithdrawalFees).getAmount(); } public void updateFromInterestPeriodSummaries(final MonetaryCurrency currency, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java index 09529aeea..ac4915646 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java @@ -82,6 +82,12 @@ public final class SavingsAccountTransaction extends AbstractPersistable { isReversed); } + public static SavingsAccountTransaction fee(final SavingsAccount savingsAccount, final LocalDate date, final Money amount) { + final boolean isReversed = false; + return new SavingsAccountTransaction(savingsAccount, SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue(), date, amount, + isReversed); + } + private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Integer typeOf, final LocalDate transactionLocalDate, final Money amount, final boolean isReversed) { this.savingsAccount = savingsAccount; @@ -115,6 +121,10 @@ public final class SavingsAccountTransaction extends AbstractPersistable { return SavingsAccountTransactionType.fromInt(this.typeOf).isInterestPosting() && isNotReversed(); } + public boolean isWithdrawalFee() { + return SavingsAccountTransactionType.fromInt(this.typeOf).isWithdrawalFee() && isNotReversed(); + } + public boolean isNotReversed() { return !isReversed(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransactionSummaryWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransactionSummaryWrapper.java index 1f7d9e45a..a2297fa4e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransactionSummaryWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransactionSummaryWrapper.java @@ -43,4 +43,14 @@ public final class SavingsAccountTransactionSummaryWrapper { } return total.getAmountDefaultedToNullIfZero(); } + + public BigDecimal calculateTotalWithdrawalFees(final MonetaryCurrency currency, final List transactions) { + Money total = Money.zero(currency); + for (SavingsAccountTransaction transaction : transactions) { + if (transaction.isWithdrawalFee() && transaction.isNotReversed()) { + total = total.plus(transaction.getAmount(currency)); + } + } + return total.getAmountDefaultedToNullIfZero(); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransactionType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransactionType.java index 5a207fec7..1e89b6664 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransactionType.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransactionType.java @@ -14,7 +14,8 @@ public enum SavingsAccountTransactionType { INVALID(0, "savingsAccountTransactionType.invalid"), // DEPOSIT(1, "savingsAccountTransactionType.deposit"), // WITHDRAWAL(2, "savingsAccountTransactionType.withdrawal"), // - INTEREST_POSTING(3, "savingsAccountTransactionType.interestPosting"); + INTEREST_POSTING(3, "savingsAccountTransactionType.interestPosting"), // + WITHDRAWAL_FEE(4, "savingsAccountTransactionType.withdrawalFee"); private final Integer value; private final String code; @@ -47,6 +48,9 @@ public enum SavingsAccountTransactionType { case 3: loanTransactionType = SavingsAccountTransactionType.INTEREST_POSTING; break; + case 4: + loanTransactionType = SavingsAccountTransactionType.WITHDRAWAL_FEE; + break; } return loanTransactionType; } @@ -62,4 +66,8 @@ public enum SavingsAccountTransactionType { public boolean isInterestPosting() { return this.value.equals(SavingsAccountTransactionType.INTEREST_POSTING.getValue()); } + + public boolean isWithdrawalFee() { + return this.value.equals(SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue()); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java index 8df85e0b6..892026d40 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProduct.java @@ -18,6 +18,8 @@ import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockin import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.minRequiredOpeningBalanceParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nameParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nominalAnnualInterestRateParamName; +import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeAmountParamName; +import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeTypeParamName; import java.math.BigDecimal; import java.util.LinkedHashMap; @@ -29,9 +31,9 @@ import javax.persistence.Entity; import javax.persistence.Table; import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.exception.GeneralPlatformDomainRuleException; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; -import org.mifosplatform.portfolio.savings.exception.InvalidSavingsProductSettingsException; import org.springframework.data.jpa.domain.AbstractPersistable; @Entity @@ -88,15 +90,22 @@ public class SavingsProduct extends AbstractPersistable { @Column(name = "lockin_period_frequency_enum", nullable = true) private Integer lockinPeriodFrequencyType; + @Column(name = "withdrawal_fee_amount", scale = 6, precision = 19, nullable = true) + private BigDecimal withdrawalFeeAmount; + + @Column(name = "withdrawal_fee_type_enum", nullable = true) + private Integer withdrawalFeeType; + public static SavingsProduct createNew(final String name, final String description, final MonetaryCurrency currency, final BigDecimal interestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, - final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType) { + final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, + final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType) { return new SavingsProduct(name, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, - lockinPeriodFrequencyType); + lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType); } protected SavingsProduct() { @@ -108,7 +117,8 @@ public class SavingsProduct extends AbstractPersistable { final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, - final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType) { + final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, + final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType) { this.name = name; this.description = description; @@ -129,7 +139,13 @@ public class SavingsProduct extends AbstractPersistable { this.lockinPeriodFrequencyType = lockinPeriodFrequencyType.getValue(); } + this.withdrawalFeeAmount = withdrawalFeeAmount; + if (withdrawalFeeType != null) { + this.withdrawalFeeType = withdrawalFeeType.getValue(); + } + validateLockinDetails(); + validateWithdrawalFeeDetails(); } public MonetaryCurrency currency() { @@ -172,6 +188,18 @@ public class SavingsProduct extends AbstractPersistable { return type; } + public BigDecimal withdrawalFeeAmount() { + return this.withdrawalFeeAmount; + } + + public SavingsWithdrawalFeesType withdrawalFeeType() { + SavingsWithdrawalFeesType type = null; + if (this.withdrawalFeeType != null) { + type = SavingsWithdrawalFeesType.fromInt(this.withdrawalFeeType); + } + return type; + } + public Map update(final JsonCommand command) { final Map actualChanges = new LinkedHashMap(10); @@ -258,16 +286,43 @@ public class SavingsProduct extends AbstractPersistable { this.lockinPeriodFrequencyType = SavingsPeriodFrequencyType.fromInt(newValue).getValue(); } + if (command.isChangeInBigDecimalParameterNamed(withdrawalFeeAmountParamName, this.withdrawalFeeAmount)) { + final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(withdrawalFeeAmountParamName); + actualChanges.put(withdrawalFeeAmountParamName, newValue); + actualChanges.put(localeParamName, localeAsInput); + this.withdrawalFeeAmount = newValue; + } + + if (command.isChangeInIntegerParameterNamed(withdrawalFeeTypeParamName, this.withdrawalFeeType)) { + final Integer newValue = command.integerValueOfParameterNamed(withdrawalFeeTypeParamName); + actualChanges.put(withdrawalFeeTypeParamName, newValue); + this.withdrawalFeeType = SavingsWithdrawalFeesType.fromInt(newValue).getValue(); + } + validateLockinDetails(); + validateWithdrawalFeeDetails(); return actualChanges; } + private void validateWithdrawalFeeDetails() { + if (isInvalidConfigurationOfWithdrawalFeeSettings()) { + Object[] defaultUserMessageArgs = new Object[] { withdrawalFeeAmountParamName }; + throw new GeneralPlatformDomainRuleException("error.msg.product.savings.invalid.withdrawalfee.settings", + "Invalid configuration of withdrawal fee settings.", defaultUserMessageArgs); + } + } + + private boolean isInvalidConfigurationOfWithdrawalFeeSettings() { + return (this.withdrawalFeeAmount == null && this.withdrawalFeeType != null) + || (this.withdrawalFeeType == null && this.withdrawalFeeAmount != null); + } + private void validateLockinDetails() { if (isInvalidConfigurationOfLockinSettings()) { - // - throw new InvalidSavingsProductSettingsException("error.msg.product.savings.invalid.lockin.settings", - "Invalid configuration of lock in settings.", lockinPeriodFrequencyParamName); + Object[] defaultUserMessageArgs = new Object[] { lockinPeriodFrequencyParamName }; + throw new GeneralPlatformDomainRuleException("error.msg.product.savings.invalid.lockin.settings", + "Invalid configuration of lock in settings.", defaultUserMessageArgs); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductAssembler.java index 742072c5e..aa57dcc4c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductAssembler.java @@ -12,6 +12,8 @@ import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockin import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.minRequiredOpeningBalanceParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nameParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nominalAnnualInterestRateParamName; +import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeAmountParamName; +import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeTypeParamName; import java.math.BigDecimal; @@ -67,8 +69,15 @@ public class SavingsProductAssembler { lockinPeriodFrequencyType = SavingsPeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue); } + final BigDecimal withdrawalFeeAmount = command.bigDecimalValueOfParameterNamed(withdrawalFeeAmountParamName); + SavingsWithdrawalFeesType withdrawalFeeType = null; + final Integer withdrawalFeeTypeValue = command.integerValueOfParameterNamed(withdrawalFeeTypeParamName); + if (withdrawalFeeTypeValue != null) { + withdrawalFeeType = SavingsWithdrawalFeesType.fromInt(withdrawalFeeTypeValue); + } + return SavingsProduct.createNew(name, description, currency, interestRate, interestCompoundingPeriodType, - interestPostingPeriodType, interestCalculationType, - interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType); + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsWithdrawalFeesType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsWithdrawalFeesType.java new file mode 100644 index 000000000..3811dfc43 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsWithdrawalFeesType.java @@ -0,0 +1,43 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.savings.domain; + +public enum SavingsWithdrawalFeesType { + + INVALID(0, "savingsWithdrawalFeesType.invalid"), // + FLAT(1, "savingsWithdrawalFeesType.flat"), // + PERCENT_OF_AMOUNT(2, "savingsWithdrawalFeesType.percent.of.amount"); + + private final Integer value; + private final String code; + + private SavingsWithdrawalFeesType(final Integer value, final String code) { + this.value = value; + this.code = code; + } + + public Integer getValue() { + return this.value; + } + + public String getCode() { + return code; + } + + public static SavingsWithdrawalFeesType fromInt(final Integer type) { + + SavingsWithdrawalFeesType withdrawalFeeType = SavingsWithdrawalFeesType.INVALID; + switch (type) { + case 1: + withdrawalFeeType = FLAT; + break; + case 2: + withdrawalFeeType = PERCENT_OF_AMOUNT; + break; + } + return withdrawalFeeType; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/InvalidSavingsProductSettingsException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/InvalidSavingsProductSettingsException.java deleted file mode 100644 index 831718be7..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/exception/InvalidSavingsProductSettingsException.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.mifosplatform.portfolio.savings.exception; - -import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException; - -public class InvalidSavingsProductSettingsException extends AbstractPlatformDomainRuleException { - - public InvalidSavingsProductSettingsException(final String errorCode, final String defaultMessage, final String paramName) { - super(errorCode, defaultMessage, paramName); - } -} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java index d5c6b439d..4803fc33f 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java @@ -112,8 +112,11 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead sqlBuilder.append("sa.min_required_opening_balance as minRequiredOpeningBalance, "); sqlBuilder.append("sa.lockin_period_frequency as lockinPeriodFrequency,"); sqlBuilder.append("sa.lockin_period_frequency_enum as lockinPeriodFrequencyType, "); + sqlBuilder.append("sa.withdrawal_fee_amount as withdrawalFeeAmount,"); + sqlBuilder.append("sa.withdrawal_fee_type_enum as withdrawalFeeTypeEnum, "); sqlBuilder.append("sa.total_deposits_derived as totalDeposits, "); sqlBuilder.append("sa.total_withdrawals_derived as totalWithdrawals, "); + sqlBuilder.append("sa.total_withdrawal_fees_derived as totalWithdrawalFees, "); sqlBuilder.append("sa.total_interest_earned_derived as totalInterestEarned, "); sqlBuilder.append("sa.total_interest_posted_derived as totalInterestPosted, "); sqlBuilder.append("sa.account_balance_derived as accountBalance "); @@ -184,19 +187,29 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodType); } + final BigDecimal withdrawalFeeAmount = rs.getBigDecimal("withdrawalFeeAmount"); + + EnumOptionData withdrawalFeeType = null; + final Integer withdrawalFeeTypeValue = JdbcSupport.getInteger(rs, "withdrawalFeeTypeEnum"); + if (withdrawalFeeTypeValue != null) { + withdrawalFeeType = SavingsEnumerations.withdrawalFeeType(withdrawalFeeTypeValue); + } + final BigDecimal totalDeposits = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalDeposits"); final BigDecimal totalWithdrawals = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalWithdrawals"); + final BigDecimal totalWithdrawalFees = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalWithdrawalFees"); + final BigDecimal totalInterestEarned = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalInterestEarned"); final BigDecimal totalInterestPosted = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalInterestPosted"); final BigDecimal accountBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "accountBalance"); final SavingsAccountSummaryData summary = new SavingsAccountSummaryData(currency, totalDeposits, totalWithdrawals, - totalInterestEarned, totalInterestPosted, accountBalance); + totalWithdrawalFees, totalInterestEarned, totalInterestPosted, accountBalance); return SavingsAccountData.instance(id, accountNo, externalId, status, activationDate, groupId, groupName, clientId, clientName, productId, productName, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, - lockinPeriodFrequencyType, summary); + lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, summary); } } @@ -238,11 +251,14 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final Collection lockinPeriodFrequencyTypeOptions = this.dropdownReadPlatformService .retrieveLockinPeriodFrequencyTypeOptions(); + + final Collection withdrawalFeeTypeOptions = this.dropdownReadPlatformService.retrievewithdrawalFeeTypeOptions(); + final Collection transactions = null; template = SavingsAccountData.withTemplateOptions(template, productOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, - lockinPeriodFrequencyTypeOptions, transactions); + lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions); } else { String clientName = null; @@ -262,11 +278,13 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final Collection interestCalculationTypeOptions = null; final Collection interestCalculationDaysInYearTypeOptions = null; final Collection lockinPeriodFrequencyTypeOptions = null; + final Collection withdrawalFeeTypeOptions = null; + final Collection transactions = null; template = SavingsAccountData.withTemplateOptions(template, productOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, - lockinPeriodFrequencyTypeOptions, transactions); + lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions); } return template; @@ -404,8 +422,10 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead sqlBuilder.append("sp.interest_calculation_type_enum as interestCalculationType, "); sqlBuilder.append("sp.interest_calculation_days_in_year_type_enum as interestCalculationDaysInYearType, "); sqlBuilder.append("sp.min_required_opening_balance as minRequiredOpeningBalance, "); - sqlBuilder.append("sp.lockin_period_frequency as lockinPeriodFrequency, "); - sqlBuilder.append("sp.lockin_period_frequency_enum as lockinPeriodFrequencyType "); + sqlBuilder.append("sp.lockin_period_frequency as lockinPeriodFrequency,"); + sqlBuilder.append("sp.lockin_period_frequency_enum as lockinPeriodFrequencyType, "); + sqlBuilder.append("sp.withdrawal_fee_amount as withdrawalFeeAmount,"); + sqlBuilder.append("sp.withdrawal_fee_type_enum as withdrawalFeeTypeEnum "); sqlBuilder.append("from m_savings_product sp "); sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code "); @@ -456,6 +476,14 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodType); } + final BigDecimal withdrawalFeeAmount = rs.getBigDecimal("withdrawalFeeAmount"); + + EnumOptionData withdrawalFeeType = null; + final Integer withdrawalFeeTypeValue = JdbcSupport.getInteger(rs, "withdrawalFeeTypeEnum"); + if (withdrawalFeeTypeValue != null) { + withdrawalFeeType = SavingsEnumerations.withdrawalFeeType(withdrawalFeeTypeValue); + } + Long clientId = null; String clientName = null; if (client != null) { @@ -476,7 +504,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead return SavingsAccountData.instance(null, null, null, status, activationDate, groupId, groupName, clientId, clientName, productId, productName, currency, nominalAnnualIterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, - lockinPeriodFrequencyType, summary); + lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, summary); } } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java index 13c53a014..00ecddd65 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java @@ -137,7 +137,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi .build(); } catch (DataAccessException dve) { handleDataIntegrityIssues(command, dve); - return new CommandProcessingResult(Long.valueOf(-1)); + return CommandProcessingResult.empty(); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsDropdownReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsDropdownReadPlatformService.java index 181f7971f..047294cc3 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsDropdownReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsDropdownReadPlatformService.java @@ -15,4 +15,6 @@ public interface SavingsDropdownReadPlatformService { Collection retrieveInterestCalculationTypeOptions(); Collection retrieveInterestCalculationDaysInYearTypeOptions(); + + Collection retrievewithdrawalFeeTypeOptions(); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsDropdownReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsDropdownReadPlatformServiceImpl.java index cedf7ea6d..cc91bdc74 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsDropdownReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsDropdownReadPlatformServiceImpl.java @@ -10,11 +10,22 @@ import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationDays import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationType; import org.mifosplatform.portfolio.savings.domain.SavingsInterestPostingPeriodType; import org.mifosplatform.portfolio.savings.domain.SavingsPeriodFrequencyType; +import org.mifosplatform.portfolio.savings.domain.SavingsWithdrawalFeesType; import org.springframework.stereotype.Service; @Service public class SavingsDropdownReadPlatformServiceImpl implements SavingsDropdownReadPlatformService { + @Override + public Collection retrievewithdrawalFeeTypeOptions() { + List allowedOptions = Arrays.asList( // + SavingsEnumerations.withdrawalFeeType(SavingsWithdrawalFeesType.FLAT), // + SavingsEnumerations.withdrawalFeeType(SavingsWithdrawalFeesType.PERCENT_OF_AMOUNT) // + ); + + return allowedOptions; + } + @Override public List retrieveLockinPeriodFrequencyTypeOptions() { List allowedLockinPeriodFrequencyTypeOptions = Arrays.asList( // diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsEnumerations.java index c945bb30d..a63024a2e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsEnumerations.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsEnumerations.java @@ -15,6 +15,7 @@ import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationDays import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationType; import org.mifosplatform.portfolio.savings.domain.SavingsInterestPostingPeriodType; import org.mifosplatform.portfolio.savings.domain.SavingsPeriodFrequencyType; +import org.mifosplatform.portfolio.savings.domain.SavingsWithdrawalFeesType; public class SavingsEnumerations { @@ -75,6 +76,11 @@ public class SavingsEnumerations { optionData = new SavingsAccountTransactionEnumData(SavingsAccountTransactionType.INTEREST_POSTING.getValue().longValue(), SavingsAccountTransactionType.INTEREST_POSTING.getCode(), "Interest posting"); break; + case WITHDRAWAL_FEE: + optionData = new SavingsAccountTransactionEnumData(SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue().longValue(), + SavingsAccountTransactionType.WITHDRAWAL_FEE.getCode(), "Withdrawal fee"); + + break; } return optionData; } @@ -144,7 +150,7 @@ public class SavingsEnumerations { return optionData; } - public static EnumOptionData interestPeriodType(final Integer type) { + public static EnumOptionData compoundingInterestPeriodType(final Integer type) { return compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.fromInt(type)); } @@ -243,4 +249,28 @@ public class SavingsEnumerations { return optionData; } + + public static EnumOptionData withdrawalFeeType(final Integer type) { + return withdrawalFeeType(SavingsWithdrawalFeesType.fromInt(type)); + } + + public static EnumOptionData withdrawalFeeType(final SavingsWithdrawalFeesType type) { + EnumOptionData optionData = new EnumOptionData(SavingsWithdrawalFeesType.INVALID.getValue().longValue(), + SavingsWithdrawalFeesType.INVALID.getCode(), "Invalid"); + + switch (type) { + case INVALID: + break; + case FLAT: + optionData = new EnumOptionData(SavingsWithdrawalFeesType.FLAT.getValue().longValue(), + SavingsWithdrawalFeesType.FLAT.getCode(), "Flat"); + break; + case PERCENT_OF_AMOUNT: + optionData = new EnumOptionData(SavingsWithdrawalFeesType.PERCENT_OF_AMOUNT.getValue().longValue(), + SavingsWithdrawalFeesType.PERCENT_OF_AMOUNT.getCode(), "% of Amount"); + break; + } + + return optionData; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java index a0077aaf5..3a1ef3c48 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java @@ -16,11 +16,6 @@ import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSourc import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.portfolio.savings.data.SavingsProductData; -import org.mifosplatform.portfolio.savings.domain.SavingsCompoundingInterestPeriodType; -import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationDaysInYearType; -import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationType; -import org.mifosplatform.portfolio.savings.domain.SavingsInterestPostingPeriodType; -import org.mifosplatform.portfolio.savings.domain.SavingsPeriodFrequencyType; import org.mifosplatform.portfolio.savings.exception.SavingsProductNotFoundException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.EmptyResultDataAccessException; @@ -88,7 +83,9 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead sqlBuilder.append("sp.interest_calculation_days_in_year_type_enum as interestCalculationDaysInYearType, "); sqlBuilder.append("sp.min_required_opening_balance as minRequiredOpeningBalance, "); sqlBuilder.append("sp.lockin_period_frequency as lockinPeriodFrequency,"); - sqlBuilder.append("sp.lockin_period_frequency_enum as lockinPeriodFrequencyType "); + sqlBuilder.append("sp.lockin_period_frequency_enum as lockinPeriodFrequencyType, "); + sqlBuilder.append("sp.withdrawal_fee_amount as withdrawalFeeAmount,"); + sqlBuilder.append("sp.withdrawal_fee_type_enum as withdrawalFeeTypeEnum "); sqlBuilder.append("from m_savings_product sp "); sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code "); @@ -116,23 +113,21 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead currencyNameCode); final BigDecimal nominalAnnualInterestRate = rs.getBigDecimal("nominalAnnualInterestRate"); + final Integer compoundingInterestPeriodTypeValue = JdbcSupport.getInteger(rs, "compoundingInterestPeriodType"); EnumOptionData compoundingInterestPeriodType = SavingsEnumerations - .compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs, - "compoundingInterestPeriodType"))); + .compoundingInterestPeriodType(compoundingInterestPeriodTypeValue); - SavingsInterestPostingPeriodType interestPostingPeriod = SavingsInterestPostingPeriodType.fromInt(JdbcSupport.getInteger(rs, - "interestPostingPeriodType")); - EnumOptionData interestPostingPeriodType = SavingsEnumerations.interestPostingPeriodType(interestPostingPeriod); + final Integer interestPostingPeriodTypeValue = JdbcSupport.getInteger(rs, "interestPostingPeriodType"); + EnumOptionData interestPostingPeriodType = SavingsEnumerations.interestPostingPeriodType(interestPostingPeriodTypeValue); - EnumOptionData interestCalculationType = SavingsEnumerations.interestCalculationType(SavingsInterestCalculationType - .fromInt(JdbcSupport.getInteger(rs, "interestCalculationType"))); + final Integer interestCalculationTypeValue = JdbcSupport.getInteger(rs, "interestCalculationType"); + EnumOptionData interestCalculationType = SavingsEnumerations.interestCalculationType(interestCalculationTypeValue); EnumOptionData interestCalculationDaysInYearType = null; final Integer interestCalculationDaysInYearTypeValue = JdbcSupport.getInteger(rs, "interestCalculationDaysInYearType"); if (interestCalculationDaysInYearTypeValue != null) { interestCalculationDaysInYearType = SavingsEnumerations - .interestCalculationDaysInYearType(SavingsInterestCalculationDaysInYearType - .fromInt(interestCalculationDaysInYearTypeValue)); + .interestCalculationDaysInYearType(interestCalculationDaysInYearTypeValue); } final BigDecimal minRequiredOpeningBalance = rs.getBigDecimal("minRequiredOpeningBalance"); @@ -141,13 +136,20 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead EnumOptionData lockinPeriodFrequencyType = null; final Integer lockinPeriodFrequencyTypeValue = JdbcSupport.getInteger(rs, "lockinPeriodFrequencyType"); if (lockinPeriodFrequencyTypeValue != null) { - lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(SavingsPeriodFrequencyType - .fromInt(lockinPeriodFrequencyTypeValue)); + lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodFrequencyTypeValue); + } + + final BigDecimal withdrawalFeeAmount = rs.getBigDecimal("withdrawalFeeAmount"); + + EnumOptionData withdrawalFeeType = null; + final Integer withdrawalFeeTypeValue = JdbcSupport.getInteger(rs, "withdrawalFeeTypeEnum"); + if (withdrawalFeeTypeValue != null) { + withdrawalFeeType = SavingsEnumerations.withdrawalFeeType(withdrawalFeeTypeValue); } return SavingsProductData.instance(id, name, description, currency, nominalAnnualInterestRate, compoundingInterestPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, - lockinPeriodFrequency, lockinPeriodFrequencyType); + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java index ad5dc062c..0c3eb5dbf 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java @@ -72,8 +72,6 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi public CommandProcessingResult create(final JsonCommand command) { try { - this.context.authenticatedUser(); - this.fromApiJsonDataValidator.validateForCreate(command.json()); final SavingsProduct product = this.savingsProductAssembler.assemble(command);