From 0694dde72fd926c365bf643fed729b0045436861 Mon Sep 17 00:00:00 2001 From: ashok-conflux Date: Fri, 27 Sep 2013 17:19:46 +0530 Subject: [PATCH] Move withdrawal and annual fee charges to charges workflow --- ..._move_withdrawal_annual_fee_to_charges.sql | 25 + .../SynchronousCommandProcessingService.java | 4 +- .../core/serialization/FromJsonHelper.java | 9 + .../core/service/DateUtils.java | 4 +- .../portfolio/charge/data/ChargeData.java | 4 +- .../portfolio/charge/domain/Charge.java | 10 + .../charge/domain/ChargeCalculationType.java | 2 +- .../charge/domain/ChargeTimeType.java | 42 +- ...ChargeDropdownReadPlatformServiceImpl.java | 23 +- .../charge/service/ChargeEnumerations.java | 14 +- .../SavingsAccountTransactionType.java | 8 +- .../savings/SavingsApiConstants.java | 30 +- .../api/SavingsAccountsApiResource.java | 10 +- .../data/SavingsAccountChargeData.java | 24 +- .../SavingsAccountChargeDataValidator.java | 9 +- .../savings/data/SavingsAccountData.java | 145 ++++-- .../data/SavingsAccountDataValidator.java | 109 ++-- .../savings/data/SavingsProductData.java | 74 +-- .../data/SavingsProductDataValidator.java | 54 +- .../savings/domain/SavingsAccount.java | 475 ++++++------------ .../domain/SavingsAccountAssembler.java | 40 +- .../savings/domain/SavingsAccountCharge.java | 173 +++++-- .../domain/SavingsAccountChargeAssembler.java | 33 +- .../domain/SavingsAccountTransaction.java | 22 +- .../savings/domain/SavingsProduct.java | 132 ++--- .../domain/SavingsProductAssembler.java | 18 +- .../domain/SavingsProductChargeAssembler.java | 9 +- ...AnnualFeeSavingsAccountCommandHandler.java | 5 +- ...sAccountChargeReadPlatformServiceImpl.java | 17 +- .../SavingsAccountReadPlatformService.java | 3 +- ...SavingsAccountReadPlatformServiceImpl.java | 66 ++- .../SavingsAccountWritePlatformService.java | 2 +- ...WritePlatformServiceJpaRepositoryImpl.java | 42 +- ...SavingsProductReadPlatformServiceImpl.java | 22 +- .../ScheduledJobRunnerServiceImpl.java | 12 +- 35 files changed, 812 insertions(+), 859 deletions(-) create mode 100644 mifosng-db/migrations/core_db/V118_7__move_withdrawal_annual_fee_to_charges.sql diff --git a/mifosng-db/migrations/core_db/V118_7__move_withdrawal_annual_fee_to_charges.sql b/mifosng-db/migrations/core_db/V118_7__move_withdrawal_annual_fee_to_charges.sql new file mode 100644 index 000000000..791f391ef --- /dev/null +++ b/mifosng-db/migrations/core_db/V118_7__move_withdrawal_annual_fee_to_charges.sql @@ -0,0 +1,25 @@ +ALTER TABLE `m_savings_account_charge` + ADD COLUMN `is_active` TINYINT(1) NOT NULL DEFAULT '1' AFTER `waived`; + +ALTER TABLE `m_savings_account` + DROP COLUMN `withdrawal_fee_amount`, + DROP COLUMN `withdrawal_fee_type_enum`; + +ALTER TABLE `m_savings_account_charge` + ADD COLUMN `fee_on_month` SMALLINT(5) NULL DEFAULT NULL AFTER `due_for_collection_as_of_date`, + ADD COLUMN `fee_on_day` SMALLINT(5) NULL DEFAULT NULL AFTER `fee_on_month`; + +ALTER TABLE `m_savings_account_charge` + CHANGE COLUMN `due_for_collection_as_of_date` `charge_due_date` DATE NULL DEFAULT NULL AFTER `charge_time_enum`; + + +ALTER TABLE `m_savings_account` + DROP COLUMN `annual_fee_amount`, + DROP COLUMN `annual_fee_on_month`, + DROP COLUMN `annual_fee_on_day`, + DROP COLUMN `annual_fee_next_due_date`; + +ALTER TABLE `m_savings_product` + DROP COLUMN `annual_fee_amount`, + DROP COLUMN `annual_fee_on_month`, + DROP COLUMN `annual_fee_on_day`; \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java index 1940a4322..9cd185eac 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java @@ -419,9 +419,9 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer handler = this.applicationContext.getBean("calculateInterestSavingsAccountCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isSavingsAccountInterestPosting()) { handler = this.applicationContext.getBean("postInterestSavingsAccountCommandHandler", NewCommandSourceHandler.class); - } else if (wrapper.isSavingsAccountApplyAnnualFee()) { + } /*else if (wrapper.isSavingsAccountApplyAnnualFee()) { handler = this.applicationContext.getBean("applyAnnualFeeSavingsAccountCommandHandler", NewCommandSourceHandler.class); - } else if (wrapper.isSavingsAccountUndoTransaction()) { + } */else if (wrapper.isSavingsAccountUndoTransaction()) { handler = this.applicationContext.getBean("undoTransactionSavingsAccountCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isAdjustSavingsAccountTransaction()) { handler = this.applicationContext.getBean("savingsTransactionAdjustmentCommandHandler", NewCommandSourceHandler.class); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/serialization/FromJsonHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/serialization/FromJsonHelper.java index 19c8d8766..4d0cc7c58 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/serialization/FromJsonHelper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/serialization/FromJsonHelper.java @@ -149,6 +149,11 @@ public class FromJsonHelper { public MonthDay extractMonthDayNamed(final String parameterName, final JsonElement element) { return this.helperDelegator.extractMonthDayNamed(parameterName, element); } + + public MonthDay extractMonthDayNamed(final String parameterName, final JsonObject object, final String dateFormat, + final Locale clientApplicationLocale) { + return this.helperDelegator.extractMonthDayNamed(parameterName, object, dateFormat, clientApplicationLocale); + } public LocalDate extractLocalDateNamed(final String parameterName, final JsonElement element) { return this.helperDelegator.extractLocalDateNamed(parameterName, element, new HashSet()); @@ -216,4 +221,8 @@ public class FromJsonHelper { public String extractDateFormatParameter(final JsonObject element) { return this.helperDelegator.extractDateFormatParameter(element); } + + public String extractMonthDayFormatParameter(final JsonObject element) { + return this.helperDelegator.extractMonthDayFormatParameter(element); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DateUtils.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DateUtils.java index 6643de289..fefaaa2ec 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DateUtils.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DateUtils.java @@ -64,7 +64,7 @@ public class DateUtils { return formattedSqlDate; } - public static boolean isInFuture(final LocalDate date){ - return date.isAfter(getLocalDateOfTenant()); + public static boolean isDateInTheFuture(final LocalDate localDate){ + return localDate.isAfter(getLocalDateOfTenant()); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/data/ChargeData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/data/ChargeData.java index 64252a5a3..77de17e18 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/data/ChargeData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/data/ChargeData.java @@ -11,6 +11,7 @@ import java.util.Collection; import java.util.List; import org.joda.time.LocalDate; +import org.joda.time.MonthDay; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData; @@ -176,9 +177,10 @@ public class ChargeData implements Comparable, Serializable { final BigDecimal amountPercentageAppliedTo = BigDecimal.ZERO; final Collection chargeOptions = null; final LocalDate dueAsOfDate = null; + final MonthDay feeOnMonthDay = null; return SavingsAccountChargeData.instance(savingsChargeId, this.id, this.name, this.currency, this.amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding, this.chargeTimeType, dueAsOfDate, this.chargeCalculationType, percentage, - amountPercentageAppliedTo, chargeOptions, this.penalty); + amountPercentageAppliedTo, chargeOptions, this.penalty, feeOnMonthDay); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/Charge.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/Charge.java index 0562aacb0..f9859a3be 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/Charge.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/Charge.java @@ -114,6 +114,11 @@ public class Charge extends AbstractPersistable { baseDataValidator.reset().parameter("chargeCalculationType").value(this.chargeCalculation) .failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.calculation.type.for.savings"); } + + if (!ChargeTimeType.fromInt(getChargeTime()).isWithdrawalFee() && ChargeCalculationType.fromInt(getChargeCalculation()).isPercentageOfAmount()) { + baseDataValidator.reset().parameter("chargeCalculationType").value(this.chargeCalculation) + .failWithCodeNoParameterAddedToErrorCode("charge.calculation.type.percentage.allowed.only.for.withdrawal"); + } } else if (isLoanCharge()) { @@ -277,6 +282,11 @@ public class Charge extends AbstractPersistable { baseDataValidator.reset().parameter("chargeCalculationType").value(this.chargeCalculation) .failWithCodeNoParameterAddedToErrorCode("not.allowed.charge.calculation.type.for.savings"); } + + if (!ChargeTimeType.fromInt(getChargeTime()).isWithdrawalFee() && ChargeCalculationType.fromInt(getChargeCalculation()).isPercentageOfAmount()) { + baseDataValidator.reset().parameter("chargeCalculationType").value(this.chargeCalculation) + .failWithCodeNoParameterAddedToErrorCode("charge.calculation.type.percentage.allowed.only.for.withdrawal"); + } } } 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 22a107ff9..7d2689f73 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 @@ -57,6 +57,6 @@ public enum ChargeCalculationType { } public boolean isAllowedSavingsChargeCalculationType() { - return isFlat(); + return isFlat() || isPercentageOfAmount(); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeTimeType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeTimeType.java index e79085c51..15f64fbd0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeTimeType.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeTimeType.java @@ -8,8 +8,12 @@ package org.mifosplatform.portfolio.charge.domain; public enum ChargeTimeType { INVALID(0, "chargeTimeType.invalid"), // - DISBURSEMENT(1, "chargeTimeType.disbursement"), // - SPECIFIED_DUE_DATE(2, "chargeTimeType.specifiedDueDate"), MONTHLY(3, "chargeTimeType.monthly"), YEARLY(4, "chargeTimeType.yearly"); + DISBURSEMENT(1, "chargeTimeType.disbursement"), //only for loan charges + SPECIFIED_DUE_DATE(2, "chargeTimeType.specifiedDueDate"), // for loan and savings charges + SAVINGS_ACTIVATION(3, "chargeTimeType.savingsActivation"), // only for savings + SAVINGS_CLOSURE(4, "chargeTimeType.savingsClosure"), // only for savings + WITHDRAWAL_FEE(5, "chargeTimeType.withdrawalFee"), // only for savings + ANNUAL_FEE(6, "chargeTimeType.annualFee"); // only for savings private final Integer value; private final String code; @@ -32,7 +36,11 @@ public enum ChargeTimeType { } public static int maxValue() { - return 4; + int max = 1;// Default it to one (1) + for (ChargeTimeType chargeTimeType : ChargeTimeType.values()) { + max = (chargeTimeType.getValue() > max) ? chargeTimeType.getValue() : max; + } + return max; } public static ChargeTimeType fromInt(final Integer chargeTime) { @@ -45,10 +53,16 @@ public enum ChargeTimeType { chargeTimeType = SPECIFIED_DUE_DATE; break; case 3: - chargeTimeType = MONTHLY; + chargeTimeType = SAVINGS_ACTIVATION; break; case 4: - chargeTimeType = YEARLY; + chargeTimeType = SAVINGS_CLOSURE; + break; + case 5: + chargeTimeType = WITHDRAWAL_FEE; + break; + case 6: + chargeTimeType = ANNUAL_FEE; break; default: chargeTimeType = INVALID; @@ -65,12 +79,20 @@ public enum ChargeTimeType { return this.value.equals(ChargeTimeType.SPECIFIED_DUE_DATE.getValue()); } - public boolean isMonthly() { - return this.value.equals(ChargeTimeType.MONTHLY.getValue()); + public boolean isSavingsActivation() { + return this.value.equals(ChargeTimeType.SAVINGS_ACTIVATION.getValue()); } - public boolean isYearly() { - return this.value.equals(ChargeTimeType.YEARLY.getValue()); + public boolean isSavingsClosure() { + return this.value.equals(ChargeTimeType.SAVINGS_CLOSURE.getValue()); + } + + public boolean isWithdrawalFee() { + return this.value.equals(ChargeTimeType.WITHDRAWAL_FEE.getValue()); + } + + public boolean isAnnualFee() { + return this.value.equals(ChargeTimeType.ANNUAL_FEE.getValue()); } public boolean isAllowedLoanChargeTime() { @@ -78,6 +100,6 @@ public enum ChargeTimeType { } public boolean isAllowedSavingsChargeTime() { - return isOnSpecifiedDueDate() || isMonthly() || isYearly(); + return isOnSpecifiedDueDate() || isSavingsActivation() || isSavingsClosure() || isWithdrawalFee() || isAnnualFee(); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeDropdownReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeDropdownReadPlatformServiceImpl.java index 0114eb0eb..41d761495 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeDropdownReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeDropdownReadPlatformServiceImpl.java @@ -49,15 +49,7 @@ public class ChargeDropdownReadPlatformServiceImpl implements ChargeDropdownRead public List retrieveCollectionTimeTypes() { final List chargeTimeTypes = new ArrayList(); for (final ChargeTimeType chargeTimeType : ChargeTimeType.values()) { - if (ChargeTimeType.INVALID.equals(chargeTimeType) || ChargeTimeType.MONTHLY.equals(chargeTimeType) // To - // be - // implemented - // for - // Savings - || ChargeTimeType.YEARLY.equals(chargeTimeType)) { - // implemented - // for - // Savings + if (ChargeTimeType.INVALID.equals(chargeTimeType)) { continue; } chargeTimeTypes.add(ChargeEnumerations.chargeTimeType(chargeTimeType)); @@ -82,17 +74,14 @@ public class ChargeDropdownReadPlatformServiceImpl implements ChargeDropdownRead @Override public List retrieveSavingsCalculationTypes() { - return Arrays.asList(chargeCalculationType(ChargeCalculationType.FLAT) - //Enable when percentage of amount is supported for savings charges - //, chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT) - ); + return Arrays.asList(chargeCalculationType(ChargeCalculationType.FLAT), + chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT)); } @Override public List retrieveSavingsCollectionTimeTypes() { - return Arrays.asList(chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE) - //enable when support is added for recurring Monthly and yearly charges - //, chargeTimeType(ChargeTimeType.MONTHLY), chargeTimeType(ChargeTimeType.YEARLY) - ); + return Arrays.asList(chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE), chargeTimeType(ChargeTimeType.SAVINGS_ACTIVATION), + chargeTimeType(ChargeTimeType.SAVINGS_CLOSURE), chargeTimeType(ChargeTimeType.WITHDRAWAL_FEE), + chargeTimeType(ChargeTimeType.ANNUAL_FEE)); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeEnumerations.java index b5da02db2..61f71acd4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeEnumerations.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeEnumerations.java @@ -28,11 +28,17 @@ public class ChargeEnumerations { optionData = new EnumOptionData(ChargeTimeType.SPECIFIED_DUE_DATE.getValue().longValue(), ChargeTimeType.SPECIFIED_DUE_DATE.getCode(), "Specified due date"); break; - case MONTHLY: - optionData = new EnumOptionData(ChargeTimeType.MONTHLY.getValue().longValue(), ChargeTimeType.MONTHLY.getCode(), "Monthly"); + case SAVINGS_ACTIVATION: + optionData = new EnumOptionData(ChargeTimeType.SAVINGS_ACTIVATION.getValue().longValue(), ChargeTimeType.SAVINGS_ACTIVATION.getCode(), "Savings Activation"); break; - case YEARLY: - optionData = new EnumOptionData(ChargeTimeType.YEARLY.getValue().longValue(), ChargeTimeType.YEARLY.getCode(), "Yearly"); + case SAVINGS_CLOSURE: + optionData = new EnumOptionData(ChargeTimeType.SAVINGS_CLOSURE.getValue().longValue(), ChargeTimeType.SAVINGS_CLOSURE.getCode(), "Savings Closure"); + break; + case WITHDRAWAL_FEE: + optionData = new EnumOptionData(ChargeTimeType.WITHDRAWAL_FEE.getValue().longValue(), ChargeTimeType.WITHDRAWAL_FEE.getCode(), "Withdrawal Fee"); + break; + case ANNUAL_FEE: + optionData = new EnumOptionData(ChargeTimeType.ANNUAL_FEE.getValue().longValue(), ChargeTimeType.ANNUAL_FEE.getCode(), "Annual Fee"); break; default: optionData = new EnumOptionData(ChargeTimeType.INVALID.getValue().longValue(), ChargeTimeType.INVALID.getCode(), "Invalid"); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsAccountTransactionType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsAccountTransactionType.java index 74dfd4acb..4cb3b6105 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsAccountTransactionType.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsAccountTransactionType.java @@ -105,9 +105,13 @@ public enum SavingsAccountTransactionType { return this.value.equals(SavingsAccountTransactionType.ANNUAL_FEE.getValue()); } - public boolean isCharge() { + public boolean isPayCharge() { return this.value.equals(SavingsAccountTransactionType.PAY_CHARGE.getValue()); } + + public boolean isChargeTransaction(){ + return isPayCharge() || isWithdrawalFee() || isAnnualFee(); + } public boolean isWaiveCharge() { return this.value.equals(SavingsAccountTransactionType.WAIVE_CHARGES.getValue()); @@ -130,7 +134,7 @@ public enum SavingsAccountTransactionType { } public boolean isDebit() { - return isWithdrawal() || isWithdrawalFee() || isAnnualFee() || isCharge(); + return isWithdrawal() || isWithdrawalFee() || isAnnualFee() || isPayCharge(); } public boolean isCredit() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java index fb1fd6d77..bdab0a9b9 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java @@ -81,8 +81,8 @@ public class SavingsApiConstants { public static final String withdrawalFeeAmountParamName = "withdrawalFeeAmount"; public static final String withdrawalFeeTypeParamName = "withdrawalFeeType"; public static final String withdrawalFeeForTransfersParamName = "withdrawalFeeForTransfers"; - public static final String annualFeeAmountParamName = "annualFeeAmount"; - public static final String annualFeeOnMonthDayParamName = "annualFeeOnMonthDay"; + public static final String feeAmountParamName = "feeAmount";//to be deleted + public static final String feeOnMonthDayParamName = "feeOnMonthDay"; public static final String accountingRuleParamName = "accountingRule"; public static final String paymentTypeIdParamName = "paymentTypeId"; public static final String transactionAccountNumberParamName = "accountNumber"; @@ -118,16 +118,22 @@ public class SavingsApiConstants { public static final String amountParamName = "amount"; public static final String amountPaidParamName = "amountPaid"; public static final String chargeOptionsParamName = "chargeOptions"; + public static final String chargePaymentModeParamName = "chargePaymentMode"; public static final String noteParamName = "note"; + + //Savings account associations + public static final String transactions = "transactions"; + public static final String charges = "charges"; + public static final Set SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, monthDayFormatParamName, nameParamName, descriptionParamName, currencyCodeParamName, digitsAfterDecimalParamName, inMultiplesOfParamName, nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, - withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, withdrawalFeeForTransfersParamName, annualFeeAmountParamName, - annualFeeOnMonthDayParamName, accountingRuleParamName, chargesParamName, + withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, withdrawalFeeForTransfersParamName, feeAmountParamName, + feeOnMonthDayParamName, accountingRuleParamName, chargesParamName, SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_SAVINGS.getValue(), @@ -147,7 +153,7 @@ public class SavingsApiConstants { nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, - withdrawalFeeForTransfersParamName, annualFeeAmountParamName, annualFeeOnMonthDayParamName, "currencyOptions", + withdrawalFeeForTransfersParamName, feeAmountParamName, feeOnMonthDayParamName, "currencyOptions", "interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions", "interestCalculationTypeOptions", "interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions")); @@ -156,8 +162,10 @@ public class SavingsApiConstants { productIdParamName, fieldOfficerIdParamName, submittedOnDateParamName, nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, - lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, - withdrawalFeeForTransfersParamName, annualFeeAmountParamName, annualFeeOnMonthDayParamName, chargesParamName)); + lockinPeriodFrequencyTypeParamName, + //withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, + //withdrawalFeeForTransfersParamName, + feeAmountParamName, feeOnMonthDayParamName, chargesParamName)); /** * These parameters will match the class level parameters of @@ -169,10 +177,10 @@ public class SavingsApiConstants { groupIdParamName, "groupName", "savingsProductId", "savingsProductName", "currency", nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, - withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, withdrawalFeeForTransfersParamName, annualFeeAmountParamName, - annualFeeOnMonthDayParamName, "summary", "transactions", "productOptions", "interestCompoundingPeriodTypeOptions", + withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, withdrawalFeeForTransfersParamName, feeAmountParamName, + feeOnMonthDayParamName, "summary", "transactions", "productOptions", "interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions", "interestCalculationTypeOptions", "interestCalculationDaysInYearTypeOptions", - "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions")); + "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions", "withdrawalFee", "annualFee")); public static final Set SAVINGS_ACCOUNT_TRANSACTION_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList( localeParamName, dateFormatParamName, transactionDateParamName, transactionAmountParamName, paymentTypeIdParamName, @@ -195,7 +203,7 @@ public class SavingsApiConstants { amountPaidParamName, chargeOptionsParamName)); public static final Set SAVINGS_ACCOUNT_CHARGES_ADD_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList( - chargeIdParamName, amountParamName, dueAsOfDateParamName, dateFormatParamName, localeParamName)); + chargeIdParamName, amountParamName, dueAsOfDateParamName, dateFormatParamName, localeParamName, feeOnMonthDayParamName, monthDayFormatParamName)); public static final Set SAVINGS_ACCOUNT_CHARGES_PAY_CHARGE_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList( amountParamName, dueAsOfDateParamName, dateFormatParamName, localeParamName)); 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 2834c5946..a98d147b6 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 @@ -154,11 +154,11 @@ public class SavingsAccountsApiResource { if (!associationParameters.isEmpty()) { if (associationParameters.contains("all")) { - associationParameters.addAll(Arrays.asList("transactions", "charges")); + associationParameters.addAll(Arrays.asList(SavingsApiConstants.transactions, SavingsApiConstants.charges)); } - if (associationParameters.contains("transactions")) { - mandatoryResponseParameters.add("transactions"); + if (associationParameters.contains(SavingsApiConstants.transactions)) { + mandatoryResponseParameters.add(SavingsApiConstants.transactions); final Collection currentTransactions = this.savingsAccountReadPlatformService .retrieveAllTransactions(accountId); if (!CollectionUtils.isEmpty(currentTransactions)) { @@ -166,8 +166,8 @@ public class SavingsAccountsApiResource { } } - if (associationParameters.contains("charges")) { - mandatoryResponseParameters.add("charges"); + if (associationParameters.contains(SavingsApiConstants.charges)) { + mandatoryResponseParameters.add(SavingsApiConstants.charges); final Collection currentCharges = this.savingsAccountChargeReadPlatformService .retrieveSavingsAccountCharges(accountId); if (!CollectionUtils.isEmpty(currentCharges)) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountChargeData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountChargeData.java index d7db3d3f9..351e37c1f 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountChargeData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountChargeData.java @@ -9,9 +9,11 @@ import java.math.BigDecimal; import java.util.Collection; import org.joda.time.LocalDate; +import org.joda.time.MonthDay; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.portfolio.charge.data.ChargeData; +import org.mifosplatform.portfolio.charge.domain.ChargeTimeType; /** * Immutable data object for Savings Account charge data. @@ -27,12 +29,14 @@ public class SavingsAccountChargeData { @SuppressWarnings("unused") private final String name; - @SuppressWarnings("unused") private final EnumOptionData chargeTimeType; @SuppressWarnings("unused") private final LocalDate dueDate; + @SuppressWarnings("unused") + private final MonthDay feeOnMonthDay; + private final EnumOptionData chargeCalculationType; private final BigDecimal percentage; @@ -82,28 +86,30 @@ public class SavingsAccountChargeData { final BigDecimal percentage = BigDecimal.ZERO; final boolean penalty = false; final LocalDate dueAsOfDate = null; + final MonthDay feeOnMonthDay = null; + return new SavingsAccountChargeData(id, chargeId, name, chargeTimeType, dueAsOfDate, chargeCalculationType, percentage, amountPercentageAppliedTo, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding, chargeOptions, - penalty); + penalty, feeOnMonthDay); } public static SavingsAccountChargeData instance(final Long id, final Long chargeId, final String name, final CurrencyData currency, final BigDecimal amount, final BigDecimal amountPaid, final BigDecimal amountWaived, final BigDecimal amountWrittenOff, final BigDecimal amountOutstanding, final EnumOptionData chargeTimeType, final LocalDate dueAsOfDate, final EnumOptionData chargeCalculationType, final BigDecimal percentage, final BigDecimal amountPercentageAppliedTo, - final Collection chargeOptions, final boolean penalty) { + final Collection chargeOptions, final boolean penalty, final MonthDay feeOnMonthDay) { return new SavingsAccountChargeData(id, chargeId, name, chargeTimeType, dueAsOfDate, chargeCalculationType, percentage, amountPercentageAppliedTo, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding, chargeOptions, - penalty); + penalty, feeOnMonthDay); } private SavingsAccountChargeData(final Long id, final Long chargeId, final String name, final EnumOptionData chargeTimeType, final LocalDate dueAsOfDate, final EnumOptionData chargeCalculationType, final BigDecimal percentage, final BigDecimal amountPercentageAppliedTo, final CurrencyData currency, final BigDecimal amount, final BigDecimal amountPaid, final BigDecimal amountWaived, final BigDecimal amountWrittenOff, final BigDecimal amountOutstanding, - final Collection chargeOptions, final boolean penalty) { + final Collection chargeOptions, final boolean penalty, final MonthDay feeOnMonthDay) { this.id = id; this.chargeId = chargeId; this.name = name; @@ -121,10 +127,18 @@ public class SavingsAccountChargeData { this.amountOrPercentage = getAmountOrPercentage(); this.chargeOptions = chargeOptions; this.penalty = penalty; + this.feeOnMonthDay = feeOnMonthDay; } private BigDecimal getAmountOrPercentage() { return (this.chargeCalculationType != null) && (this.chargeCalculationType.getId().intValue() > 1) ? this.percentage : this.amount; } + public boolean isWithdrawalFee(){ + return ChargeTimeType.fromInt(this.chargeTimeType.getId().intValue()).isWithdrawalFee(); + } + + public boolean isAnnualFee(){ + return ChargeTimeType.fromInt(this.chargeTimeType.getId().intValue()).isAnnualFee(); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountChargeDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountChargeDataValidator.java index 50161bfed..9cfc4da44 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountChargeDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountChargeDataValidator.java @@ -11,6 +11,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_AC import static org.mifosplatform.portfolio.savings.SavingsApiConstants.amountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName; import java.lang.reflect.Type; import java.math.BigDecimal; @@ -20,6 +21,7 @@ import java.util.Map; import org.apache.commons.lang.StringUtils; import org.joda.time.LocalDate; +import org.joda.time.MonthDay; import org.mifosplatform.infrastructure.core.data.ApiParameterError; import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; @@ -62,7 +64,12 @@ public class SavingsAccountChargeDataValidator { final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed(dueAsOfDateParamName, element); baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate).notNull(); } - + + if (this.fromApiJsonHelper.parameterExists(feeOnMonthDayParamName, element)) { + final MonthDay monthDay = this.fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, element); + baseDataValidator.reset().parameter(feeOnMonthDayParamName).value(monthDay).notNull(); + } + throwExceptionIfValidationWarningsExist(dataValidationErrors); } 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 3e9b2d7de..39d0e179a 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 @@ -7,11 +7,10 @@ package org.mifosplatform.portfolio.savings.data; import java.math.BigDecimal; import java.util.Collection; +import java.util.HashSet; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; -import org.joda.time.LocalDate; -import org.joda.time.MonthDay; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.staff.data.StaffData; @@ -44,18 +43,13 @@ public class SavingsAccountData { private final BigDecimal minRequiredOpeningBalance; private final Integer lockinPeriodFrequency; private final EnumOptionData lockinPeriodFrequencyType; - private final BigDecimal withdrawalFeeAmount; - private final EnumOptionData withdrawalFeeType; private final boolean withdrawalFeeForTransfers; - private final BigDecimal annualFeeAmount; - private final MonthDay annualFeeOnMonthDay; - private final LocalDate annualFeeNextDueDate; // associations private final SavingsAccountSummaryData summary; @SuppressWarnings("unused") private final Collection transactions; - @SuppressWarnings("unused") + private final Collection charges; // template @@ -69,6 +63,11 @@ public class SavingsAccountData { private final Collection withdrawalFeeTypeOptions; private final Collection chargeOptions; + @SuppressWarnings("unused") + private final SavingsAccountChargeData withdrawalFee; + @SuppressWarnings("unused") + private final SavingsAccountChargeData annualFee; + public static SavingsAccountData instance(final Long id, final String accountNo, final String externalId, final Long groupId, final String groupName, final Long clientId, final String clientName, final Long productId, final String productName, final Long fieldOfficerId, final String fieldOfficerName, final SavingsAccountStatusEnumData status, @@ -76,9 +75,7 @@ public class SavingsAccountData { final EnumOptionData interestCompoundingPeriodType, final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, - final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount, final EnumOptionData withdrawalFeeType, - final boolean withdrawalFeeForTransfers, final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay, - final LocalDate annualFeeNextDueDate, final SavingsAccountSummaryData summary) { + final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers, final SavingsAccountSummaryData summary) { final Collection productOptions = null; final Collection fieldOfficerOptions = null; @@ -96,15 +93,44 @@ public class SavingsAccountData { return new SavingsAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, - lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, withdrawalFeeForTransfers, - annualFeeAmount, annualFeeOnMonthDay, annualFeeNextDueDate, summary, transactions, productOptions, fieldOfficerOptions, - interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, - interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, - chargeOptions); + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions, + fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, + interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, + withdrawalFeeTypeOptions, charges, chargeOptions); } public static SavingsAccountData lookup(final Long accountId, final String accountNo) { + final String externalId = null; + final Long productId = null; + final Long groupId = null; + final Long clientId = null; + final String clientName = null; + final String groupName = null; + final String productName = null; + final Long fieldOfficerId = null; + final String fieldOfficerName = null; + final SavingsAccountStatusEnumData status = null; + final SavingsAccountApplicationTimelineData timeline = null; + final CurrencyData currency = null; + final BigDecimal nominalAnnualInterestRate = null; + final EnumOptionData interestPeriodType = null; + final EnumOptionData interestPostingPeriodType = null; + final EnumOptionData interestCalculationType = null; + final EnumOptionData interestCalculationDaysInYearType = null; + final BigDecimal minRequiredOpeningBalance = null; + final Integer lockinPeriodFrequency = null; + final EnumOptionData lockinPeriodFrequencyType = null; +// final BigDecimal withdrawalFeeAmount = null; +// final EnumOptionData withdrawalFeeType = null; + final boolean withdrawalFeeForTransfers = false; +// final BigDecimal annualFeeAmount = null; +// final MonthDay annualFeeOnMonthDay = null; +// final LocalDate annualFeeNextDueDate = null; + + final SavingsAccountSummaryData summary = null; + final Collection transactions = null; + final Collection productOptions = null; final Collection fieldOfficerOptions = null; final Collection interestCompoundingPeriodTypeOptions = null; @@ -114,12 +140,13 @@ public class SavingsAccountData { final Collection lockinPeriodFrequencyTypeOptions = null; final Collection withdrawalFeeTypeOptions = null; - final Collection transactions = null; final Collection charges = null; final Collection chargeOptions = null; - return new SavingsAccountData(accountId, accountNo, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, false, null, null, null, null, transactions, productOptions, + return new SavingsAccountData(accountId, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, + fieldOfficerId , fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType, + interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions); @@ -149,12 +176,11 @@ public class SavingsAccountData { account.fieldOfficerName, account.status, account.timeline, account.currency, account.nominalAnnualInterestRate, account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType, account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency, - account.lockinPeriodFrequencyType, account.withdrawalFeeAmount, account.withdrawalFeeType, - account.withdrawalFeeForTransfers, account.annualFeeAmount, account.annualFeeOnMonthDay, account.annualFeeNextDueDate, - account.summary, transactions, template.productOptions, template.fieldOfficerOptions, - template.interestCompoundingPeriodTypeOptions, template.interestPostingPeriodTypeOptions, - template.interestCalculationTypeOptions, template.interestCalculationDaysInYearTypeOptions, - template.lockinPeriodFrequencyTypeOptions, template.withdrawalFeeTypeOptions, charges, template.chargeOptions); + account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers, account.summary, transactions, + template.productOptions, template.fieldOfficerOptions, template.interestCompoundingPeriodTypeOptions, + template.interestPostingPeriodTypeOptions, template.interestCalculationTypeOptions, + template.interestCalculationDaysInYearTypeOptions, template.lockinPeriodFrequencyTypeOptions, + template.withdrawalFeeTypeOptions, charges, template.chargeOptions); } public static SavingsAccountData withTemplateOptions(final SavingsAccountData account, @@ -172,11 +198,10 @@ public class SavingsAccountData { account.fieldOfficerName, account.status, account.timeline, account.currency, account.nominalAnnualInterestRate, account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType, account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency, - account.lockinPeriodFrequencyType, account.withdrawalFeeAmount, account.withdrawalFeeType, - account.withdrawalFeeForTransfers, account.annualFeeAmount, account.annualFeeOnMonthDay, account.annualFeeNextDueDate, - account.summary, transactions, productOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions, - interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, - lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, chargeOptions); + account.lockinPeriodFrequencyType, account.withdrawalFeeForTransfers, account.summary, transactions, productOptions, + fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, + interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, + withdrawalFeeTypeOptions, charges, chargeOptions); } public static SavingsAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId, @@ -200,12 +225,12 @@ public class SavingsAccountData { final BigDecimal minRequiredOpeningBalance = null; final Integer lockinPeriodFrequency = null; final EnumOptionData lockinPeriodFrequencyType = null; - final BigDecimal withdrawalFeeAmount = null; - final EnumOptionData withdrawalFeeType = null; +// final BigDecimal withdrawalFeeAmount = null; +// final EnumOptionData withdrawalFeeType = null; final boolean withdrawalFeeForTransfers = false; - final BigDecimal annualFeeAmount = null; - final MonthDay annualFeeOnMonthDay = null; - final LocalDate annualFeeNextDueDate = null; +// final BigDecimal annualFeeAmount = null; +// final MonthDay annualFeeOnMonthDay = null; +// final LocalDate annualFeeNextDueDate = null; final SavingsAccountSummaryData summary = null; final Collection transactions = null; @@ -225,11 +250,10 @@ public class SavingsAccountData { return new SavingsAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, - lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, withdrawalFeeForTransfers, - annualFeeAmount, annualFeeOnMonthDay, annualFeeNextDueDate, summary, transactions, productOptions, fieldOfficerOptions, - interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, - interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges, - chargeOptions); + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions, + fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, + interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, + withdrawalFeeTypeOptions, charges, chargeOptions); } private SavingsAccountData(final Long id, final String accountNo, final String externalId, final Long groupId, final String groupName, @@ -238,11 +262,10 @@ public class SavingsAccountData { final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestPeriodType, final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, - final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount, - final EnumOptionData withdrawalFeeType, final boolean withdrawalFeeForTransfers, final BigDecimal annualFeeAmount, - final MonthDay annualFeeOnMonthDay, final LocalDate annualFeeNextDueDate, final SavingsAccountSummaryData summary, - final Collection transactions, final Collection productOptions, - final Collection fieldOfficerOptions, final Collection interestCompoundingPeriodTypeOptions, + final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers, + final SavingsAccountSummaryData summary, final Collection transactions, + final Collection productOptions, final Collection fieldOfficerOptions, + final Collection interestCompoundingPeriodTypeOptions, final Collection interestPostingPeriodTypeOptions, final Collection interestCalculationTypeOptions, final Collection interestCalculationDaysInYearTypeOptions, @@ -270,12 +293,12 @@ public class SavingsAccountData { this.minRequiredOpeningBalance = minRequiredOpeningBalance; this.lockinPeriodFrequency = lockinPeriodFrequency; this.lockinPeriodFrequencyType = lockinPeriodFrequencyType; - this.withdrawalFeeAmount = withdrawalFeeAmount; - this.withdrawalFeeType = withdrawalFeeType; +// this.withdrawalFeeAmount = withdrawalFeeAmount; +// this.withdrawalFeeType = withdrawalFeeType; this.withdrawalFeeForTransfers = withdrawalFeeForTransfers; - this.annualFeeAmount = annualFeeAmount; - this.annualFeeOnMonthDay = annualFeeOnMonthDay; - this.annualFeeNextDueDate = annualFeeNextDueDate; +// this.annualFeeAmount = annualFeeAmount; +// this.annualFeeOnMonthDay = annualFeeOnMonthDay; +// this.annualFeeNextDueDate = annualFeeNextDueDate; this.summary = summary; this.transactions = transactions; @@ -292,8 +315,26 @@ public class SavingsAccountData { this.charges = charges;// charges associated with Savings account // charges available for adding to Savings account this.chargeOptions = chargeOptions; + + this.withdrawalFee = getWithdrawalFee(); + + this.annualFee = getAnnualFee(); } + private SavingsAccountChargeData getWithdrawalFee() { + for (SavingsAccountChargeData charge : this.charges()) { + if (charge.isWithdrawalFee()) return charge; + } + return null; + } + + private SavingsAccountChargeData getAnnualFee() { + for (SavingsAccountChargeData charge : this.charges()) { + if (charge.isAnnualFee()) return charge; + } + return null; + } + public Long id() { return this.id; } @@ -328,4 +369,8 @@ public class SavingsAccountData { public int hashCode() { return new HashCodeBuilder(17, 37).append(this.id).append(this.accountNo).toHashCode(); } + + public Collection charges() { + return (this.charges == null) ? new HashSet() : this.charges; + } } \ 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 2cd1510bc..39bb81e7c 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 @@ -6,10 +6,13 @@ package org.mifosplatform.portfolio.savings.data; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountNoParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeAmountParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeOnMonthDayParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.amountParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargesParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.clientIdParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.externalIdParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeAmountParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.fieldOfficerIdParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.groupIdParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName; @@ -22,14 +25,13 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequire import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.productIdParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.submittedOnDateParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeAmountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeTypeParamName; import java.lang.reflect.Type; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Map; import org.apache.commons.lang.StringUtils; @@ -45,11 +47,12 @@ import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType; import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType; -import org.mifosplatform.portfolio.savings.SavingsWithdrawalFeesType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; @Component @@ -176,32 +179,6 @@ public class SavingsAccountDataValidator { } } - if (this.fromApiJsonHelper.parameterExists(withdrawalFeeAmountParamName, element)) { - - final BigDecimal withdrawalFeeAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(withdrawalFeeAmountParamName, - element); - baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(withdrawalFeeAmount).zeroOrPositiveAmount(); - - if (withdrawalFeeAmount != null) { - final Integer withdrawalFeeType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(withdrawalFeeTypeParamName, element); - baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType) - .isOneOfTheseValues(SavingsWithdrawalFeesType.integerValues()); - } - } - - if (this.fromApiJsonHelper.parameterExists(withdrawalFeeTypeParamName, element)) { - final Integer withdrawalFeeType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(withdrawalFeeTypeParamName, element); - baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType).ignoreIfNull() - .isOneOfTheseValues(1, 2); - - if (withdrawalFeeType != null) { - final BigDecimal withdrawalFeeAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed( - withdrawalFeeAmountParamName, element); - baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(withdrawalFeeAmount).notNull() - .zeroOrPositiveAmount(); - } - } - if (this.fromApiJsonHelper.parameterExists(withdrawalFeeForTransfersParamName, element)) { final Boolean isWithdrawalFeeApplicableForTransfers = this.fromApiJsonHelper.extractBooleanNamed( withdrawalFeeForTransfersParamName, element); @@ -209,29 +186,34 @@ public class SavingsAccountDataValidator { .ignoreIfNull().validateForBooleanValue(); } - if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) { - final BigDecimal annualFeeAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(annualFeeAmountParamName, element); - baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).zeroOrPositiveAmount(); - - if (annualFeeAmount != null) { - final MonthDay monthDayOfAnnualFee = this.fromApiJsonHelper.extractMonthDayNamed(annualFeeOnMonthDayParamName, element); - baseDataValidator.reset().parameter(annualFeeOnMonthDayParamName).value(monthDayOfAnnualFee).notNull(); - } - } - - if (this.fromApiJsonHelper.parameterExists(annualFeeOnMonthDayParamName, element)) { - - final MonthDay monthDayOfAnnualFee = this.fromApiJsonHelper.extractMonthDayNamed(annualFeeOnMonthDayParamName, element); - if (monthDayOfAnnualFee != null) { - final BigDecimal annualFeeAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(annualFeeAmountParamName, - element); - baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).notNull().zeroOrPositiveAmount(); - } - } - + validateSavingsCharges(element, baseDataValidator); + throwExceptionIfValidationWarningsExist(dataValidationErrors); } + private void validateSavingsCharges(final JsonElement element, final DataValidatorBuilder baseDataValidator) { + + if (element.isJsonObject()) { + final JsonObject topLevelJsonElement = element.getAsJsonObject(); + final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement); + if (topLevelJsonElement.has(chargesParamName) && topLevelJsonElement.get(chargesParamName).isJsonArray()) { + final JsonArray array = topLevelJsonElement.get(chargesParamName).getAsJsonArray(); + for (int i = 0; i < array.size(); i++) { + + final JsonObject savingsChargeElement = array.get(i).getAsJsonObject(); + + //final Long id = this.fromApiJsonHelper.extractLongNamed(idParamName, savingsChargeElement); + + final Long chargeId = this.fromApiJsonHelper.extractLongNamed(chargeIdParamName, savingsChargeElement); + baseDataValidator.reset().parameter(chargeIdParamName).value(chargeId).longGreaterThanZero(); + + final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalNamed(amountParamName, savingsChargeElement, locale); + baseDataValidator.reset().parameter(amountParamName).value(amount).notNull().positiveAmount(); + } + } + } + } + public void validateForUpdate(final String json) { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } @@ -359,19 +341,6 @@ public class SavingsAccountDataValidator { baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).inMinMaxRange(0, 3); } - if (this.fromApiJsonHelper.parameterExists(withdrawalFeeAmountParamName, element)) { - final BigDecimal withdrawalFeeAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(withdrawalFeeAmountParamName, - element); - baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(withdrawalFeeAmount).ignoreIfNull() - .zeroOrPositiveAmount(); - } - - if (this.fromApiJsonHelper.parameterExists(withdrawalFeeTypeParamName, element)) { - final Integer withdrawalFeeType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(withdrawalFeeTypeParamName, element); - baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType).ignoreIfNull() - .isOneOfTheseValues(SavingsWithdrawalFeesType.integerValues()); - } - if (this.fromApiJsonHelper.parameterExists(withdrawalFeeForTransfersParamName, element)) { final Boolean isWithdrawalFeeApplicableForTransfers = this.fromApiJsonHelper.extractBooleanNamed( withdrawalFeeForTransfersParamName, element); @@ -379,14 +348,14 @@ public class SavingsAccountDataValidator { .ignoreIfNull().validateForBooleanValue(); } - if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) { - final BigDecimal annualFeeAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(annualFeeAmountParamName, element); - baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).ignoreIfNull().zeroOrPositiveAmount(); + if (this.fromApiJsonHelper.parameterExists(feeAmountParamName, element)) { + final BigDecimal annualFeeAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(feeAmountParamName, element); + baseDataValidator.reset().parameter(feeAmountParamName).value(annualFeeAmount).ignoreIfNull().zeroOrPositiveAmount(); } - if (this.fromApiJsonHelper.parameterExists(annualFeeOnMonthDayParamName, element)) { - final MonthDay monthDayOfAnnualFee = this.fromApiJsonHelper.extractMonthDayNamed(annualFeeOnMonthDayParamName, element); - baseDataValidator.reset().parameter(annualFeeOnMonthDayParamName).value(monthDayOfAnnualFee).ignoreIfNull(); + if (this.fromApiJsonHelper.parameterExists(feeOnMonthDayParamName, element)) { + final MonthDay monthDayOfAnnualFee = this.fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, element); + baseDataValidator.reset().parameter(feeOnMonthDayParamName).value(monthDayOfAnnualFee).ignoreIfNull(); } throwExceptionIfValidationWarningsExist(dataValidationErrors); 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 a00e21f2e..c5bbf8e56 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 @@ -10,7 +10,6 @@ import java.util.Collection; import java.util.List; import java.util.Map; -import org.joda.time.MonthDay; import org.mifosplatform.accounting.common.AccountingRuleType; import org.mifosplatform.accounting.glaccount.data.GLAccountData; import org.mifosplatform.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper; @@ -37,11 +36,7 @@ public class SavingsProductData { private final BigDecimal minRequiredOpeningBalance; private final Integer lockinPeriodFrequency; private final EnumOptionData lockinPeriodFrequencyType; - private final BigDecimal withdrawalFeeAmount; - private final EnumOptionData withdrawalFeeType; private final boolean withdrawalFeeForTransfers; - private final BigDecimal annualFeeAmount; - private final MonthDay annualFeeOnMonthDay; // accounting private final EnumOptionData accountingRule; @@ -86,11 +81,7 @@ public class SavingsProductData { final BigDecimal minRequiredOpeningBalance = null; final Integer lockinPeriodFrequency = null; final EnumOptionData lockinPeriodFrequencyType = null; - final BigDecimal withdrawalFeeAmount = null; - final EnumOptionData withdrawalFeeType = null; final boolean withdrawalFeeForTransfers = false; - final BigDecimal annualFeeAmount = null; - final MonthDay annualFeeMonthDay = null; final Map accountingMappings = null; final Collection paymentChannelToFundSourceMappings = null; final Collection charges = null; @@ -99,11 +90,11 @@ public class SavingsProductData { return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, - lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, withdrawalFeeForTransfers, - accountingRule, annualFeeAmount, annualFeeMonthDay, accountingMappings, paymentChannelToFundSourceMappings, - currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, - interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, - accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, accountingRule, accountingMappings, + paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, + interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, + lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, + accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings); } @@ -111,13 +102,12 @@ public class SavingsProductData { return new SavingsProductData(product.id, product.name, product.description, product.currency, product.nominalAnnualInterestRate, product.interestCompoundingPeriodType, product.interestPostingPeriodType, product.interestCalculationType, product.interestCalculationDaysInYearType, product.minRequiredOpeningBalance, product.lockinPeriodFrequency, - product.lockinPeriodFrequencyType, product.withdrawalFeeAmount, product.withdrawalFeeType, - product.withdrawalFeeForTransfers, product.accountingRule, product.annualFeeAmount, product.annualFeeOnMonthDay, - product.accountingMappings, product.paymentChannelToFundSourceMappings, product.currencyOptions, - product.interestCompoundingPeriodTypeOptions, product.interestPostingPeriodTypeOptions, - product.interestCalculationTypeOptions, product.interestCalculationDaysInYearTypeOptions, - product.lockinPeriodFrequencyTypeOptions, product.withdrawalFeeTypeOptions, product.paymentTypeOptions, - product.accountingRuleOptions, product.accountingMappingOptions, charges, product.chargeOptions, product.penaltyOptions, + product.lockinPeriodFrequencyType, product.withdrawalFeeForTransfers, product.accountingRule, product.accountingMappings, + product.paymentChannelToFundSourceMappings, product.currencyOptions, product.interestCompoundingPeriodTypeOptions, + product.interestPostingPeriodTypeOptions, product.interestCalculationTypeOptions, + product.interestCalculationDaysInYearTypeOptions, product.lockinPeriodFrequencyTypeOptions, + product.withdrawalFeeTypeOptions, product.paymentTypeOptions, product.accountingRuleOptions, + product.accountingMappingOptions, charges, product.chargeOptions, product.penaltyOptions, product.feeToIncomeAccountMappings, product.penaltyToIncomeAccountMappings); } @@ -139,9 +129,8 @@ public class SavingsProductData { existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType, existingProduct.interestPostingPeriodType, existingProduct.interestCalculationType, existingProduct.interestCalculationDaysInYearType, existingProduct.minRequiredOpeningBalance, - existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType, existingProduct.withdrawalFeeAmount, - existingProduct.withdrawalFeeType, existingProduct.withdrawalFeeForTransfers, existingProduct.accountingRule, - existingProduct.annualFeeAmount, existingProduct.annualFeeOnMonthDay, existingProduct.accountingMappings, + existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType, + existingProduct.withdrawalFeeForTransfers, existingProduct.accountingRule, existingProduct.accountingMappings, existingProduct.paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, @@ -172,9 +161,8 @@ public class SavingsProductData { existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType, existingProduct.interestPostingPeriodType, existingProduct.interestCalculationType, existingProduct.interestCalculationDaysInYearType, existingProduct.minRequiredOpeningBalance, - existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType, existingProduct.withdrawalFeeAmount, - existingProduct.withdrawalFeeType, existingProduct.withdrawalFeeForTransfers, existingProduct.accountingRule, - existingProduct.annualFeeAmount, existingProduct.annualFeeOnMonthDay, accountingMappings, + existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType, + existingProduct.withdrawalFeeForTransfers, existingProduct.accountingRule, accountingMappings, paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, @@ -186,9 +174,8 @@ public class SavingsProductData { 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 BigDecimal withdrawalFeeAmount, - final EnumOptionData withdrawalFeeType, final boolean withdrawalFeeForTransfers, final BigDecimal annualFeeAmount, - final MonthDay annualFeeOnMonthDay, final EnumOptionData accountingType) { + final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers, + final EnumOptionData accountingType) { final Map accountingMappings = null; final Collection paymentChannelToFundSourceMappings = null; @@ -211,11 +198,11 @@ public class SavingsProductData { return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, - lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, withdrawalFeeForTransfers, - accountingType, annualFeeAmount, annualFeeOnMonthDay, accountingMappings, paymentChannelToFundSourceMappings, - currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, - interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, - accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, accountingType, accountingMappings, + paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, + interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, + lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, + accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings); } @@ -231,12 +218,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 boolean withdrawalFeeForTransfers = false; final EnumOptionData accountingType = null; - final BigDecimal annualFeeAmount = null; - final MonthDay annualFeeMonthDay = null; final Map accountingMappings = null; final Collection paymentChannelToFundSourceMappings = null; @@ -258,8 +241,8 @@ public class SavingsProductData { return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, - lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, withdrawalFeeForTransfers, - accountingType, annualFeeAmount, annualFeeMonthDay, accountingMappings, paymentChannelToFundSourceMappings, + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, + accountingType, accountingMappings, paymentChannelToFundSourceMappings, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings, @@ -270,9 +253,8 @@ public class SavingsProductData { 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 BigDecimal withdrawalFeeAmount, - final EnumOptionData withdrawalFeeType, final boolean withdrawalFeeForTransfers, final EnumOptionData accountingType, - final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay, final Map accountingMappings, + final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers, + final EnumOptionData accountingType, final Map accountingMappings, final Collection paymentChannelToFundSourceMappings, final Collection currencyOptions, final Collection interestCompoundingPeriodTypeOptions, final Collection interestPostingPeriodTypeOptions, @@ -297,11 +279,7 @@ public class SavingsProductData { this.minRequiredOpeningBalance = minRequiredOpeningBalance; this.lockinPeriodFrequency = lockinPeriodFrequency; this.lockinPeriodFrequencyType = lockinPeriodFrequencyType; - this.withdrawalFeeAmount = withdrawalFeeAmount; - this.withdrawalFeeType = withdrawalFeeType; this.withdrawalFeeForTransfers = withdrawalFeeForTransfers; - this.annualFeeAmount = annualFeeAmount; - this.annualFeeOnMonthDay = annualFeeOnMonthDay; this.currencyOptions = currencyOptions; this.interestCompoundingPeriodTypeOptions = interestCompoundingPeriodTypeOptions; 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 72a5077fe..ce39ed6e2 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 @@ -7,8 +7,8 @@ package org.mifosplatform.portfolio.savings.data; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeAmountParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeOnMonthDayParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeAmountParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName; @@ -22,9 +22,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeri import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nameParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeAmountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeTypeParamName; import java.lang.reflect.Type; import java.math.BigDecimal; @@ -46,7 +44,6 @@ import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType; import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType; -import org.mifosplatform.portfolio.savings.SavingsWithdrawalFeesType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -151,7 +148,7 @@ public class SavingsProductDataValidator { } } - if (this.fromApiJsonHelper.parameterExists(withdrawalFeeAmountParamName, element)) { + /*if (this.fromApiJsonHelper.parameterExists(withdrawalFeeAmountParamName, element)) { final BigDecimal withdrawalFeeAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(withdrawalFeeAmountParamName, element); @@ -176,7 +173,7 @@ public class SavingsProductDataValidator { .zeroOrPositiveAmount(); } } - +*/ if (this.fromApiJsonHelper.parameterExists(withdrawalFeeForTransfersParamName, element)) { final Boolean isWithdrawalFeeApplicableForTransfers = this.fromApiJsonHelper.extractBooleanNamed( withdrawalFeeForTransfersParamName, element); @@ -184,23 +181,23 @@ public class SavingsProductDataValidator { .ignoreIfNull().validateForBooleanValue(); } - if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) { - final BigDecimal annualFeeAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(annualFeeAmountParamName, element); - baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).zeroOrPositiveAmount(); + if (this.fromApiJsonHelper.parameterExists(feeAmountParamName, element)) { + final BigDecimal annualFeeAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(feeAmountParamName, element); + baseDataValidator.reset().parameter(feeAmountParamName).value(annualFeeAmount).zeroOrPositiveAmount(); if (annualFeeAmount != null) { - final MonthDay monthDayOfAnnualFee = this.fromApiJsonHelper.extractMonthDayNamed(annualFeeOnMonthDayParamName, element); - baseDataValidator.reset().parameter(annualFeeOnMonthDayParamName).value(monthDayOfAnnualFee).notNull(); + final MonthDay monthDayOfAnnualFee = this.fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, element); + baseDataValidator.reset().parameter(feeOnMonthDayParamName).value(monthDayOfAnnualFee).notNull(); } } - if (this.fromApiJsonHelper.parameterExists(annualFeeOnMonthDayParamName, element)) { + if (this.fromApiJsonHelper.parameterExists(feeOnMonthDayParamName, element)) { - final MonthDay monthDayOfAnnualFee = this.fromApiJsonHelper.extractMonthDayNamed(annualFeeOnMonthDayParamName, element); + final MonthDay monthDayOfAnnualFee = this.fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, element); if (monthDayOfAnnualFee != null) { - final BigDecimal annualFeeAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(annualFeeAmountParamName, + final BigDecimal annualFeeAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(feeAmountParamName, element); - baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).notNull().zeroOrPositiveAmount(); + baseDataValidator.reset().parameter(feeAmountParamName).value(annualFeeAmount).notNull().zeroOrPositiveAmount(); } } @@ -331,19 +328,6 @@ public class SavingsProductDataValidator { baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).inMinMaxRange(0, 3); } - if (this.fromApiJsonHelper.parameterExists(withdrawalFeeAmountParamName, element)) { - final BigDecimal withdrawalFeeAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(withdrawalFeeAmountParamName, - element); - baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(withdrawalFeeAmount).ignoreIfNull() - .zeroOrPositiveAmount(); - } - - if (this.fromApiJsonHelper.parameterExists(withdrawalFeeTypeParamName, element)) { - final Integer withdrawalFeeType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(withdrawalFeeTypeParamName, element); - baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType).ignoreIfNull() - .isOneOfTheseValues(1, 2); - } - if (this.fromApiJsonHelper.parameterExists(withdrawalFeeForTransfersParamName, element)) { final Boolean isWithdrawalFeeApplicableForTransfers = this.fromApiJsonHelper.extractBooleanNamed( withdrawalFeeForTransfersParamName, element); @@ -351,14 +335,14 @@ public class SavingsProductDataValidator { .ignoreIfNull().validateForBooleanValue(); } - if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) { - final BigDecimal annualFeeAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(annualFeeAmountParamName, element); - baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).ignoreIfNull().zeroOrPositiveAmount(); + if (this.fromApiJsonHelper.parameterExists(feeAmountParamName, element)) { + final BigDecimal annualFeeAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(feeAmountParamName, element); + baseDataValidator.reset().parameter(feeAmountParamName).value(annualFeeAmount).ignoreIfNull().zeroOrPositiveAmount(); } - if (this.fromApiJsonHelper.parameterExists(annualFeeOnMonthDayParamName, element)) { - final MonthDay monthDayOfAnnualFee = this.fromApiJsonHelper.extractMonthDayNamed(annualFeeOnMonthDayParamName, element); - baseDataValidator.reset().parameter(annualFeeOnMonthDayParamName).value(monthDayOfAnnualFee).ignoreIfNull(); + if (this.fromApiJsonHelper.parameterExists(feeOnMonthDayParamName, element)) { + final MonthDay monthDayOfAnnualFee = this.fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, element); + baseDataValidator.reset().parameter(feeOnMonthDayParamName).value(monthDayOfAnnualFee).ignoreIfNull(); } final Long savingsControlAccountId = this.fromApiJsonHelper.extractLongNamed( 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 7f9c390a2..7e6e2d291 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 @@ -6,15 +6,10 @@ package org.mifosplatform.portfolio.savings.domain; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeAmountParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeOnMonthDayParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.localeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeAmountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeTypeParamName; import java.math.BigDecimal; import java.math.MathContext; @@ -49,7 +44,6 @@ import org.apache.commons.lang.StringUtils; import org.hibernate.annotations.LazyCollection; import org.hibernate.annotations.LazyCollectionOption; import org.joda.time.LocalDate; -import org.joda.time.MonthDay; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.mifosplatform.infrastructure.core.api.JsonCommand; @@ -76,7 +70,6 @@ import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearT import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType; -import org.mifosplatform.portfolio.savings.SavingsWithdrawalFeesType; import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionDTO; import org.mifosplatform.portfolio.savings.domain.interest.PostingPeriod; import org.mifosplatform.portfolio.savings.exception.InsufficientAccountBalanceException; @@ -223,28 +216,9 @@ 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; - @Column(name = "withdrawal_fee_for_transfer", nullable = true) private boolean withdrawalFeeApplicableForTransfer; - @Column(name = "annual_fee_amount", scale = 6, precision = 19, nullable = true) - private BigDecimal annualFeeAmount; - - @Column(name = "annual_fee_on_month", nullable = true) - private Integer annualFeeOnMonth; - - @Column(name = "annual_fee_on_day", nullable = true) - private Integer annualFeeOnDay; - - @Temporal(TemporalType.DATE) - @Column(name = "annual_fee_next_due_date", nullable = true) - private Date annualFeeNextDueDate; - @Embedded private SavingsAccountSummary summary; @@ -275,16 +249,13 @@ public class SavingsAccount extends AbstractPersistable { final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, - final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType, - final boolean withdrawalFeeApplicableForTransfer, final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay, - final Set savingsAccountCharges) { + final boolean withdrawalFeeApplicableForTransfer, final Set savingsAccountCharges) { final SavingsAccountStatusType status = SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL; return new SavingsAccount(client, group, product, fieldOfficer, accountNo, externalId, status, accountType, submittedOnDate, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, - withdrawalFeeAmount, withdrawalFeeType, withdrawalFeeApplicableForTransfer, annualFeeAmount, annualFeeOnMonthDay, - savingsAccountCharges); + withdrawalFeeApplicableForTransfer, savingsAccountCharges); } private SavingsAccount(final Client client, final Group group, final SavingsProduct product, final Staff fieldOfficer, @@ -294,9 +265,7 @@ public class SavingsAccount extends AbstractPersistable { final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, - final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType, - final boolean withdrawalFeeApplicableForTransfer, final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay, - final Set savingsAccountCharges) { + final boolean withdrawalFeeApplicableForTransfer, final Set savingsAccountCharges) { this.client = client; this.group = group; this.product = product; @@ -322,18 +291,8 @@ public class SavingsAccount extends AbstractPersistable { if (lockinPeriodFrequencyType != null) { this.lockinPeriodFrequencyType = lockinPeriodFrequencyType.getValue(); } - this.withdrawalFeeAmount = withdrawalFeeAmount; - if (withdrawalFeeType != null) { - this.withdrawalFeeType = withdrawalFeeType.getValue(); - } this.withdrawalFeeApplicableForTransfer = withdrawalFeeApplicableForTransfer; - this.annualFeeAmount = annualFeeAmount; - if (annualFeeOnMonthDay != null) { - this.annualFeeOnMonth = annualFeeOnMonthDay.getMonthOfYear(); - this.annualFeeOnDay = annualFeeOnMonthDay.getDayOfMonth(); - } - if (!CollectionUtils.isEmpty(savingsAccountCharges)) { this.charges = associateChargesWithThisSavingsAccount(savingsAccountCharges); } @@ -616,7 +575,7 @@ public class SavingsAccount extends AbstractPersistable { return transaction; } - private LocalDate getActivationLocalDate() { + public LocalDate getActivationLocalDate() { LocalDate activationLocalDate = null; if (this.activatedOnDate != null) { activationLocalDate = new LocalDate(this.activatedOnDate); @@ -682,33 +641,20 @@ public class SavingsAccount extends AbstractPersistable { transactionDTO.getPaymentDetail(), transactionDTO.getTransactionDate(), transactionAmountMoney); this.transactions.add(transaction); - if (applyWithdrawFee && isAutomaticWithdrawalFee()) { - - SavingsAccountTransaction withdrawalFeeTransaction = null; - Money feeAmount = Money.zero(this.currency); - switch (SavingsWithdrawalFeesType.fromInt(this.withdrawalFeeType)) { - case INVALID: - break; - case FLAT: - feeAmount = Money.of(this.currency, this.withdrawalFeeAmount); - withdrawalFeeTransaction = SavingsAccountTransaction - .fee(this, office(), transactionDTO.getTransactionDate(), feeAmount); - this.transactions.add(withdrawalFeeTransaction); - break; - case PERCENT_OF_AMOUNT: - final BigDecimal feeAmountDecimal = transactionDTO.getTransactionAmount().multiply(this.withdrawalFeeAmount) - .divide(BigDecimal.valueOf(100l)); - feeAmount = Money.of(this.currency, feeAmountDecimal); - withdrawalFeeTransaction = SavingsAccountTransaction - .fee(this, office(), transactionDTO.getTransactionDate(), feeAmount); - this.transactions.add(withdrawalFeeTransaction); - break; + if (applyWithdrawFee){ + //auto pay withdrawal fee + payWithdrawalFee(transactionDTO.getTransactionAmount(), transactionDTO.getTransactionDate()); + } + return transaction; + } + + private void payWithdrawalFee(final BigDecimal transactionAmoount, final LocalDate transactionDate){ + for (SavingsAccountCharge charge : this.charges()) { + if (charge.isWithdrawalFee()){ + charge.updateWithdralFeeAmount(transactionAmoount); + this.payCharge(charge, charge.getAmountOutstanding(this.getCurrency()), transactionDate); } } - - this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); - - return transaction; } public boolean isBeforeLastPostingPeriod(final LocalDate transactionDate) { @@ -768,128 +714,7 @@ public class SavingsAccount extends AbstractPersistable { } } } - - public SavingsAccountTransaction addAnnualFee(final MathContext mc, final DateTimeFormatter formatter, - final LocalDate annualFeeTransactionDate, final LocalDate today) { - - final List dataValidationErrors = new ArrayList(); - final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) - .resource(SAVINGS_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.applyAnnualFeeTransactionAction); - - if (isNotActive()) { - baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.is.not.active"); - - if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } - } - - final LocalDate nextAnnualFeeDueDate = getNextAnnualFeeDueDate(); - if (nextAnnualFeeDueDate == null || annualFeeSettingsNotSet()) { - baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("no.annualfee.settings"); - - throw new PlatformApiDataValidationException(dataValidationErrors); - } - - if (nextAnnualFeeDueDate.isBefore(getActivationLocalDate())) { - - baseDataValidator.reset().parameter("annualFeeTransactionDate").value(getActivationLocalDate().toString(formatter)) - .failWithCodeNoParameterAddedToErrorCode("before.activationDate"); - - throw new PlatformApiDataValidationException(dataValidationErrors); - } - - if (isDateInTheFuture(annualFeeTransactionDate)) { - baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.in.the.future"); - - throw new PlatformApiDataValidationException(dataValidationErrors); - } - - if (isNotValidAnnualFeeTransactionDate(annualFeeTransactionDate, today)) { - baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("invalid.date"); - - throw new PlatformApiDataValidationException(dataValidationErrors); - } - - Date currentAnnualFeeNextDueDate = findLatestAnnualFeeTransactionDueDate(); - if (currentAnnualFeeNextDueDate != null && new LocalDate(currentAnnualFeeNextDueDate).isEqual(annualFeeTransactionDate)) { - baseDataValidator.reset().parameter("annualFeeTransactionDate").value(annualFeeTransactionDate.toString(formatter)) - .failWithCodeNoParameterAddedToErrorCode("transaction.exists.on.date"); - - throw new PlatformApiDataValidationException(dataValidationErrors); - } - validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_PAY_CHARGE, annualFeeTransactionDate); - - final Money annualFee = Money.of(this.currency, this.annualFeeAmount); - final SavingsAccountTransaction annualFeeTransaction = SavingsAccountTransaction.annualFee(this, office(), - annualFeeTransactionDate, annualFee); - this.transactions.add(annualFeeTransaction); - - validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.applyAnnualFeeTransactionAction); - - this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); - - calculateInterestUsing(mc, today); - - currentAnnualFeeNextDueDate = findLatestAnnualFeeTransactionDueDate(); - if (currentAnnualFeeNextDueDate != null) { - final LocalDate newAnnualFeeNextDueDate = new LocalDate(currentAnnualFeeNextDueDate).withMonthOfYear(this.annualFeeOnMonth) - .withDayOfMonth(this.annualFeeOnDay).plusYears(1); - this.annualFeeNextDueDate = newAnnualFeeNextDueDate.toDate(); - } else { - updateToNextAnnualFeeDueDateFrom(getActivationLocalDate()); - } - - return annualFeeTransaction; - } - - private boolean isNotValidAnnualFeeTransactionDate(final LocalDate annualFeeTransactionDate, final LocalDate today) { - return !isValidAnnualFeeTransactionDate(annualFeeTransactionDate, today); - } - - private boolean isValidAnnualFeeTransactionDate(final LocalDate annualFeeTransactionDate, final LocalDate today) { - - LocalDate startingDate = getActivationLocalDate(); - boolean isValid = false; - while (!startingDate.isAfter(today) && !isValid) { - LocalDate nextDueLocalDate = startingDate.withMonthOfYear(this.annualFeeOnMonth).withDayOfMonth(this.annualFeeOnDay); - if (startingDate.isAfter(nextDueLocalDate)) { - nextDueLocalDate = nextDueLocalDate.plusYears(1); - } - isValid = nextDueLocalDate.isEqual(annualFeeTransactionDate); - - startingDate = nextDueLocalDate.plusYears(1); - } - - return isValid; - } - - private void updateToNextAnnualFeeDueDateFrom(final LocalDate startingDate) { - LocalDate nextDueLocalDate = startingDate.withMonthOfYear(this.annualFeeOnMonth).withDayOfMonth(this.annualFeeOnDay); - if (startingDate.isAfter(nextDueLocalDate)) { - nextDueLocalDate = nextDueLocalDate.plusYears(1); - } - this.annualFeeNextDueDate = nextDueLocalDate.toDate(); - } - - private LocalDate getNextAnnualFeeDueDate() { - LocalDate nextAnnualFeeDueDate = null; - if (this.annualFeeNextDueDate != null) { - nextAnnualFeeDueDate = new LocalDate(this.annualFeeNextDueDate); - } - return nextAnnualFeeDueDate; - } - - private boolean annualFeeSettingsNotSet() { - return !annualFeeSettingsSet(); - } - - private boolean annualFeeSettingsSet() { - return this.annualFeeOnDay != null && this.annualFeeOnMonth != null; - } - - private boolean isAutomaticWithdrawalFee() { - return this.withdrawalFeeType != null; - } - + private boolean isAccountLocked(final LocalDate transactionDate) { boolean isLocked = false; final boolean accountHasLockedInSetting = this.lockedInUntilDate != null; @@ -1037,55 +862,12 @@ public class SavingsAccount extends AbstractPersistable { this.lockinPeriodFrequencyType = null; } - if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(withdrawalFeeAmountParamName, this.withdrawalFeeAmount)) { - final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(withdrawalFeeAmountParamName); - actualChanges.put(withdrawalFeeAmountParamName, newValue); - actualChanges.put(localeParamName, localeAsInput); - this.withdrawalFeeAmount = newValue; - } - - if (command.isChangeInIntegerParameterNamedDefaultingZeroToNull(withdrawalFeeTypeParamName, this.withdrawalFeeType)) { - final Integer newValue = command.integerValueOfParameterNamedDefaultToNullIfZero(withdrawalFeeTypeParamName); - actualChanges.put(withdrawalFeeTypeParamName, newValue); - this.withdrawalFeeType = newValue != null ? SavingsWithdrawalFeesType.fromInt(newValue).getValue() : newValue; - } - if (command.isChangeInBooleanParameterNamed(withdrawalFeeForTransfersParamName, this.withdrawalFeeApplicableForTransfer)) { final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(withdrawalFeeForTransfersParamName); actualChanges.put(withdrawalFeeForTransfersParamName, newValue); this.withdrawalFeeApplicableForTransfer = newValue; } - // set period type to null if frequency is null - if (this.withdrawalFeeAmount == null) { - this.withdrawalFeeType = null; - } - - if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(annualFeeAmountParamName, this.annualFeeAmount)) { - final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(annualFeeAmountParamName); - actualChanges.put(annualFeeAmountParamName, newValue); - actualChanges.put(localeParamName, localeAsInput); - this.annualFeeAmount = newValue; - } - - if (command.hasParameter(annualFeeOnMonthDayParamName)) { - final MonthDay monthDay = command.extractMonthDayNamed(annualFeeOnMonthDayParamName); - final String actualValueEntered = command.stringValueOfParameterNamed(annualFeeOnMonthDayParamName); - final Integer dayOfMonthValue = monthDay.getDayOfMonth(); - if (this.annualFeeOnDay != dayOfMonthValue) { - actualChanges.put(annualFeeOnMonthDayParamName, actualValueEntered); - actualChanges.put(localeParamName, localeAsInput); - this.annualFeeOnDay = dayOfMonthValue; - } - - final Integer monthOfYear = monthDay.getMonthOfYear(); - if (this.annualFeeOnMonth != monthOfYear) { - actualChanges.put(annualFeeOnMonthDayParamName, actualValueEntered); - actualChanges.put(localeParamName, localeAsInput); - this.annualFeeOnMonth = monthOfYear; - } - } - // charges final String chargesParamName = "charges"; if (command.hasParameter(chargesParamName)) { @@ -1095,60 +877,8 @@ public class SavingsAccount extends AbstractPersistable { } } - // set period type to null if frequency is null - if (this.annualFeeAmount == null) { - this.annualFeeOnDay = null; - this.annualFeeOnMonth = null; - } - validateLockinDetails(); - validateWithdrawalFeeDetails(); - validateAnnualFeeDetails(); - } - - private void validateAnnualFeeDetails() { - - final List dataValidationErrors = new ArrayList(); - final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) - .resource(SAVINGS_ACCOUNT_RESOURCE_NAME); - - if (this.annualFeeAmount == null) { - - if (this.annualFeeOnMonth != null || this.annualFeeOnDay != null) { - baseDataValidator.reset().parameter(annualFeeAmountParamName).value(this.annualFeeAmount).notNull(); - } - } else { - - if (this.annualFeeOnMonth == null || this.annualFeeOnDay == null) { - baseDataValidator.reset().parameter(annualFeeOnMonthDayParamName).value(this.annualFeeOnMonth).notNull(); - } - - baseDataValidator.reset().parameter(annualFeeAmountParamName).value(this.annualFeeAmount).zeroOrPositiveAmount(); - } - - if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } - } - - private void validateWithdrawalFeeDetails() { - - final List dataValidationErrors = new ArrayList(); - final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) - .resource(SAVINGS_ACCOUNT_RESOURCE_NAME); - - if (this.withdrawalFeeAmount == null) { - baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(this.withdrawalFeeType).ignoreIfNull() - .isOneOfTheseValues(1, 2); - - if (this.withdrawalFeeType != null) { - baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(this.withdrawalFeeAmount).notNull(); - } - } else { - baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(this.withdrawalFeeAmount).zeroOrPositiveAmount(); - baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(this.withdrawalFeeType).notNull() - .isOneOfTheseValues(1, 2); - } - - if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } private void validateLockinDetails() { @@ -1311,8 +1041,8 @@ public class SavingsAccount extends AbstractPersistable { public void validateNewApplicationState(final LocalDate todayDateOfTenant) { validateLockinDetails(); - validateWithdrawalFeeDetails(); - validateAnnualFeeDetails(); + //validateWithdrawalFeeDetails(); + //validateAnnualFeeDetails(); final LocalDate submittedOn = getSubmittedOnLocalDate(); @@ -1470,22 +1200,16 @@ public class SavingsAccount extends AbstractPersistable { validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_UNDO_TRANSACTION, transactionToUndo.transactionLocalDate()); transactionToUndo.reverse(); - if (transactionToUndo.isAnnualFee()) { - this.annualFeeNextDueDate = findLatestAnnualFeeTransactionDueDate(); - if (this.annualFeeNextDueDate == null) { - updateToNextAnnualFeeDueDateFrom(getActivationLocalDate()); - } else { - final LocalDate newAnnualFeeNextDueDate = new LocalDate(this.annualFeeNextDueDate) - .withMonthOfYear(this.annualFeeOnMonth).withDayOfMonth(this.annualFeeOnDay).plusYears(1); - this.annualFeeNextDueDate = newAnnualFeeNextDueDate.toDate(); - } - } else if (transactionToUndo.isCharge() || transactionToUndo.isWaiveCharge()) { + if (transactionToUndo.isChargeTransaction() || transactionToUndo.isWaiveCharge()) { // undo charge final Set chargesPaidBy = transactionToUndo.getSavingsAccountChargesPaid(); for (final SavingsAccountChargePaidBy savingsAccountChargePaidBy : chargesPaidBy) { final SavingsAccountCharge chargeToUndo = savingsAccountChargePaidBy.getSavingsAccountCharge(); - if (transactionToUndo.isCharge()) { + if (transactionToUndo.isChargeTransaction()) { chargeToUndo.undoPayment(this.getCurrency(), transactionToUndo.getAmount(this.getCurrency())); + if(chargeToUndo.isAnnualFee()){ + chargeToUndo.updateToPreviousAnnualFeeDueDate(); + } } else if (transactionToUndo.isWaiveCharge()) { chargeToUndo.undoWaiver(this.getCurrency(), transactionToUndo.getAmount(this.getCurrency())); } @@ -1672,10 +1396,10 @@ public class SavingsAccount extends AbstractPersistable { this.activatedBy = currentUser; this.lockedInUntilDate = calculateDateAccountIsLockedUntil(getActivationLocalDate()); - if (annualFeeSettingsSet()) { + /*if (annualFeeSettingsSet()) { updateToNextAnnualFeeDueDateFrom(getActivationLocalDate()); } - +*/ if (this.client != null && this.client.isActivatedAfter(activationDate)) { final DateTimeFormatter formatter = DateTimeFormat.forPattern(command.dateFormat()).withLocale(command.extractLocale()); final String dateAsString = formatter.print(this.client.getActivationLocalDate()); @@ -1730,9 +1454,27 @@ public class SavingsAccount extends AbstractPersistable { // correct. recalculateDailyBalances(openingAccountBalance); } - + + + //update annual fee due date + for (SavingsAccountCharge charge : this.charges()) { + charge.updateToNextAnnualFeeDueDateFrom(getActivationLocalDate()); + } + + //auto pay the activation time charges + this.payActivationCharges(); + //TODO : AA add activation charges to actual changes list + return actualChanges; } + + private void payActivationCharges() { + for (SavingsAccountCharge savingsAccountCharge : this.charges()) { + if (savingsAccountCharge.isSavingsActivation()) { + payCharge(savingsAccountCharge, savingsAccountCharge.getAmountOutstanding(getCurrency()), getActivationLocalDate()); + } + } + } public Map close(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate) { @@ -1918,6 +1660,11 @@ public class SavingsAccount extends AbstractPersistable { final SavingsAccountCharge savingsAccountCharge = getCharge(savingsAccountChargeId); + if (savingsAccountCharge.isWithdrawalFee()){ + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.waiver.of.withdrawal.fee.not.supported"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + // validate charge is not already paid or waived if (savingsAccountCharge.isWaived()) { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.charge.is.already.waived"); @@ -1963,14 +1710,55 @@ public class SavingsAccount extends AbstractPersistable { throw new PlatformApiDataValidationException(dataValidationErrors); } } + + if (savingsAccountCharge.isSavingsActivation() && !(isSubmittedAndPendingApproval() || (isApproved() && isNotActive()))) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode( + "not.valid.account.status.cannot.add.activation.time.charge"); + throw new PlatformApiDataValidationException(dataValidationErrors); + } - validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_APPLY_CHARGE, chargeDueDate); - + //Only one withdrawal fee is supported per account + if(savingsAccountCharge.isWithdrawalFee()){ + if(this.isWithDrawalFeeExists()){ + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode( + "multiple.withdrawal.fee.per.account.not.supported"); + throw new PlatformApiDataValidationException(dataValidationErrors); + } + } + + //Only one annual fee is supported per account + if(savingsAccountCharge.isAnnualFee()){ + if(this.isAnnualFeeExists()){ + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode( + "multiple.annual.fee.per.account.not.supported"); + throw new PlatformApiDataValidationException(dataValidationErrors); + } + } + + //activation charge and withdrawal charges not required this validation + if(savingsAccountCharge.isOnSpecifiedDueDate()){ + validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_APPLY_CHARGE, chargeDueDate); + } + // add new charge to savings account - getCharges().add(savingsAccountCharge); + charges().add(savingsAccountCharge); } + private boolean isWithDrawalFeeExists() { + for (SavingsAccountCharge charge : this.charges()) { + if(charge.isWithdrawalFee()) return true; + } + return false; + } + + private boolean isAnnualFeeExists() { + for (SavingsAccountCharge charge : this.charges()) { + if(charge.isAnnualFee()) return true; + } + return false; + } + public void payCharge(final SavingsAccountCharge savingsAccountCharge, final BigDecimal amountPaid, final LocalDate transactionDate, final DateTimeFormatter formatter) { @@ -1988,24 +1776,45 @@ public class SavingsAccount extends AbstractPersistable { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - if (savingsAccountCharge.isOnSpecifiedDueDate()) { - if (getActivationLocalDate() != null && transactionDate.isBefore(getActivationLocalDate())) { - baseDataValidator.reset().parameter(dueAsOfDateParamName).value(getActivationLocalDate().toString(formatter)) - .failWithCodeNoParameterAddedToErrorCode("transaction.before.activationDate"); - throw new PlatformApiDataValidationException(dataValidationErrors); - } else if (getSubmittedOnLocalDate() != null && transactionDate.isBefore(getSubmittedOnLocalDate())) { - baseDataValidator.reset().parameter(dueAsOfDateParamName).value(getSubmittedOnLocalDate().toString(formatter)) - .failWithCodeNoParameterAddedToErrorCode("transaction.before.submittedOnDate"); - throw new PlatformApiDataValidationException(dataValidationErrors); - } + if (getActivationLocalDate() != null && transactionDate.isBefore(getActivationLocalDate())) { + baseDataValidator.reset().parameter(dueAsOfDateParamName).value(getActivationLocalDate().toString(formatter)) + .failWithCodeNoParameterAddedToErrorCode("transaction.before.activationDate"); + throw new PlatformApiDataValidationException(dataValidationErrors); } - if (DateUtils.isInFuture(transactionDate)) { + if (DateUtils.isDateInTheFuture(transactionDate)) { baseDataValidator.reset().parameter(dueAsOfDateParamName).value(getSubmittedOnLocalDate().toString(formatter)) .failWithCodeNoParameterAddedToErrorCode("transaction.is.futureDate"); throw new PlatformApiDataValidationException(dataValidationErrors); } + + if (savingsAccountCharge.isSavingsActivation()) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode( + "transaction.not.valid.cannot.pay.activation.time.charge.is.automated"); + throw new PlatformApiDataValidationException(dataValidationErrors); + } + + if (savingsAccountCharge.isAnnualFee()){ + final LocalDate annualFeeDueDate = savingsAccountCharge.getDueLocalDate(); + if (annualFeeDueDate == null) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("no.annualfee.settings"); + throw new PlatformApiDataValidationException(dataValidationErrors); + } + + if (!annualFeeDueDate.equals(transactionDate)) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("invalid.date"); + throw new PlatformApiDataValidationException(dataValidationErrors); + } + Date currentAnnualFeeNextDueDate = findLatestAnnualFeeTransactionDueDate(); + if (currentAnnualFeeNextDueDate != null && new LocalDate(currentAnnualFeeNextDueDate).isEqual(transactionDate)) { + baseDataValidator.reset().parameter("dueDate").value(transactionDate.toString(formatter)) + .failWithCodeNoParameterAddedToErrorCode("transaction.exists.on.date"); + + throw new PlatformApiDataValidationException(dataValidationErrors); + } + } + // validate charge is not already paid or waived if (savingsAccountCharge.isWaived()) { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.charge.is.already.waived"); @@ -2015,21 +1824,35 @@ public class SavingsAccount extends AbstractPersistable { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - final Money amountPaidMoney = Money.of(currency, amountPaid); - if (!savingsAccountCharge.getAmountOutstanding(getCurrency()).isGreaterThanOrEqualTo(amountPaidMoney)) { + final Money chargePaid = Money.of(currency, amountPaid); + if (!savingsAccountCharge.getAmountOutstanding(getCurrency()).isGreaterThanOrEqualTo(chargePaid)) { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.charge.amount.paid.in.access"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - savingsAccountCharge.pay(getCurrency(), amountPaidMoney); - - handlePayChargeTransactions(savingsAccountCharge, amountPaidMoney, transactionDate); + this.payCharge(savingsAccountCharge, chargePaid, transactionDate); + + //update next annual fee due date + savingsAccountCharge.updateToNextAnnualFeeDueDate(); } + public void payCharge(final SavingsAccountCharge savingsAccountCharge, final Money amountPaid, final LocalDate transactionDate) { + savingsAccountCharge.pay(getCurrency(), amountPaid); + handlePayChargeTransactions(savingsAccountCharge, amountPaid, transactionDate); + } + private void handlePayChargeTransactions(SavingsAccountCharge savingsAccountCharge, Money transactionAmount, final LocalDate transactionDate) { - final SavingsAccountTransaction chargeTransaction = SavingsAccountTransaction.charge(this, office(), transactionDate, - transactionAmount); + SavingsAccountTransaction chargeTransaction = null; + + if (savingsAccountCharge.isWithdrawalFee()){ + chargeTransaction = SavingsAccountTransaction.withdrawalFee(this, office(), transactionDate, transactionAmount); + }else if (savingsAccountCharge.isAnnualFee()){ + chargeTransaction = SavingsAccountTransaction.annualFee(this, office(), transactionDate, transactionAmount); + }else { + chargeTransaction = SavingsAccountTransaction.charge(this, office(), transactionDate, transactionAmount); + } + handleChargeTransactions(savingsAccountCharge, chargeTransaction); } @@ -2062,7 +1885,7 @@ public class SavingsAccount extends AbstractPersistable { return charge; } - private Set getCharges() { + public Set charges() { return (this.charges == null) ? new HashSet() : this.charges; } } \ No newline at end of file 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 bc480c9bb..35e19bc3a 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 @@ -6,8 +6,6 @@ package org.mifosplatform.portfolio.savings.domain; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountNoParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeAmountParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeOnMonthDayParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.clientIdParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.externalIdParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.fieldOfficerIdParamName; @@ -22,15 +20,12 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequire import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.productIdParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.submittedOnDateParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeAmountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeTypeParamName; import java.math.BigDecimal; import java.util.Set; import org.joda.time.LocalDate; -import org.joda.time.MonthDay; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.infrastructure.core.service.DateUtils; @@ -50,7 +45,6 @@ import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearT import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType; -import org.mifosplatform.portfolio.savings.SavingsWithdrawalFeesType; import org.mifosplatform.portfolio.savings.exception.SavingsProductNotFoundException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -199,50 +193,18 @@ public class SavingsAccountAssembler { } else { 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(); - } - boolean iswithdrawalFeeApplicableForTransfer = false; if (command.parameterExists(withdrawalFeeForTransfersParamName)) { iswithdrawalFeeApplicableForTransfer = command.booleanPrimitiveValueOfParameterNamed(withdrawalFeeForTransfersParamName); } - BigDecimal annualFeeAmount = null; - if (command.parameterExists(annualFeeAmountParamName)) { - annualFeeAmount = command.bigDecimalValueOfParameterNamed(annualFeeAmountParamName); - } else { - annualFeeAmount = product.annualFeeAmount(); - } - - MonthDay monthDayOfAnnualFee = null; - if (command.parameterExists(annualFeeOnMonthDayParamName)) { - monthDayOfAnnualFee = command.extractMonthDayNamed(annualFeeOnMonthDayParamName); - } else { - monthDayOfAnnualFee = product.monthDayOfAnnualFee(); - } final Set charges = this.savingsAccountChargeAssembler.fromParsedJson(element, product.currency().getCode()); final SavingsAccount account = SavingsAccount.createNewApplicationForSubmittal(client, group, product, fieldOfficer, accountNo, externalId, accountType, submittedOnDate, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, - lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, iswithdrawalFeeApplicableForTransfer, annualFeeAmount, - monthDayOfAnnualFee, charges); + lockinPeriodFrequencyType, iswithdrawalFeeApplicableForTransfer, charges); account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper); account.validateNewApplicationState(DateUtils.getLocalDateOfTenant()); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountCharge.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountCharge.java index fbdb113fa..ca25f95d0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountCharge.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountCharge.java @@ -9,6 +9,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.amountPara import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dateFormatParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.localeParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName; import java.math.BigDecimal; import java.math.MathContext; @@ -28,6 +29,7 @@ import javax.persistence.TemporalType; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.joda.time.LocalDate; +import org.joda.time.MonthDay; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; @@ -53,15 +55,22 @@ public class SavingsAccountCharge extends AbstractPersistable { private Integer chargeTime; @Temporal(TemporalType.DATE) - @Column(name = "due_for_collection_as_of_date") + @Column(name = "charge_due_date") private Date dueDate; + @Column(name = "fee_on_month", nullable = true) + private Integer feeOnMonth; + + @Column(name = "fee_on_day", nullable = true) + private Integer feeOnDay; + @Column(name = "charge_calculation_enum") private Integer chargeCalculation; @Column(name = "calculation_percentage", scale = 6, precision = 19, nullable = true) private BigDecimal percentage; + // TODO AA: This field may not require for savings charges @Column(name = "calculation_on_amount", scale = 6, precision = 19, nullable = true) private BigDecimal amountPercentageAppliedTo; @@ -89,29 +98,38 @@ public class SavingsAccountCharge extends AbstractPersistable { @Column(name = "waived", nullable = false) private boolean waived = false; + @Column(name = "is_active", nullable = false) + private boolean status = true; + public static SavingsAccountCharge createNewFromJson(final SavingsAccount savingsAccount, final Charge chargeDefinition, final JsonCommand command) { final BigDecimal amount = command.bigDecimalValueOfParameterNamed(amountParamName); final LocalDate dueDate = command.localDateValueOfParameterNamed(dueAsOfDateParamName); + final MonthDay feeOnMonthDay = command.extractMonthDayNamed(feeOnMonthDayParamName); final ChargeTimeType chargeTime = null; final ChargeCalculationType chargeCalculation = null; + final boolean status = true; - return new SavingsAccountCharge(savingsAccount, chargeDefinition, amount, chargeTime, chargeCalculation, dueDate); + return new SavingsAccountCharge(savingsAccount, chargeDefinition, amount, chargeTime, chargeCalculation, dueDate, status, + feeOnMonthDay); } // TODO AA: refactor method signature - public static SavingsAccountCharge createNewWithoutSavingsAccount(final Charge chargeDefinition, final BigDecimal amount, - final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate) { - return new SavingsAccountCharge(null, chargeDefinition, amount, chargeTime, chargeCalculation, dueDate); + public static SavingsAccountCharge createNewWithoutSavingsAccount(final Charge chargeDefinition, final BigDecimal amountPayable, + final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate, final boolean status, + final MonthDay feeOnMonthDay) { + return new SavingsAccountCharge(null, chargeDefinition, amountPayable, chargeTime, chargeCalculation, dueDate, status, + feeOnMonthDay); } protected SavingsAccountCharge() { // } - public SavingsAccountCharge(final SavingsAccount savingsAccount, final Charge chargeDefinition, final BigDecimal amount, - final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate) { + private SavingsAccountCharge(final SavingsAccount savingsAccount, final Charge chargeDefinition, final BigDecimal amount, + final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate, final boolean status, + final MonthDay feeOnMonthDay) { this.savingsAccount = savingsAccount; this.charge = chargeDefinition; @@ -127,6 +145,16 @@ public class SavingsAccountCharge extends AbstractPersistable { } + if (isAnnualFee()) { + if (feeOnMonthDay == null) { + final String defaultUserMessage = "Savings Account charge is missing due date."; + throw new SavingsAccountChargeWithoutMandatoryFieldException("savingsaccount.charge", dueAsOfDateParamName, + defaultUserMessage, chargeDefinition.getId(), chargeDefinition.getName()); + } + this.feeOnMonth = feeOnMonthDay.getMonthOfYear(); + this.feeOnDay = feeOnMonthDay.getDayOfMonth(); + } + this.dueDate = (dueDate == null) ? null : dueDate.toDate(); this.chargeCalculation = chargeDefinition.getChargeCalculation(); @@ -139,13 +167,18 @@ public class SavingsAccountCharge extends AbstractPersistable { chargeAmount = amount; } - final BigDecimal transactionAmount = new BigDecimal(0);// percentage - // based fee is - // not yet - // supported for - // savings + final BigDecimal transactionAmount = new BigDecimal(0); + populateDerivedFields(transactionAmount, chargeAmount); + resetOutstandingAmount();// reset only if it's withdrawal or annual fee this.paid = determineIfFullyPaid(); + this.status = status; + } + + private void resetOutstandingAmount() { + if (this.isWithdrawalFee()) { + this.amountOutstanding = BigDecimal.ZERO; + } } private void populateDerivedFields(final BigDecimal transactionAmount, final BigDecimal chargeAmount) { @@ -219,6 +252,9 @@ public class SavingsAccountCharge extends AbstractPersistable { amountPaid = amountPaid.minus(transactionAmount); this.amountPaid = amountPaid.getAmount(); this.amountOutstanding = calculateAmountOutstanding(currency); + if (this.isWithdrawalFee()) { + this.amountOutstanding = BigDecimal.ZERO; + } this.paid = false; } @@ -228,7 +264,7 @@ public class SavingsAccountCharge extends AbstractPersistable { this.waived = true; return getAmountWaived(currency); } - + public void undoWaiver(final MonetaryCurrency currency, final Money transactionAmount) { Money amountWaived = getAmountWaived(currency); amountWaived = amountWaived.minus(transactionAmount); @@ -257,16 +293,17 @@ public class SavingsAccountCharge extends AbstractPersistable { this.savingsAccount = savingsAccount; } - public void update(final BigDecimal amount, final LocalDate dueDate) { - final BigDecimal transactionAmount = BigDecimal.ZERO;// percentage based - // fee calculation - // is not yet - // supported for - // Savings + public void update(final BigDecimal amount, final LocalDate dueDate, final MonthDay feeOnMonthDay) { + final BigDecimal transactionAmount = BigDecimal.ZERO; if (dueDate != null) { this.dueDate = dueDate.toDate(); } + if (feeOnMonthDay != null) { + this.feeOnMonth = feeOnMonthDay.getMonthOfYear(); + this.feeOnDay = feeOnMonthDay.getDayOfMonth(); + } + if (amount != null) { switch (ChargeCalculationType.fromInt(this.chargeCalculation)) { case INVALID: @@ -313,6 +350,24 @@ public class SavingsAccountCharge extends AbstractPersistable { this.dueDate = newValue.toDate(); } + if (command.hasParameter(feeOnMonthDayParamName)) { + final MonthDay monthDay = command.extractMonthDayNamed(feeOnMonthDayParamName); + final String actualValueEntered = command.stringValueOfParameterNamed(feeOnMonthDayParamName); + final Integer dayOfMonthValue = monthDay.getDayOfMonth(); + if (this.feeOnDay != dayOfMonthValue) { + actualChanges.put(feeOnMonthDayParamName, actualValueEntered); + actualChanges.put(localeParamName, localeAsInput); + this.feeOnDay = dayOfMonthValue; + } + + final Integer monthOfYear = monthDay.getMonthOfYear(); + if (this.feeOnMonth != monthOfYear) { + actualChanges.put(feeOnMonthDayParamName, actualValueEntered); + actualChanges.put(localeParamName, localeAsInput); + this.feeOnMonth = monthOfYear; + } + } + if (command.isChangeInBigDecimalParameterNamed(amountParamName, this.amount)) { final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(amountParamName); actualChanges.put(amountParamName, newValue); @@ -322,6 +377,7 @@ public class SavingsAccountCharge extends AbstractPersistable { break; case FLAT: this.amount = newValue; + this.amountOutstanding = calculateOutstanding(); break; case PERCENT_OF_AMOUNT: this.percentage = newValue; @@ -347,10 +403,6 @@ public class SavingsAccountCharge extends AbstractPersistable { return actualChanges; } - public boolean isSpecifiedDueDate() { - return ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.SPECIFIED_DUE_DATE); - } - private boolean isGreaterThanZero(final BigDecimal value) { return value.compareTo(BigDecimal.ZERO) == 1; } @@ -405,8 +457,8 @@ public class SavingsAccountCharge extends AbstractPersistable { public BigDecimal amount() { return this.amount; } - - public BigDecimal amoutOutstanding(){ + + public BigDecimal amoutOutstanding() { return this.amountOutstanding; } @@ -452,10 +504,10 @@ public class SavingsAccountCharge extends AbstractPersistable { return Money.of(currency, this.amountWrittenOff); } - public Money getAmountOutstanding(final MonetaryCurrency currency){ + public Money getAmountOutstanding(final MonetaryCurrency currency) { return Money.of(currency, this.amountOutstanding); } - + /** * @param incrementBy * Amount used to pay off this charge @@ -498,19 +550,35 @@ public class SavingsAccountCharge extends AbstractPersistable { return this.charge; } + public SavingsAccount savingsAccount() { + return this.savingsAccount; + } + public boolean isOnSpecifiedDueDate() { return ChargeTimeType.fromInt(this.chargeTime).isOnSpecifiedDueDate(); } - public SavingsAccount savingsAccount(){ - return this.savingsAccount; + public boolean isSavingsActivation() { + return ChargeTimeType.fromInt(this.chargeTime).isSavingsActivation(); } - + + public boolean isSavingsClosure() { + return ChargeTimeType.fromInt(this.chargeTime).isSavingsClosure(); + } + + public boolean isWithdrawalFee() { + return ChargeTimeType.fromInt(this.chargeTime).isWithdrawalFee(); + } + + public boolean isAnnualFee() { + return ChargeTimeType.fromInt(this.chargeTime).isAnnualFee(); + } + public boolean hasCurrencyCodeOf(final String matchingCurrencyCode) { if (this.currencyCode() == null || matchingCurrencyCode == null) { return false; } return this.currencyCode().equalsIgnoreCase(matchingCurrencyCode); } - + @Override public boolean equals(final Object obj) { if (obj == null) { return false; } @@ -533,4 +601,47 @@ public class SavingsAccountCharge extends AbstractPersistable { .append(this.amount).append(getDueLocalDate()) // .toHashCode(); } + + public BigDecimal updateWithdralFeeAmount(final BigDecimal transactionAmount) { + BigDecimal amountPaybale = BigDecimal.ZERO; + if (ChargeCalculationType.fromInt(this.chargeCalculation).isFlat()) { + amountPaybale = this.amount; + } else if (ChargeCalculationType.fromInt(this.chargeCalculation).isPercentageOfAmount()) { + amountPaybale = transactionAmount.multiply(this.percentage).divide(BigDecimal.valueOf(100l)); + } + this.amountOutstanding = amountPaybale; + return amountPaybale; + } + + public void updateToNextAnnualFeeDueDateFrom(final LocalDate startingDate) { + if (isAnnualFee()) { + LocalDate nextDueLocalDate = startingDate.withMonthOfYear(this.feeOnMonth).withDayOfMonth(this.feeOnDay); + if (startingDate.isAfter(nextDueLocalDate)) { + nextDueLocalDate = nextDueLocalDate.plusYears(1); + } + this.dueDate = nextDueLocalDate.toDate(); + } + } + + public void updateToNextAnnualFeeDueDate() { + if (isAnnualFee()) { + LocalDate nextDueLocalDate = new LocalDate(dueDate); + this.dueDate = nextDueLocalDate.withMonthOfYear(this.feeOnMonth).withDayOfMonth(this.feeOnDay).plusYears(1).toDate(); + } + } + + public void updateToPreviousAnnualFeeDueDate() { + if (isAnnualFee()) { + LocalDate previousDueLocalDate = new LocalDate(dueDate); + this.dueDate = previousDueLocalDate.withMonthOfYear(this.feeOnMonth).withDayOfMonth(this.feeOnDay).minusYears(1).toDate(); + } + } + + public boolean feeSettingsNotSet() { + return !feeSettingsSet(); + } + + public boolean feeSettingsSet() { + return this.feeOnDay != null && this.feeOnMonth != null; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountChargeAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountChargeAssembler.java index 2fa0b7b85..199520f89 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountChargeAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountChargeAssembler.java @@ -12,6 +12,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdPa import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeTimeTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargesParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.idParamName; import java.math.BigDecimal; @@ -22,6 +23,7 @@ import java.util.Locale; import java.util.Set; import org.joda.time.LocalDate; +import org.joda.time.MonthDay; import org.mifosplatform.infrastructure.core.data.ApiParameterError; import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; @@ -61,6 +63,7 @@ public class SavingsAccountChargeAssembler { if (element.isJsonObject()) { final JsonObject topLevelJsonElement = element.getAsJsonObject(); final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement); + final String monthDayFormat = this.fromApiJsonHelper.extractMonthDayFormatParameter(topLevelJsonElement); final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement); if (topLevelJsonElement.has(chargesParamName) && topLevelJsonElement.get(chargesParamName).isJsonArray()) { final JsonArray array = topLevelJsonElement.get(chargesParamName).getAsJsonArray(); @@ -78,6 +81,8 @@ public class SavingsAccountChargeAssembler { final LocalDate dueDate = this.fromApiJsonHelper.extractLocalDateNamed(dueAsOfDateParamName, savingsChargeElement, dateFormat, locale); + final MonthDay feeOnMonthDay = this.fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, savingsChargeElement, monthDayFormat, locale); + if (id == null) { final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeId); @@ -95,8 +100,10 @@ public class SavingsAccountChargeAssembler { if (chargeCalculationType != null) { ChargeCalculationType.fromInt(chargeCalculationType); } + + final boolean status = true; final SavingsAccountCharge savingsAccountCharge = SavingsAccountCharge.createNewWithoutSavingsAccount( - chargeDefinition, amount, chargeTime, chargeCalculation, dueDate); + chargeDefinition, amount, chargeTime, chargeCalculation, dueDate, status, feeOnMonthDay); savingsAccountCharges.add(savingsAccountCharge); } else { final Long savingsAccountChargeId = id; @@ -104,7 +111,7 @@ public class SavingsAccountChargeAssembler { .findOne(savingsAccountChargeId); if (savingsAccountCharge == null) { throw new SavingsAccountChargeNotFoundException(savingsAccountChargeId); } - savingsAccountCharge.update(amount, dueDate); + savingsAccountCharge.update(amount, dueDate, feeOnMonthDay); savingsAccountCharges.add(savingsAccountCharge); } @@ -120,11 +127,27 @@ public class SavingsAccountChargeAssembler { final List dataValidationErrors = new ArrayList(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource(SAVINGS_ACCOUNT_RESOURCE_NAME); - for (SavingsAccountCharge savingsAccountCharge : charges) { - if(!savingsAccountCharge.hasCurrencyCodeOf(productCurrencyCode)){ - baseDataValidator.reset().parameter("currency").value(savingsAccountCharge.getCharge().getId()) + boolean isOneWithdrawalPresent = false; + boolean isOneAnnualPresent = false; + for (SavingsAccountCharge charge : charges) { + if(!charge.hasCurrencyCodeOf(productCurrencyCode)){ + baseDataValidator.reset().parameter("currency").value(charge.getCharge().getId()) .failWithCodeNoParameterAddedToErrorCode("currency.and.charge.currency.not.same"); } + + if (charge.isWithdrawalFee()) { + if (isOneWithdrawalPresent) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("multiple.withdrawal.fee.per.account.not.supported"); + } + isOneWithdrawalPresent = true; + } + + if (charge.isAnnualFee()) { + if (isOneAnnualPresent) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("multiple.annual.fee.per.account.not.supported"); + } + isOneAnnualPresent = true; + } } if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } 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 a4865e2b2..77765cc0e 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 @@ -116,7 +116,7 @@ public final class SavingsAccountTransaction extends AbstractPersistable { amount, isReversed); } - public static SavingsAccountTransaction fee(final SavingsAccount savingsAccount, final Office office, final LocalDate date, + public static SavingsAccountTransaction withdrawalFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date, final Money amount) { final boolean isReversed = false; return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue(), date, amount, @@ -211,7 +211,7 @@ public final class SavingsAccountTransaction extends AbstractPersistable { } public boolean isPostInterestCalculationRequired(){ - return this.isDeposit() || this.isWithdrawal() || this.isCharge(); + return this.isDeposit() || this.isWithdrawal() || this.isPayCharge(); } public boolean isInterestPostingAndNotReversed() { @@ -221,6 +221,10 @@ public final class SavingsAccountTransaction extends AbstractPersistable { public boolean isWithdrawalFeeAndNotReversed() { return SavingsAccountTransactionType.fromInt(this.typeOf).isWithdrawalFee() && isNotReversed(); } + + public boolean isWithdrawalFee() { + return SavingsAccountTransactionType.fromInt(this.typeOf).isWithdrawalFee(); + } public boolean isAnnualFeeAndNotReversed() { return isAnnualFee() && isNotReversed(); @@ -456,11 +460,15 @@ public final class SavingsAccountTransaction extends AbstractPersistable { } public boolean isDebit() { - return isWithdrawal() || isWithdrawalFeeAndNotReversed() || isAnnualFeeAndNotReversed() || isCharge(); + return isWithdrawal() || isWithdrawalFeeAndNotReversed() || isAnnualFeeAndNotReversed() || isPayCharge(); } - public boolean isCharge() { - return SavingsAccountTransactionType.fromInt(this.typeOf).isCharge(); + public boolean isPayCharge() { + return SavingsAccountTransactionType.fromInt(this.typeOf).isPayCharge(); + } + + public boolean isChargeTransaction(){ + return SavingsAccountTransactionType.fromInt(this.typeOf).isChargeTransaction(); } public boolean isWaiveCharge(){ @@ -469,12 +477,12 @@ public final class SavingsAccountTransaction extends AbstractPersistable { public boolean isFeeCharge() { final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy(); - return (isCharge() && chargePaidBy != null) ? chargePaidBy.isFeeCharge() : false; + return (isPayCharge() && chargePaidBy != null) ? chargePaidBy.isFeeCharge() : false; } public boolean isPenaltyCharge() { final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy(); - return (isCharge() && chargePaidBy != null) ? chargePaidBy.isPenaltyCharge() : false; + return (isPayCharge() && chargePaidBy != null) ? chargePaidBy.isPenaltyCharge() : false; } public boolean isFeeChargeAndNotReversed() { 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 6aff92596..d45cde179 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 @@ -7,8 +7,6 @@ package org.mifosplatform.portfolio.savings.domain; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountingRuleParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeAmountParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeOnMonthDayParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargesParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName; @@ -24,9 +22,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeri import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nameParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeAmountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeTypeParamName; import java.math.BigDecimal; import java.util.ArrayList; @@ -44,7 +40,6 @@ import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.Table; -import org.joda.time.MonthDay; import org.mifosplatform.accounting.common.AccountingRuleType; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.ApiParameterError; @@ -58,7 +53,6 @@ import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearT import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType; -import org.mifosplatform.portfolio.savings.SavingsWithdrawalFeesType; import org.springframework.data.jpa.domain.AbstractPersistable; import com.google.gson.JsonArray; @@ -123,24 +117,9 @@ public class SavingsProduct extends AbstractPersistable { @Column(name = "accounting_type", nullable = false) private Integer accountingRule; - @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; - @Column(name = "withdrawal_fee_for_transfer") private boolean withdrawalFeeApplicableForTransfer; - @Column(name = "annual_fee_amount", scale = 6, precision = 19, nullable = true) - private BigDecimal annualFeeAmount; - - @Column(name = "annual_fee_on_month", nullable = true) - private Integer annualFeeOnMonth; - - @Column(name = "annual_fee_on_day", nullable = true) - private Integer annualFeeOnDay; - @ManyToMany @JoinTable(name = "m_savings_product_charge", joinColumns = @JoinColumn(name = "savings_product_id"), inverseJoinColumns = @JoinColumn(name = "charge_id")) private Set charges; @@ -150,14 +129,11 @@ public class SavingsProduct extends AbstractPersistable { final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, - final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType, - final boolean withdrawalFeeApplicableForTransfer, final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay, - final AccountingRuleType accountingRuleType, final Set charges) { + final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set charges) { return new SavingsProduct(name, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, - lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, withdrawalFeeApplicableForTransfer, annualFeeAmount, - annualFeeOnMonthDay, accountingRuleType, charges); + lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges); } protected SavingsProduct() { @@ -170,9 +146,7 @@ public class SavingsProduct extends AbstractPersistable { final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, - final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType, - final boolean withdrawalFeeApplicableForTransfer, final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay, - final AccountingRuleType accountingRuleType, final Set charges) { + final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set charges) { this.name = name; this.description = description; @@ -192,17 +166,13 @@ public class SavingsProduct extends AbstractPersistable { if (lockinPeriodFrequency != null && lockinPeriodFrequencyType != null) { this.lockinPeriodFrequencyType = lockinPeriodFrequencyType.getValue(); } - - this.withdrawalFeeAmount = withdrawalFeeAmount; - if (withdrawalFeeAmount != null && withdrawalFeeType != null) { - this.withdrawalFeeType = withdrawalFeeType.getValue(); - } + this.withdrawalFeeApplicableForTransfer = withdrawalFeeApplicableForTransfer; - this.annualFeeAmount = annualFeeAmount; + /* this.annualFeeAmount = annualFeeAmount; if (annualFeeAmount != null && annualFeeOnMonthDay != null) { this.annualFeeOnMonth = annualFeeOnMonthDay.getMonthOfYear(); this.annualFeeOnDay = annualFeeOnMonthDay.getDayOfMonth(); - } + }*/ if (accountingRuleType != null) { this.accountingRule = accountingRuleType.getValue(); @@ -213,8 +183,8 @@ public class SavingsProduct extends AbstractPersistable { } validateLockinDetails(); - validateWithdrawalFeeDetails(); - validateAnnualFeeDetails(); + //validateWithdrawalFeeDetails(); + //validateAnnualFeeDetails(); } public MonetaryCurrency currency() { @@ -257,30 +227,6 @@ 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 BigDecimal annualFeeAmount() { - return this.annualFeeAmount; - } - - public MonthDay monthDayOfAnnualFee() { - MonthDay monthDay = null; - if (this.annualFeeOnMonth != null && this.annualFeeOnDay != null) { - monthDay = new MonthDay(this.annualFeeOnMonth, this.annualFeeOnDay); - } - return monthDay; - } - public Map update(final JsonCommand command) { final Map actualChanges = new LinkedHashMap(10); @@ -382,7 +328,7 @@ public class SavingsProduct extends AbstractPersistable { if (this.lockinPeriodFrequency == null) { this.lockinPeriodFrequencyType = null; } - +/* if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(withdrawalFeeAmountParamName, this.withdrawalFeeAmount)) { final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(withdrawalFeeAmountParamName); actualChanges.put(withdrawalFeeAmountParamName, newValue); @@ -395,7 +341,7 @@ public class SavingsProduct extends AbstractPersistable { actualChanges.put(withdrawalFeeTypeParamName, newValue); this.withdrawalFeeType = newValue != null ? SavingsWithdrawalFeesType.fromInt(newValue).getValue() : newValue; } - +*/ if (command.isChangeInBooleanParameterNamed(withdrawalFeeForTransfersParamName, this.withdrawalFeeApplicableForTransfer)) { final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(withdrawalFeeForTransfersParamName); actualChanges.put(withdrawalFeeForTransfersParamName, newValue); @@ -403,40 +349,40 @@ public class SavingsProduct extends AbstractPersistable { } // set period type to null if frequency is null - if (this.withdrawalFeeAmount == null) { - this.withdrawalFeeType = null; - } +// if (this.withdrawalFeeAmount == null) { +// this.withdrawalFeeType = null; +// } - if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(annualFeeAmountParamName, this.annualFeeAmount)) { - final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(annualFeeAmountParamName); - actualChanges.put(annualFeeAmountParamName, newValue); +/* if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(feeAmountParamName, this.annualFeeAmount)) { + final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(feeAmountParamName); + actualChanges.put(feeAmountParamName, newValue); actualChanges.put(localeParamName, localeAsInput); this.annualFeeAmount = newValue; } - if (command.isChangeInIntegerParameterNamedDefaultingZeroToNull(annualFeeOnMonthDayParamName, this.annualFeeOnDay)) { - final MonthDay monthDay = command.extractMonthDayNamed(annualFeeOnMonthDayParamName); - final String actualValueEntered = command.stringValueOfParameterNamed(annualFeeOnMonthDayParamName); + if (command.isChangeInIntegerParameterNamedDefaultingZeroToNull(feeOnMonthDayParamName, this.annualFeeOnDay)) { + final MonthDay monthDay = command.extractMonthDayNamed(feeOnMonthDayParamName); + final String actualValueEntered = command.stringValueOfParameterNamed(feeOnMonthDayParamName); final Integer newValue = monthDay != null ? monthDay.getDayOfMonth() : null; - actualChanges.put(annualFeeOnMonthDayParamName, actualValueEntered); + actualChanges.put(feeOnMonthDayParamName, actualValueEntered); actualChanges.put(localeParamName, localeAsInput); this.annualFeeOnDay = newValue; } - if (command.isChangeInIntegerParameterNamedDefaultingZeroToNull(annualFeeOnMonthDayParamName, this.annualFeeOnMonth)) { - final MonthDay monthDay = command.extractMonthDayNamed(annualFeeOnMonthDayParamName); - final String actualValueEntered = command.stringValueOfParameterNamed(annualFeeOnMonthDayParamName); + if (command.isChangeInIntegerParameterNamedDefaultingZeroToNull(feeOnMonthDayParamName, this.annualFeeOnMonth)) { + final MonthDay monthDay = command.extractMonthDayNamed(feeOnMonthDayParamName); + final String actualValueEntered = command.stringValueOfParameterNamed(feeOnMonthDayParamName); final Integer newValue = monthDay != null ? monthDay.getMonthOfYear() : null; - actualChanges.put(annualFeeOnMonthDayParamName, actualValueEntered); + actualChanges.put(feeOnMonthDayParamName, actualValueEntered); actualChanges.put(localeParamName, localeAsInput); this.annualFeeOnMonth = newValue; - } + }*/ - // set period type to null if frequency is null - if (this.annualFeeAmount == null) { - this.annualFeeOnDay = null; - this.annualFeeOnMonth = null; - } +// // set period type to null if frequency is null +// if (this.annualFeeAmount == null) { +// this.annualFeeOnDay = null; +// this.annualFeeOnMonth = null; +// } if (command.isChangeInIntegerParameterNamed(accountingRuleParamName, this.accountingRule)) { final Integer newValue = command.integerValueOfParameterNamed(accountingRuleParamName); @@ -453,13 +399,13 @@ public class SavingsProduct extends AbstractPersistable { } validateLockinDetails(); - validateWithdrawalFeeDetails(); - validateAnnualFeeDetails(); + //validateWithdrawalFeeDetails(); + //validateAnnualFeeDetails(); return actualChanges; } - private void validateAnnualFeeDetails() { +/* private void validateAnnualFeeDetails() { final List dataValidationErrors = new ArrayList(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) @@ -468,21 +414,21 @@ public class SavingsProduct extends AbstractPersistable { if (this.annualFeeAmount == null) { if (this.annualFeeOnMonth != null || this.annualFeeOnDay != null) { - baseDataValidator.reset().parameter(annualFeeAmountParamName).value(this.annualFeeAmount).notNull(); + baseDataValidator.reset().parameter(feeAmountParamName).value(this.annualFeeAmount).notNull(); } } else { if (this.annualFeeOnMonth == null || this.annualFeeOnDay == null) { - baseDataValidator.reset().parameter(annualFeeOnMonthDayParamName).value(this.annualFeeOnMonth).notNull(); + baseDataValidator.reset().parameter(feeOnMonthDayParamName).value(this.annualFeeOnMonth).notNull(); } - baseDataValidator.reset().parameter(annualFeeAmountParamName).value(this.annualFeeAmount).zeroOrPositiveAmount(); + baseDataValidator.reset().parameter(feeAmountParamName).value(this.annualFeeAmount).zeroOrPositiveAmount(); } if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - - private void validateWithdrawalFeeDetails() { +*/ +/* private void validateWithdrawalFeeDetails() { final List dataValidationErrors = new ArrayList(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) @@ -503,7 +449,7 @@ public class SavingsProduct extends AbstractPersistable { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - +*/ private void validateLockinDetails() { final List dataValidationErrors = new ArrayList(); 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 efaf39812..6f7f3bb8d 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 @@ -5,8 +5,6 @@ */ package org.mifosplatform.portfolio.savings.domain; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeAmountParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeOnMonthDayParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargesParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName; @@ -22,15 +20,12 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeri import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nameParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeAmountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeTypeParamName; import java.math.BigDecimal; import java.util.HashSet; import java.util.Set; -import org.joda.time.MonthDay; import org.mifosplatform.accounting.common.AccountingRuleType; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; @@ -43,7 +38,6 @@ import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearT import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType; -import org.mifosplatform.portfolio.savings.SavingsWithdrawalFeesType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -107,20 +101,11 @@ public class SavingsProductAssembler { lockinPeriodFrequencyType = SavingsPeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue); } - final BigDecimal withdrawalFeeAmount = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(withdrawalFeeAmountParamName); - SavingsWithdrawalFeesType withdrawalFeeType = null; - final Integer withdrawalFeeTypeValue = command.integerValueOfParameterNamed(withdrawalFeeTypeParamName); - if (withdrawalFeeTypeValue != null) { - withdrawalFeeType = SavingsWithdrawalFeesType.fromInt(withdrawalFeeTypeValue); - } - boolean iswithdrawalFeeApplicableForTransfer = false; if (command.parameterExists(withdrawalFeeForTransfersParamName)) { iswithdrawalFeeApplicableForTransfer = command.booleanPrimitiveValueOfParameterNamed(withdrawalFeeForTransfersParamName); } - final BigDecimal annualFeeAmount = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(annualFeeAmountParamName); - final MonthDay monthDayOfAnnualFee = command.extractMonthDayNamed(annualFeeOnMonthDayParamName); final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(command.integerValueOfParameterNamed("accountingRule")); // Savings product charges @@ -128,8 +113,7 @@ public class SavingsProductAssembler { return SavingsProduct.createNew(name, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, - lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, - iswithdrawalFeeApplicableForTransfer, annualFeeAmount, monthDayOfAnnualFee, accountingRuleType, charges); + lockinPeriodFrequency, lockinPeriodFrequencyType, iswithdrawalFeeApplicableForTransfer, accountingRuleType, charges); } public Set assembleListOfSavingsProductCharges(final JsonCommand command, final String savingsProductCurrencyCode) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductChargeAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductChargeAssembler.java index a23fc8caa..a29640cc5 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductChargeAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsProductChargeAssembler.java @@ -11,6 +11,7 @@ import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeIdPa import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargeTimeTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.chargesParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.feeOnMonthDayParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.idParamName; import java.math.BigDecimal; @@ -19,6 +20,7 @@ import java.util.Locale; import java.util.Set; import org.joda.time.LocalDate; +import org.joda.time.MonthDay; import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.portfolio.charge.domain.Charge; import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType; @@ -70,7 +72,8 @@ public class SavingsProductChargeAssembler { savingsChargeElement, locale); final LocalDate dueDate = this.fromApiJsonHelper.extractLocalDateNamed(dueAsOfDateParamName, savingsChargeElement, dateFormat, locale); - + final MonthDay feeOnMonthDay = this.fromApiJsonHelper.extractMonthDayNamed(feeOnMonthDayParamName, savingsChargeElement); + if (id == null) { final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeId); final ChargeTimeType chargeTime = null; @@ -82,7 +85,7 @@ public class SavingsProductChargeAssembler { ChargeCalculationType.fromInt(chargeCalculationType); } final SavingsAccountCharge savingsAccountCharge = SavingsAccountCharge.createNewWithoutSavingsAccount( - chargeDefinition, amount, chargeTime, chargeCalculation, dueDate); + chargeDefinition, amount, chargeTime, chargeCalculation, dueDate, true, feeOnMonthDay); savingsAccountCharges.add(savingsAccountCharge); } else { final Long savingsAccountChargeId = id; @@ -90,7 +93,7 @@ public class SavingsProductChargeAssembler { .findOne(savingsAccountChargeId); if (savingsAccountCharge == null) { throw new SavingsAccountChargeNotFoundException(savingsAccountChargeId); } - savingsAccountCharge.update(amount, dueDate); + savingsAccountCharge.update(amount, dueDate, null); savingsAccountCharges.add(savingsAccountCharge); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/ApplyAnnualFeeSavingsAccountCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/ApplyAnnualFeeSavingsAccountCommandHandler.java index 1e0cf8ee6..1ffb43227 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/ApplyAnnualFeeSavingsAccountCommandHandler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/handler/ApplyAnnualFeeSavingsAccountCommandHandler.java @@ -17,6 +17,7 @@ import org.springframework.transaction.annotation.Transactional; @Service public class ApplyAnnualFeeSavingsAccountCommandHandler implements NewCommandSourceHandler { + @SuppressWarnings("unused") private final SavingsAccountWritePlatformService writePlatformService; @Autowired @@ -28,8 +29,10 @@ public class ApplyAnnualFeeSavingsAccountCommandHandler implements NewCommandSou @Override public CommandProcessingResult processCommand(final JsonCommand command) { + @SuppressWarnings("unused") final LocalDate annualFeeTransactionDate = command.localDateValueOfParameterNamed("annualFeeTransactionDate"); - return this.writePlatformService.applyAnnualFee(command.getSavingsId(), annualFeeTransactionDate); + //return this.writePlatformService.applyAnnualFee(command.getSavingsId(), annualFeeTransactionDate); + return null; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountChargeReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountChargeReadPlatformServiceImpl.java index adf756355..845cfe3be 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountChargeReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountChargeReadPlatformServiceImpl.java @@ -12,6 +12,7 @@ import java.util.Collection; import java.util.List; import org.joda.time.LocalDate; +import org.joda.time.MonthDay; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.service.RoutingDataSource; @@ -55,7 +56,9 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou + "sc.calculation_percentage as percentageOf, sc.calculation_on_amount as amountPercentageAppliedTo, " + "sc.charge_time_enum as chargeTime, " + "sc.is_penalty as penalty, " - + "sc.due_for_collection_as_of_date as dueAsOfDate, " + + "sc.charge_due_date as dueAsOfDate, " + + "sc.fee_on_month as feeOnMonth, " + + "sc.fee_on_day as feeOnDay, " + "sc.charge_calculation_enum as chargeCalculation, " + "c.currency_code as currencyCode, oc.name as currencyName, " + "oc.decimal_places as currencyDecimalPlaces, oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, " @@ -93,7 +96,13 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(chargeTime); final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate"); - + MonthDay feeOnMonthDay = null; + final Integer feeOnMonth = JdbcSupport.getInteger(rs, "feeOnMonth"); + final Integer feeOnDay = JdbcSupport.getInteger(rs, "feeOnDay"); + if (feeOnDay != null) { + feeOnMonthDay = new MonthDay(feeOnMonth, feeOnDay); + } + final int chargeCalculation = rs.getInt("chargeCalculation"); final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation); final boolean penalty = rs.getBoolean("penalty"); @@ -102,7 +111,7 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou return SavingsAccountChargeData.instance(id, chargeId, name, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding, chargeTimeType, dueAsOfDate, chargeCalculationType, percentageOf, amountPercentageAppliedTo, - chargeOptions, penalty); + chargeOptions, penalty, feeOnMonthDay); } } @@ -148,7 +157,7 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou final SavingsAccountChargeMapper rm = new SavingsAccountChargeMapper(); final String sql = "select " + rm.schema() + " where sc.savings_account_id=? " - + " order by sc.charge_time_enum ASC, sc.due_for_collection_as_of_date ASC, sc.is_penalty ASC"; + + " order by sc.charge_time_enum ASC, sc.charge_due_date ASC, sc.is_penalty ASC"; return this.jdbcTemplate.query(sql, rm, new Object[] { loanId }); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java index f09125c94..a05275a49 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountReadPlatformService.java @@ -9,7 +9,6 @@ import java.util.Collection; import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.portfolio.group.service.SearchParameters; -import org.mifosplatform.portfolio.savings.data.SavingsAccountAnnualFeeData; import org.mifosplatform.portfolio.savings.data.SavingsAccountData; import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionData; @@ -27,7 +26,7 @@ public interface SavingsAccountReadPlatformService { Collection retrieveAllTransactions(Long savingsId); - Collection retrieveAccountsWithAnnualFeeDue(); + //Collection retrieveAccountsWithAnnualFeeDue(); SavingsAccountTransactionData retrieveSavingsTransaction(Long savingsId, Long transactionId); } \ 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 62cef05c7..8fba2e734 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 @@ -14,7 +14,6 @@ import java.util.Collection; import org.apache.commons.lang.StringUtils; import org.joda.time.LocalDate; -import org.joda.time.MonthDay; import org.mifosplatform.infrastructure.codes.data.CodeValueData; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.domain.JdbcSupport; @@ -40,7 +39,6 @@ import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearT import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType; import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType; -import org.mifosplatform.portfolio.savings.data.SavingsAccountAnnualFeeData; import org.mifosplatform.portfolio.savings.data.SavingsAccountApplicationTimelineData; import org.mifosplatform.portfolio.savings.data.SavingsAccountChargeData; import org.mifosplatform.portfolio.savings.data.SavingsAccountData; @@ -74,7 +72,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead private final SavingsAccountTransactionTemplateMapper transactionTemplateMapper; private final SavingsAccountTransactionsMapper transactionsMapper; private final SavingAccountMapper savingAccountMapper; - private final SavingsAccountAnnualFeeMapper annualFeeMapper; + //private final SavingsAccountAnnualFeeMapper annualFeeMapper; // pagination private final PaginationHelper paginationHelper = new PaginationHelper(); @@ -95,7 +93,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead this.transactionTemplateMapper = new SavingsAccountTransactionTemplateMapper(); this.transactionsMapper = new SavingsAccountTransactionsMapper(); this.savingAccountMapper = new SavingAccountMapper(); - this.annualFeeMapper = new SavingsAccountAnnualFeeMapper(); + //this.annualFeeMapper = new SavingsAccountAnnualFeeMapper(); this.chargeReadPlatformService = chargeReadPlatformService; } @@ -222,13 +220,13 @@ 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.withdrawal_fee_amount as withdrawalFeeAmount,"); +// sqlBuilder.append("sa.withdrawal_fee_type_enum as withdrawalFeeTypeEnum, "); sqlBuilder.append("sa.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, "); - sqlBuilder.append("sa.annual_fee_amount as annualFeeAmount,"); - sqlBuilder.append("sa.annual_fee_on_month as annualFeeOnMonth, "); - sqlBuilder.append("sa.annual_fee_on_day as annualFeeOnDay, "); - sqlBuilder.append("sa.annual_fee_next_due_date as annualFeeNextDueDate, "); +// sqlBuilder.append("sa.annual_fee_amount as annualFeeAmount,"); +// sqlBuilder.append("sa.annual_fee_on_month as annualFeeOnMonth, "); +// sqlBuilder.append("sa.annual_fee_on_day as annualFeeOnDay, "); +// sqlBuilder.append("sa.annual_fee_next_due_date as annualFeeNextDueDate, "); 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, "); @@ -351,17 +349,17 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodType); } - final BigDecimal withdrawalFeeAmount = rs.getBigDecimal("withdrawalFeeAmount"); + /*final BigDecimal withdrawalFeeAmount = rs.getBigDecimal("withdrawalFeeAmount"); EnumOptionData withdrawalFeeType = null; final Integer withdrawalFeeTypeValue = JdbcSupport.getInteger(rs, "withdrawalFeeTypeEnum"); if (withdrawalFeeTypeValue != null) { withdrawalFeeType = SavingsEnumerations.withdrawalFeeType(withdrawalFeeTypeValue); - } + }*/ final boolean withdrawalFeeForTransfers = rs.getBoolean("withdrawalFeeForTransfers"); - final BigDecimal annualFeeAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "annualFeeAmount"); + /*final BigDecimal annualFeeAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "annualFeeAmount"); MonthDay annualFeeOnMonthDay = null; final Integer annualFeeOnMonth = JdbcSupport.getInteger(rs, "annualFeeOnMonth"); @@ -371,7 +369,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead } final LocalDate annualFeeNextDueDate = JdbcSupport.getLocalDate(rs, "annualFeeNextDueDate"); - +*/ final BigDecimal totalDeposits = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalDeposits"); final BigDecimal totalWithdrawals = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalWithdrawals"); final BigDecimal totalWithdrawalFees = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalWithdrawalFees"); @@ -390,8 +388,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead return SavingsAccountData.instance(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, - lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, withdrawalFeeForTransfers, - annualFeeAmount, annualFeeOnMonthDay, annualFeeNextDueDate, summary); + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary); } } @@ -554,13 +551,13 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead return this.jdbcTemplate.queryForObject(sql, this.transactionsMapper, new Object[] { savingsId, transactionId }); } - @Override + /*@Override public Collection retrieveAccountsWithAnnualFeeDue() { final String sql = "select " + this.annualFeeMapper.schema() + " where sa.annual_fee_next_due_date is not null and sa.annual_fee_next_due_date <= NOW()"; return this.jdbcTemplate.query(sql, this.annualFeeMapper, new Object[] {}); - } + }*/ private static final class SavingsAccountTransactionsMapper implements RowMapper { @@ -732,12 +729,12 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead 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.withdrawal_fee_amount as withdrawalFeeAmount,"); - sqlBuilder.append("sp.withdrawal_fee_type_enum as withdrawalFeeTypeEnum, "); - sqlBuilder.append("sp.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, "); - sqlBuilder.append("sp.annual_fee_amount as annualFeeAmount,"); - sqlBuilder.append("sp.annual_fee_on_month as annualFeeOnMonth, "); - sqlBuilder.append("sp.annual_fee_on_day as annualFeeOnDay "); +// sqlBuilder.append("sp.withdrawal_fee_amount as withdrawalFeeAmount,"); +// sqlBuilder.append("sp.withdrawal_fee_type_enum as withdrawalFeeTypeEnum, "); + sqlBuilder.append("sp.withdrawal_fee_for_transfer as withdrawalFeeForTransfers "); +// sqlBuilder.append("sp.annual_fee_amount as annualFeeAmount,"); +// sqlBuilder.append("sp.annual_fee_on_month as annualFeeOnMonth, "); +// sqlBuilder.append("sp.annual_fee_on_day as annualFeeOnDay "); sqlBuilder.append("from m_savings_product sp "); sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code "); @@ -789,24 +786,24 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodType); } - final BigDecimal withdrawalFeeAmount = rs.getBigDecimal("withdrawalFeeAmount"); +// final BigDecimal withdrawalFeeAmount = rs.getBigDecimal("withdrawalFeeAmount"); - EnumOptionData withdrawalFeeType = null; +/* EnumOptionData withdrawalFeeType = null; final Integer withdrawalFeeTypeValue = JdbcSupport.getInteger(rs, "withdrawalFeeTypeEnum"); if (withdrawalFeeTypeValue != null) { withdrawalFeeType = SavingsEnumerations.withdrawalFeeType(withdrawalFeeTypeValue); } - +*/ final boolean withdrawalFeeForTransfers = rs.getBoolean("withdrawalFeeForTransfers"); - final BigDecimal annualFeeAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "annualFeeAmount"); + //final BigDecimal annualFeeAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "annualFeeAmount"); - MonthDay annualFeeOnMonthDay = null; + /*MonthDay annualFeeOnMonthDay = null; final Integer annualFeeOnMonth = JdbcSupport.getInteger(rs, "annualFeeOnMonth"); final Integer annualFeeOnDay = JdbcSupport.getInteger(rs, "annualFeeOnDay"); if (annualFeeAmount != null && annualFeeOnDay != null) { annualFeeOnMonthDay = new MonthDay(annualFeeOnMonth, annualFeeOnDay); - } + }*/ Long clientId = null; String clientName = null; @@ -825,7 +822,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead final Long fieldOfficerId = null; final String fieldOfficerName = null; final SavingsAccountStatusEnumData status = null; - final LocalDate annualFeeNextDueDate = null; +// final LocalDate annualFeeNextDueDate = null; final SavingsAccountSummaryData summary = null; final SavingsAccountApplicationTimelineData timeline = SavingsAccountApplicationTimelineData.templateDefault(); @@ -833,12 +830,11 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead return SavingsAccountData.instance(null, null, null, groupId, groupName, clientId, clientName, productId, productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualIterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, - lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, withdrawalFeeForTransfers, - annualFeeAmount, annualFeeOnMonthDay, annualFeeNextDueDate, summary); + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary); } } - private static final class SavingsAccountAnnualFeeMapper implements RowMapper { + /*private static final class SavingsAccountAnnualFeeMapper implements RowMapper { private final String schemaSql; @@ -864,5 +860,5 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead return SavingsAccountAnnualFeeData.instance(id, accountNo, annualFeeNextDueDate); } - } + }*/ } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java index 7d02115aa..03c6ce906 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java @@ -20,7 +20,7 @@ public interface SavingsAccountWritePlatformService { CommandProcessingResult withdrawal(Long savingsId, JsonCommand command); - CommandProcessingResult applyAnnualFee(Long savingsId, LocalDate annualFeeTransactionDate); + /*CommandProcessingResult applyAnnualFee(Long savingsId, LocalDate annualFeeTransactionDate);*/ CommandProcessingResult calculateInterest(Long savingsId); 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 f01ef4950..288612d2d 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 @@ -57,6 +57,7 @@ import org.mifosplatform.portfolio.note.domain.Note; import org.mifosplatform.portfolio.note.domain.NoteRepository; import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; import org.mifosplatform.portfolio.paymentdetail.service.PaymentDetailWritePlatformService; +import org.mifosplatform.portfolio.savings.SavingsAccountTransactionType; import org.mifosplatform.portfolio.savings.SavingsApiConstants; import org.mifosplatform.portfolio.savings.data.SavingsAccountChargeDataValidator; import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionDTO; @@ -70,7 +71,6 @@ import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepository; import org.mifosplatform.portfolio.savings.domain.SavingsAccountStatusType; import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransactionRepository; -import org.mifosplatform.portfolio.savings.exception.InsufficientAccountBalanceException; import org.mifosplatform.portfolio.savings.exception.SavingsAccountClosingNotAllowedException; import org.mifosplatform.portfolio.savings.exception.SavingsAccountTransactionNotFoundException; import org.mifosplatform.portfolio.savings.exception.TransactionUpdateNotAllowedException; @@ -152,6 +152,18 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi final Map changes = account.activate(user, command, DateUtils.getLocalDateOfTenant()); if (!changes.isEmpty()) { + + final MathContext mc = MathContext.DECIMAL64; + if (account.isBeforeLastPostingPeriod(account.getActivationLocalDate())) { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.postInterest(mc, today); + } else { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.calculateInterestUsing(mc, today); + } + + account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name()); + this.savingAccountRepository.save(account); } @@ -236,7 +248,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi .build(); } - @Transactional + /*@Transactional @Override public CommandProcessingResult applyAnnualFee(final Long savingsId, final LocalDate annualFeeTransactionDate) { @@ -265,7 +277,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi .withGroupId(account.groupId()) // .withSavingsId(savingsId) // .build(); - } + }*/ @Transactional @Override @@ -356,6 +368,17 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi throwValidationForActiveStatus(SavingsApiConstants.undoTransactionAction); } account.undoTransaction(transactionId); + + // undoing transaction is withdrawal then undo withdrawal fee + // transaction if any + if (savingsAccountTransaction.isWithdrawal()) { + final SavingsAccountTransaction nextSavingsAccountTransaction = this.savingsAccountTransactionRepository + .findOneByIdAndSavingsAccountId(transactionId + 1, savingsId); + if (nextSavingsAccountTransaction != null && nextSavingsAccountTransaction.isWithdrawalFeeAndNotReversed()) { + account.undoTransaction(transactionId + 1); + } + } + checkClientOrGroupActive(account); if (savingsAccountTransaction.isPostInterestCalculationRequired() && account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) { @@ -604,7 +627,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi checkClientOrGroupActive(savingsAccount); final Locale locale = command.extractLocale(); - final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); + final String format = command.dateFormat(); + final DateTimeFormatter fmt = StringUtils.isNotBlank(format) ? DateTimeFormat.forPattern(format).withLocale(locale) : DateTimeFormat.forPattern("dd MM yyyy"); final Long chargeDefinitionId = command.longValueOfParameterNamed(chargeIdParamName); final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeDefinitionId); @@ -810,19 +834,15 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi // always use current date as transaction date for batch job final LocalDate transactionDate = DateUtils.getLocalDateOfTenant(); final List pendingCharges = this.savingsAccountChargeRepository.findPendingCharges(transactionDate.toDate()); - // final List paidCharges = new - // ArrayList(pendingCharges.size()); final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd MM yyyy"); for (SavingsAccountCharge savingsAccountCharge : pendingCharges) { try { - payCharge(savingsAccountCharge, transactionDate, savingsAccountCharge.amoutOutstanding(), fmt); - } catch (InsufficientAccountBalanceException isab) { + payCharge(savingsAccountCharge, transactionDate, savingsAccountCharge.amoutOutstanding(), fmt); + } catch (Exception exception) { // TODO: AA - provide a meaningful error with failed // transactions list } } - - // this.savingsAccountChargeRepository.save(paidCharges); } @Transactional @@ -845,7 +865,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi account.calculateInterestUsing(mc, today); } - account.validateAccountBalanceDoesNotBecomeNegative(amountPaid); + account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name()); this.savingAccountRepository.save(account); 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 9111e6b03..c15fc8ce0 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 @@ -10,7 +10,6 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collection; -import org.joda.time.MonthDay; import org.mifosplatform.accounting.common.AccountingEnumerations; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.domain.JdbcSupport; @@ -87,12 +86,12 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead 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.withdrawal_fee_amount as withdrawalFeeAmount,"); - sqlBuilder.append("sp.withdrawal_fee_type_enum as withdrawalFeeTypeEnum, "); +// sqlBuilder.append("sp.withdrawal_fee_amount as withdrawalFeeAmount,"); +// sqlBuilder.append("sp.withdrawal_fee_type_enum as withdrawalFeeTypeEnum, "); sqlBuilder.append("sp.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, "); - sqlBuilder.append("sp.annual_fee_amount as annualFeeAmount,"); - sqlBuilder.append("sp.annual_fee_on_month as annualFeeOnMonth, "); - sqlBuilder.append("sp.annual_fee_on_day as annualFeeOnDay, "); +// sqlBuilder.append("sp.annual_fee_amount as annualFeeAmount,"); +// sqlBuilder.append("sp.annual_fee_on_month as annualFeeOnMonth, "); +// sqlBuilder.append("sp.annual_fee_on_day as annualFeeOnDay, "); sqlBuilder.append("sp.accounting_type as accountingType "); sqlBuilder.append("from m_savings_product sp "); sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code "); @@ -150,17 +149,17 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodFrequencyTypeValue); } - final BigDecimal withdrawalFeeAmount = rs.getBigDecimal("withdrawalFeeAmount"); + /*final BigDecimal withdrawalFeeAmount = rs.getBigDecimal("withdrawalFeeAmount"); EnumOptionData withdrawalFeeType = null; final Integer withdrawalFeeTypeValue = JdbcSupport.getInteger(rs, "withdrawalFeeTypeEnum"); if (withdrawalFeeTypeValue != null) { withdrawalFeeType = SavingsEnumerations.withdrawalFeeType(withdrawalFeeTypeValue); } - +*/ final boolean withdrawalFeeForTransfers = rs.getBoolean("withdrawalFeeForTransfers"); - final BigDecimal annualFeeAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "annualFeeAmount"); +/* final BigDecimal annualFeeAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "annualFeeAmount"); MonthDay annualFeeOnMonthDay = null; final Integer annualFeeOnMonth = JdbcSupport.getInteger(rs, "annualFeeOnMonth"); @@ -168,11 +167,10 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead if (annualFeeAmount != null && annualFeeOnDay != null) { annualFeeOnMonthDay = new MonthDay(annualFeeOnMonth, annualFeeOnDay); } - +*/ return SavingsProductData.instance(id, name, description, currency, nominalAnnualInterestRate, compoundingInterestPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, - lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, withdrawalFeeForTransfers, - annualFeeAmount, annualFeeOnMonthDay, accountingRuleType); + lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, accountingRuleType); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java index c71632ddb..4359425b2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java @@ -5,16 +5,10 @@ */ package org.mifosplatform.scheduledjobs.service; -import java.util.Collection; -import java.util.List; - -import org.mifosplatform.infrastructure.core.data.ApiParameterError; -import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.service.RoutingDataSourceServiceFactory; import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil; import org.mifosplatform.infrastructure.jobs.annotation.CronTarget; import org.mifosplatform.infrastructure.jobs.service.JobName; -import org.mifosplatform.portfolio.savings.data.SavingsAccountAnnualFeeData; import org.mifosplatform.portfolio.savings.service.SavingsAccountReadPlatformService; import org.mifosplatform.portfolio.savings.service.SavingsAccountWritePlatformService; import org.slf4j.Logger; @@ -30,7 +24,9 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService private final static Logger logger = LoggerFactory.getLogger(ScheduledJobRunnerServiceImpl.class); private final RoutingDataSourceServiceFactory dataSourceServiceFactory; + @SuppressWarnings("unused") private final SavingsAccountWritePlatformService savingsAccountWritePlatformService; + @SuppressWarnings("unused") private final SavingsAccountReadPlatformService savingsAccountReadPlatformService; @Autowired @@ -203,7 +199,7 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService @CronTarget(jobName = JobName.APPLY_ANNUAL_FEE_FOR_SAVINGS) public void applyAnnualFeeForSavings() { - final Collection annualFeeData = this.savingsAccountReadPlatformService + /*final Collection annualFeeData = this.savingsAccountReadPlatformService .retrieveAccountsWithAnnualFeeDue(); for (final SavingsAccountAnnualFeeData savingsAccountReference : annualFeeData) { @@ -219,6 +215,6 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService } } - logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Savings accounts affected by update: " + annualFeeData.size()); + logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Savings accounts affected by update: " + annualFeeData.size());*/ } } \ No newline at end of file