MIFOSX-246: add support for annual fees on product and savings account. need to introduce overnight process to check and update annual fees.

This commit is contained in:
Keith Woodlock
2013-05-02 02:20:23 +01:00
parent f107d185e8
commit cf3c030db7
29 changed files with 809 additions and 353 deletions
@@ -0,0 +1,13 @@
ALTER TABLE `m_savings_product`
ADD COLUMN `annual_fee_amount` DECIMAL(19,6) NULL DEFAULT NULL AFTER `withdrawal_fee_type_enum`,
ADD COLUMN `annual_fee_on_month` SMALLINT(5) NULL DEFAULT NULL AFTER `annual_fee_amount`,
ADD COLUMN `annual_fee_on_day` SMALLINT(5) NULL DEFAULT NULL AFTER `annual_fee_on_month`;
ALTER TABLE `m_savings_account`
ADD COLUMN `annual_fee_amount` DECIMAL(19,6) NULL DEFAULT NULL AFTER `withdrawal_fee_type_enum`,
ADD COLUMN `annual_fee_on_month` SMALLINT(5) NULL DEFAULT NULL AFTER `annual_fee_amount`,
ADD COLUMN `annual_fee_on_day` SMALLINT(5) NULL DEFAULT NULL AFTER `annual_fee_on_month`,
ADD COLUMN `annual_fee_next_due_date` DATE NULL DEFAULT NULL AFTER `annual_fee_on_day`;
ALTER TABLE `m_savings_account`
ADD COLUMN `total_annual_fees_derived` DECIMAL(19,6) NULL DEFAULT NULL AFTER `total_withdrawal_fees_derived`;
@@ -22,7 +22,7 @@ public class JodaDateTimeAdapter implements JsonSerializer<DateTime> {
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Override @Override
public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) { public JsonElement serialize(final DateTime src, final Type typeOfSrc, final JsonSerializationContext context) {
JsonElement element = null; JsonElement element = null;
if (src != null) { if (src != null) {
@@ -23,7 +23,7 @@ public class JodaLocalDateAdapter implements JsonSerializer<LocalDate> {
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Override @Override
public JsonElement serialize(LocalDate src, Type typeOfSrc, JsonSerializationContext context) { public JsonElement serialize(final LocalDate src, final Type typeOfSrc, final JsonSerializationContext context) {
JsonArray array = null; JsonArray array = null;
if (src != null) { if (src != null) {
@@ -0,0 +1,37 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.infrastructure.core.api;
import java.lang.reflect.Type;
import org.joda.time.MonthDay;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
/**
* Serializer for joda time {@link MonthDay} that returns date in array format
* to match previous jackson functionality.
*/
public class JodaMonthDayAdapter implements JsonSerializer<MonthDay> {
@SuppressWarnings("unused")
@Override
public JsonElement serialize(final MonthDay src, final Type typeOfSrc, final JsonSerializationContext context) {
JsonArray array = null;
if (src != null) {
array = new JsonArray();
array.add(new JsonPrimitive(src.getMonthOfYear()));
array.add(new JsonPrimitive(src.getDayOfMonth()));
}
return array;
}
}
@@ -13,6 +13,7 @@ import java.util.Locale;
import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.infrastructure.security.domain.BasicPasswordEncodablePlatformUser; import org.mifosplatform.infrastructure.security.domain.BasicPasswordEncodablePlatformUser;
import org.mifosplatform.infrastructure.security.domain.PlatformUser; import org.mifosplatform.infrastructure.security.domain.PlatformUser;
@@ -261,6 +262,10 @@ public final class JsonCommand {
return this.fromApiJsonHelper.extractLocalDateNamed(parameterName, parsedCommand); return this.fromApiJsonHelper.extractLocalDateNamed(parameterName, parsedCommand);
} }
public MonthDay extractMonthDayNamed(final String parameterName) {
return this.fromApiJsonHelper.extractMonthDayNamed(parameterName, parsedCommand);
}
public Date DateValueOfParameterNamed(final String parameterName) { public Date DateValueOfParameterNamed(final String parameterName) {
LocalDate localDate = this.fromApiJsonHelper.extractLocalDateNamed(parameterName, parsedCommand); LocalDate localDate = this.fromApiJsonHelper.extractLocalDateNamed(parameterName, parsedCommand);
if (localDate == null) { return null; } if (localDate == null) { return null; }
@@ -7,8 +7,10 @@ package org.mifosplatform.infrastructure.core.serialization;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.api.JodaDateTimeAdapter; import org.mifosplatform.infrastructure.core.api.JodaDateTimeAdapter;
import org.mifosplatform.infrastructure.core.api.JodaLocalDateAdapter; import org.mifosplatform.infrastructure.core.api.JodaLocalDateAdapter;
import org.mifosplatform.infrastructure.core.api.JodaMonthDayAdapter;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.google.gson.Gson; import com.google.gson.Gson;
@@ -28,6 +30,7 @@ public final class CommandProcessingResultJsonSerializer {
GsonBuilder builder = new GsonBuilder(); GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(LocalDate.class, new JodaLocalDateAdapter()); builder.registerTypeAdapter(LocalDate.class, new JodaLocalDateAdapter());
builder.registerTypeAdapter(DateTime.class, new JodaDateTimeAdapter()); builder.registerTypeAdapter(DateTime.class, new JodaDateTimeAdapter());
builder.registerTypeAdapter(MonthDay.class, new JodaMonthDayAdapter());
builder.serializeNulls(); builder.serializeNulls();
this.gson = builder.create(); this.gson = builder.create();
@@ -7,8 +7,10 @@ package org.mifosplatform.infrastructure.core.serialization;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.api.JodaDateTimeAdapter; import org.mifosplatform.infrastructure.core.api.JodaDateTimeAdapter;
import org.mifosplatform.infrastructure.core.api.JodaLocalDateAdapter; import org.mifosplatform.infrastructure.core.api.JodaLocalDateAdapter;
import org.mifosplatform.infrastructure.core.api.JodaMonthDayAdapter;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.google.gson.Gson; import com.google.gson.Gson;
@@ -28,6 +30,7 @@ public final class ExcludeNothingWithPrettyPrintingOffJsonSerializerGoogleGson {
GsonBuilder builder = new GsonBuilder(); GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(LocalDate.class, new JodaLocalDateAdapter()); builder.registerTypeAdapter(LocalDate.class, new JodaLocalDateAdapter());
builder.registerTypeAdapter(DateTime.class, new JodaDateTimeAdapter()); builder.registerTypeAdapter(DateTime.class, new JodaDateTimeAdapter());
builder.registerTypeAdapter(MonthDay.class, new JodaMonthDayAdapter());
this.gson = builder.create(); this.gson = builder.create();
} }
@@ -7,8 +7,10 @@ package org.mifosplatform.infrastructure.core.serialization;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.api.JodaDateTimeAdapter; import org.mifosplatform.infrastructure.core.api.JodaDateTimeAdapter;
import org.mifosplatform.infrastructure.core.api.JodaLocalDateAdapter; import org.mifosplatform.infrastructure.core.api.JodaLocalDateAdapter;
import org.mifosplatform.infrastructure.core.api.JodaMonthDayAdapter;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.google.gson.Gson; import com.google.gson.Gson;
@@ -28,6 +30,7 @@ public final class ExcludeNothingWithPrettyPrintingOnJsonSerializerGoogleGson {
GsonBuilder builder = new GsonBuilder(); GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(LocalDate.class, new JodaLocalDateAdapter()); builder.registerTypeAdapter(LocalDate.class, new JodaLocalDateAdapter());
builder.registerTypeAdapter(DateTime.class, new JodaDateTimeAdapter()); builder.registerTypeAdapter(DateTime.class, new JodaDateTimeAdapter());
builder.registerTypeAdapter(MonthDay.class, new JodaMonthDayAdapter());
builder.setPrettyPrinting(); builder.setPrettyPrinting();
this.gson = builder.create(); this.gson = builder.create();
@@ -16,6 +16,7 @@ import java.util.Set;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.UnsupportedParameterException; import org.mifosplatform.infrastructure.core.exception.UnsupportedParameterException;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -114,6 +115,10 @@ public class FromJsonHelper {
return helperDelegator.extractBooleanNamed(parameterName, element, parametersPassedInRequest); return helperDelegator.extractBooleanNamed(parameterName, element, parametersPassedInRequest);
} }
public MonthDay extractMonthDayNamed(final String parameterName, final JsonElement element) {
return helperDelegator.extractMonthDayNamed(parameterName, element);
}
public LocalDate extractLocalDateNamed(final String parameterName, final JsonElement element) { public LocalDate extractLocalDateNamed(final String parameterName, final JsonElement element) {
return helperDelegator.extractLocalDateNamed(parameterName, element, new HashSet<String>()); return helperDelegator.extractLocalDateNamed(parameterName, element, new HashSet<String>());
} }
@@ -11,8 +11,10 @@ import java.util.Set;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.api.JodaDateTimeAdapter; import org.mifosplatform.infrastructure.core.api.JodaDateTimeAdapter;
import org.mifosplatform.infrastructure.core.api.JodaLocalDateAdapter; import org.mifosplatform.infrastructure.core.api.JodaLocalDateAdapter;
import org.mifosplatform.infrastructure.core.api.JodaMonthDayAdapter;
import org.mifosplatform.infrastructure.core.api.ParameterListExclusionStrategy; import org.mifosplatform.infrastructure.core.api.ParameterListExclusionStrategy;
import org.mifosplatform.infrastructure.core.exception.UnsupportedParameterException; import org.mifosplatform.infrastructure.core.exception.UnsupportedParameterException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -31,6 +33,7 @@ public final class GoogleGsonSerializerHelper {
final GsonBuilder builder = new GsonBuilder(); final GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(LocalDate.class, new JodaLocalDateAdapter()); builder.registerTypeAdapter(LocalDate.class, new JodaLocalDateAdapter());
builder.registerTypeAdapter(DateTime.class, new JodaDateTimeAdapter()); builder.registerTypeAdapter(DateTime.class, new JodaDateTimeAdapter());
builder.registerTypeAdapter(MonthDay.class, new JodaMonthDayAdapter());
if (prettyPrint) { if (prettyPrint) {
builder.setPrettyPrinting(); builder.setPrettyPrinting();
} }
@@ -61,6 +64,7 @@ public final class GoogleGsonSerializerHelper {
final GsonBuilder builder = new GsonBuilder().addSerializationExclusionStrategy(strategy); final GsonBuilder builder = new GsonBuilder().addSerializationExclusionStrategy(strategy);
builder.registerTypeAdapter(LocalDate.class, new JodaLocalDateAdapter()); builder.registerTypeAdapter(LocalDate.class, new JodaLocalDateAdapter());
builder.registerTypeAdapter(DateTime.class, new JodaDateTimeAdapter()); builder.registerTypeAdapter(DateTime.class, new JodaDateTimeAdapter());
builder.registerTypeAdapter(MonthDay.class, new JodaMonthDayAdapter());
if (prettyPrint) { if (prettyPrint) {
builder.setPrettyPrinting(); builder.setPrettyPrinting();
} }
@@ -18,7 +18,9 @@ import java.util.Set;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.infrastructure.core.data.ApiParameterError; import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.springframework.format.number.NumberFormatter; import org.springframework.format.number.NumberFormatter;
@@ -181,6 +183,20 @@ public class JsonParserHelper {
return value; return value;
} }
public String extractMonthDayFormatParameter(final JsonObject element) {
String value = null;
if (element.isJsonObject()) {
JsonObject object = element.getAsJsonObject();
final String monthDayFormatParameter = "monthDayFormat";
if (object.has(monthDayFormatParameter) && object.get(monthDayFormatParameter).isJsonPrimitive()) {
final JsonPrimitive primitive = object.get(monthDayFormatParameter).getAsJsonPrimitive();
value = primitive.getAsString();
}
}
return value;
}
public Locale extractLocaleParameter(final JsonObject element) { public Locale extractLocaleParameter(final JsonObject element) {
Locale clientApplicationLocale = null; Locale clientApplicationLocale = null;
if (element.isJsonObject()) { if (element.isJsonObject()) {
@@ -252,6 +268,40 @@ public class JsonParserHelper {
return value; return value;
} }
public MonthDay extractMonthDayNamed(final String parameterName, final JsonElement element) {
MonthDay value = null;
if (element.isJsonObject()) {
JsonObject object = element.getAsJsonObject();
final String monthDayFormat = extractMonthDayFormatParameter(object);
final Locale clientApplicationLocale = extractLocaleParameter(object);
value = extractMonthDayNamed(parameterName, object, monthDayFormat, clientApplicationLocale);
}
return value;
}
public MonthDay extractMonthDayNamed(final String parameterName, final JsonObject element, final String dateFormat,
final Locale clientApplicationLocale) {
MonthDay value = null;
if (element.isJsonObject()) {
JsonObject object = element.getAsJsonObject();
if (object.has(parameterName) && object.get(parameterName).isJsonPrimitive()) {
final JsonPrimitive primitive = object.get(parameterName).getAsJsonPrimitive();
final String valueAsString = primitive.getAsString();
if (StringUtils.isNotBlank(valueAsString)) {
DateTimeFormatter formatter = DateTimeFormat.forPattern(dateFormat).withLocale(clientApplicationLocale);
value = MonthDay.parse(valueAsString.toLowerCase(clientApplicationLocale), formatter);
}
}
}
return value;
}
public LocalDate extractLocalDateNamed(final String parameterName, final JsonElement element, public LocalDate extractLocalDateNamed(final String parameterName, final JsonElement element,
final Set<String> parametersPassedInCommand) { final Set<String> parametersPassedInCommand) {
@@ -45,335 +45,261 @@ import com.google.gson.JsonArray;
@Table(name = "m_product_loan", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "unq_name") }) @Table(name = "m_product_loan", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "unq_name") })
public class LoanProduct extends AbstractPersistable<Long> { public class LoanProduct extends AbstractPersistable<Long> {
@ManyToOne @ManyToOne
@JoinColumn(name = "fund_id", nullable = true) @JoinColumn(name = "fund_id", nullable = true)
private Fund fund; private Fund fund;
@ManyToOne @ManyToOne
@JoinColumn(name = "loan_transaction_strategy_id", nullable = true) @JoinColumn(name = "loan_transaction_strategy_id", nullable = true)
private LoanTransactionProcessingStrategy transactionProcessingStrategy; private LoanTransactionProcessingStrategy transactionProcessingStrategy;
@Column(name = "name", nullable = false, unique = true) @Column(name = "name", nullable = false, unique = true)
private String name; private String name;
@Column(name = "description") @Column(name = "description")
private String description; private String description;
@ManyToMany @ManyToMany
@JoinTable(name = "m_product_loan_charge", joinColumns = @JoinColumn(name = "product_loan_id"), inverseJoinColumns = @JoinColumn(name = "charge_id")) @JoinTable(name = "m_product_loan_charge", joinColumns = @JoinColumn(name = "product_loan_id"), inverseJoinColumns = @JoinColumn(name = "charge_id"))
private List<Charge> charges; private List<Charge> charges;
@Embedded @Embedded
private final LoanProductRelatedDetail loanProductRelatedDetail; private final LoanProductRelatedDetail loanProductRelatedDetail;
@Embedded @Embedded
private LoanProductMinMaxConstraints loanProductMinMaxConstraints; private LoanProductMinMaxConstraints loanProductMinMaxConstraints;
@Column(name = "accounting_type", nullable = false) @Column(name = "accounting_type", nullable = false)
private Integer accountingRule; private Integer accountingRule;
public static LoanProduct assembleFromJson( public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy,
final Fund fund, final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator) {
final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy,
final List<Charge> productCharges, final JsonCommand command,
final AprCalculator aprCalculator) {
final String name = command.stringValueOfParameterNamed("name"); final String name = command.stringValueOfParameterNamed("name");
final String description = command final String description = command.stringValueOfParameterNamed("description");
.stringValueOfParameterNamed("description"); final String currencyCode = command.stringValueOfParameterNamed("currencyCode");
final String currencyCode = command final Integer digitsAfterDecimal = command.integerValueOfParameterNamed("digitsAfterDecimal");
.stringValueOfParameterNamed("currencyCode");
final Integer digitsAfterDecimal = command
.integerValueOfParameterNamed("digitsAfterDecimal");
final MonetaryCurrency currency = new MonetaryCurrency(currencyCode, final MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal);
digitsAfterDecimal); final BigDecimal principal = command.bigDecimalValueOfParameterNamed("principal");
final BigDecimal principal = command final BigDecimal minPrincipal = command.bigDecimalValueOfParameterNamed("minPrincipal");
.bigDecimalValueOfParameterNamed("principal"); final BigDecimal maxPrincipal = command.bigDecimalValueOfParameterNamed("maxPrincipal");
final BigDecimal minPrincipal = command
.bigDecimalValueOfParameterNamed("minPrincipal");
final BigDecimal maxPrincipal = command
.bigDecimalValueOfParameterNamed("maxPrincipal");
final InterestMethod interestMethod = InterestMethod.fromInt(command final InterestMethod interestMethod = InterestMethod.fromInt(command.integerValueOfParameterNamed("interestType"));
.integerValueOfParameterNamed("interestType")); final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod .integerValueOfParameterNamed("interestCalculationPeriodType"));
.fromInt(command final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.integerValueOfParameterNamed("amortizationType"));
.integerValueOfParameterNamed("interestCalculationPeriodType")); final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.fromInt(command
final AmortizationMethod amortizationMethod = AmortizationMethod .integerValueOfParameterNamed("repaymentFrequencyType"));
.fromInt(command final PeriodFrequencyType interestFrequencyType = PeriodFrequencyType.fromInt(command
.integerValueOfParameterNamed("amortizationType")); .integerValueOfParameterNamed("interestRateFrequencyType"));
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType final BigDecimal interestRatePerPeriod = command.bigDecimalValueOfParameterNamed("interestRatePerPeriod");
.fromInt(command final BigDecimal minInterestRatePerPeriod = command.bigDecimalValueOfParameterNamed("minInterestRatePerPeriod");
.integerValueOfParameterNamed("repaymentFrequencyType")); final BigDecimal maxInterestRatePerPeriod = command.bigDecimalValueOfParameterNamed("maxInterestRatePerPeriod");
final PeriodFrequencyType interestFrequencyType = PeriodFrequencyType final BigDecimal annualInterestRate = aprCalculator.calculateFrom(interestFrequencyType, interestRatePerPeriod);
.fromInt(command
.integerValueOfParameterNamed("interestRateFrequencyType"));
final BigDecimal interestRatePerPeriod = command
.bigDecimalValueOfParameterNamed("interestRatePerPeriod");
final BigDecimal minInterestRatePerPeriod = command
.bigDecimalValueOfParameterNamed("minInterestRatePerPeriod");
final BigDecimal maxInterestRatePerPeriod = command
.bigDecimalValueOfParameterNamed("maxInterestRatePerPeriod");
final BigDecimal annualInterestRate = aprCalculator.calculateFrom(
interestFrequencyType, interestRatePerPeriod);
final Integer repaymentEvery = command final Integer repaymentEvery = command.integerValueOfParameterNamed("repaymentEvery");
.integerValueOfParameterNamed("repaymentEvery"); final Integer numberOfRepayments = command.integerValueOfParameterNamed("numberOfRepayments");
final Integer numberOfRepayments = command final Integer minNumberOfRepayments = command.integerValueOfParameterNamed("minNumberOfRepayments");
.integerValueOfParameterNamed("numberOfRepayments"); final Integer maxNumberOfRepayments = command.integerValueOfParameterNamed("maxNumberOfRepayments");
final Integer minNumberOfRepayments = command final BigDecimal inArrearsTolerance = command.bigDecimalValueOfParameterNamed("inArrearsTolerance");
.integerValueOfParameterNamed("minNumberOfRepayments"); final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(command.integerValueOfParameterNamed("accountingRule"));
final Integer maxNumberOfRepayments = command
.integerValueOfParameterNamed("maxNumberOfRepayments");
final BigDecimal inArrearsTolerance = command
.bigDecimalValueOfParameterNamed("inArrearsTolerance");
final AccountingRuleType accountingRuleType = AccountingRuleType
.fromInt(command.integerValueOfParameterNamed("accountingRule"));
return new LoanProduct(fund, loanTransactionProcessingStrategy, name, return new LoanProduct(fund, loanTransactionProcessingStrategy, name, description, currency, principal, minPrincipal, maxPrincipal,
description, currency, principal, minPrincipal, maxPrincipal, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType, annualInterestRate,
interestRatePerPeriod, minInterestRatePerPeriod, interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentFrequencyType, numberOfRepayments,
maxInterestRatePerPeriod, interestFrequencyType, minNumberOfRepayments, maxNumberOfRepayments, amortizationMethod, inArrearsTolerance, productCharges, accountingRuleType);
annualInterestRate, interestMethod, }
interestCalculationPeriodMethod, repaymentEvery,
repaymentFrequencyType, numberOfRepayments,
minNumberOfRepayments, maxNumberOfRepayments,
amortizationMethod, inArrearsTolerance, productCharges,
accountingRuleType);
}
protected LoanProduct() { protected LoanProduct() {
this.loanProductRelatedDetail = null; this.loanProductRelatedDetail = null;
this.loanProductMinMaxConstraints = null; this.loanProductMinMaxConstraints = null;
} }
public LoanProduct( public LoanProduct(final Fund fund, final LoanTransactionProcessingStrategy transactionProcessingStrategy, final String name,
final Fund fund, final String description, final MonetaryCurrency currency, final BigDecimal defaultPrincipal,
final LoanTransactionProcessingStrategy transactionProcessingStrategy, final BigDecimal defaultMinPrincipal, final BigDecimal defaultMaxPrincipal,
final String name, final BigDecimal defaultNominalInterestRatePerPeriod, final BigDecimal defaultMinNominalInterestRatePerPeriod,
final String description, final BigDecimal defaultMaxNominalInterestRatePerPeriod, final PeriodFrequencyType interestPeriodFrequencyType,
final MonetaryCurrency currency, final BigDecimal defaultAnnualNominalInterestRate, final InterestMethod interestMethod,
final BigDecimal defaultPrincipal, final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final Integer repayEvery,
final BigDecimal defaultMinPrincipal, final PeriodFrequencyType repaymentFrequencyType, final Integer defaultNumberOfInstallments,
final BigDecimal defaultMaxPrincipal, final Integer defaultMinNumberOfInstallments, final Integer defaultMaxNumberOfInstallments,
final BigDecimal defaultNominalInterestRatePerPeriod, final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance, final List<Charge> charges,
final BigDecimal defaultMinNominalInterestRatePerPeriod, final AccountingRuleType accountingRuleType) {
final BigDecimal defaultMaxNominalInterestRatePerPeriod, this.fund = fund;
final PeriodFrequencyType interestPeriodFrequencyType, this.transactionProcessingStrategy = transactionProcessingStrategy;
final BigDecimal defaultAnnualNominalInterestRate, this.name = name.trim();
final InterestMethod interestMethod, if (StringUtils.isNotBlank(description)) {
final InterestCalculationPeriodMethod interestCalculationPeriodMethod, this.description = description.trim();
final Integer repayEvery, } else {
final PeriodFrequencyType repaymentFrequencyType, this.description = null;
final Integer defaultNumberOfInstallments, }
final Integer defaultMinNumberOfInstallments,
final Integer defaultMaxNumberOfInstallments,
final AmortizationMethod amortizationMethod,
final BigDecimal inArrearsTolerance, final List<Charge> charges,
final AccountingRuleType accountingRuleType) {
this.fund = fund;
this.transactionProcessingStrategy = transactionProcessingStrategy;
this.name = name.trim();
if (StringUtils.isNotBlank(description)) {
this.description = description.trim();
} else {
this.description = null;
}
if (charges != null) { if (charges != null) {
this.charges = charges; this.charges = charges;
} }
this.loanProductRelatedDetail = new LoanProductRelatedDetail(currency, this.loanProductRelatedDetail = new LoanProductRelatedDetail(currency, defaultPrincipal, defaultNominalInterestRatePerPeriod,
defaultPrincipal, defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery,
interestPeriodFrequencyType, defaultAnnualNominalInterestRate, repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod, inArrearsTolerance);
interestMethod, interestCalculationPeriodMethod, repayEvery,
repaymentFrequencyType, defaultNumberOfInstallments,
amortizationMethod, inArrearsTolerance);
this.loanProductMinMaxConstraints = new LoanProductMinMaxConstraints( this.loanProductMinMaxConstraints = new LoanProductMinMaxConstraints(defaultMinPrincipal, defaultMaxPrincipal,
defaultMinPrincipal, defaultMaxPrincipal, defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod, defaultMinNumberOfInstallments,
defaultMinNominalInterestRatePerPeriod, defaultMaxNumberOfInstallments);
defaultMaxNominalInterestRatePerPeriod,
defaultMinNumberOfInstallments, defaultMaxNumberOfInstallments);
if (accountingRuleType != null) { if (accountingRuleType != null) {
this.accountingRule = accountingRuleType.getValue(); this.accountingRule = accountingRuleType.getValue();
} }
} }
public MonetaryCurrency getCurrency() { public MonetaryCurrency getCurrency() {
return this.loanProductRelatedDetail.getCurrency(); return this.loanProductRelatedDetail.getCurrency();
} }
public void update(final Fund fund) { public void update(final Fund fund) {
this.fund = fund; this.fund = fund;
} }
public void update(final LoanTransactionProcessingStrategy strategy) { public void update(final LoanTransactionProcessingStrategy strategy) {
this.transactionProcessingStrategy = strategy; this.transactionProcessingStrategy = strategy;
} }
public boolean update(final List<Charge> newProductCharges) { public boolean update(final List<Charge> newProductCharges) {
if (newProductCharges == null) if (newProductCharges == null) return false;
return false;
boolean updated = false; boolean updated = false;
if (this.charges != null) { if (this.charges != null) {
final Set<Charge> currentSetOfCharges = new HashSet<Charge>( final Set<Charge> currentSetOfCharges = new HashSet<Charge>(this.charges);
this.charges); final Set<Charge> newSetOfCharges = new HashSet<Charge>(newProductCharges);
final Set<Charge> newSetOfCharges = new HashSet<Charge>(
newProductCharges);
if (!(currentSetOfCharges.equals(newSetOfCharges))) { if (!(currentSetOfCharges.equals(newSetOfCharges))) {
updated = true; updated = true;
this.charges = newProductCharges; this.charges = newProductCharges;
} }
} else { } else {
updated = true; updated = true;
this.charges = newProductCharges; this.charges = newProductCharges;
} }
return updated; return updated;
} }
public Integer getAccountingType() { public Integer getAccountingType() {
return this.accountingRule; return this.accountingRule;
} }
public Map<String, Object> update(final JsonCommand command, public Map<String, Object> update(final JsonCommand command, final AprCalculator aprCalculator) {
final AprCalculator aprCalculator) {
final Map<String, Object> actualChanges = this.loanProductRelatedDetail final Map<String, Object> actualChanges = this.loanProductRelatedDetail.update(command, aprCalculator);
.update(command, aprCalculator); actualChanges.putAll(this.loanProductMinMaxConstraints().update(command));
actualChanges.putAll(this.loanProductMinMaxConstraints()
.update(command));
final String accountingTypeParamName = "accountingRule"; final String accountingTypeParamName = "accountingRule";
if (command.isChangeInIntegerParameterNamed(accountingTypeParamName, if (command.isChangeInIntegerParameterNamed(accountingTypeParamName, this.accountingRule)) {
this.accountingRule)) { final Integer newValue = command.integerValueOfParameterNamed(accountingTypeParamName);
final Integer newValue = command actualChanges.put(accountingTypeParamName, newValue);
.integerValueOfParameterNamed(accountingTypeParamName); this.accountingRule = newValue;
actualChanges.put(accountingTypeParamName, newValue); }
this.accountingRule = newValue;
}
final String nameParamName = "name"; final String nameParamName = "name";
if (command.isChangeInStringParameterNamed(nameParamName, this.name)) { if (command.isChangeInStringParameterNamed(nameParamName, this.name)) {
final String newValue = command final String newValue = command.stringValueOfParameterNamed(nameParamName);
.stringValueOfParameterNamed(nameParamName); actualChanges.put(nameParamName, newValue);
actualChanges.put(nameParamName, newValue); this.name = newValue;
this.name = newValue; }
}
final String descriptionParamName = "description"; final String descriptionParamName = "description";
if (command.isChangeInStringParameterNamed(descriptionParamName, if (command.isChangeInStringParameterNamed(descriptionParamName, this.description)) {
this.description)) { final String newValue = command.stringValueOfParameterNamed(descriptionParamName);
final String newValue = command actualChanges.put(descriptionParamName, newValue);
.stringValueOfParameterNamed(descriptionParamName); this.description = newValue;
actualChanges.put(descriptionParamName, newValue); }
this.description = newValue;
}
Long existingFundId = null; Long existingFundId = null;
if (this.fund != null) { if (this.fund != null) {
existingFundId = this.fund.getId(); existingFundId = this.fund.getId();
} }
final String fundIdParamName = "fundId"; final String fundIdParamName = "fundId";
if (command.isChangeInLongParameterNamed(fundIdParamName, if (command.isChangeInLongParameterNamed(fundIdParamName, existingFundId)) {
existingFundId)) { final Long newValue = command.longValueOfParameterNamed(fundIdParamName);
final Long newValue = command actualChanges.put(fundIdParamName, newValue);
.longValueOfParameterNamed(fundIdParamName); }
actualChanges.put(fundIdParamName, newValue);
}
Long existingStrategyId = null; Long existingStrategyId = null;
if (this.transactionProcessingStrategy != null) { if (this.transactionProcessingStrategy != null) {
existingStrategyId = this.transactionProcessingStrategy.getId(); existingStrategyId = this.transactionProcessingStrategy.getId();
} }
final String transactionProcessingStrategyParamName = "transactionProcessingStrategyId"; final String transactionProcessingStrategyParamName = "transactionProcessingStrategyId";
if (command.isChangeInLongParameterNamed( if (command.isChangeInLongParameterNamed(transactionProcessingStrategyParamName, existingStrategyId)) {
transactionProcessingStrategyParamName, existingStrategyId)) { final Long newValue = command.longValueOfParameterNamed(transactionProcessingStrategyParamName);
final Long newValue = command actualChanges.put(transactionProcessingStrategyParamName, newValue);
.longValueOfParameterNamed(transactionProcessingStrategyParamName); }
actualChanges.put(transactionProcessingStrategyParamName, newValue);
}
final String chargesParamName = "charges"; final String chargesParamName = "charges";
if (command.hasParameter(chargesParamName)) { if (command.hasParameter(chargesParamName)) {
JsonArray jsonArray = command JsonArray jsonArray = command.arrayOfParameterNamed(chargesParamName);
.arrayOfParameterNamed(chargesParamName); if (jsonArray != null) {
if (jsonArray != null) {
} }
actualChanges.put(chargesParamName, actualChanges.put(chargesParamName, command.jsonFragment(chargesParamName));
command.jsonFragment(chargesParamName)); }
}
return actualChanges; return actualChanges;
} }
public boolean isCashBasedAccountingEnabled() { public boolean isCashBasedAccountingEnabled() {
return AccountingRuleType.CASH_BASED.getValue().equals( return AccountingRuleType.CASH_BASED.getValue().equals(this.accountingRule);
this.accountingRule); }
}
public boolean isAccrualBasedAccountingEnabled() { public boolean isAccrualBasedAccountingEnabled() {
return AccountingRuleType.ACCRUAL_BASED.getValue().equals( return AccountingRuleType.ACCRUAL_BASED.getValue().equals(this.accountingRule);
this.accountingRule); }
}
public Money getPrincipalAmount() { public Money getPrincipalAmount() {
return this.loanProductRelatedDetail.getPrincipal(); return this.loanProductRelatedDetail.getPrincipal();
} }
public Money getMinPrincipalAmount() { public Money getMinPrincipalAmount() {
return Money.of(this.loanProductRelatedDetail.getCurrency(), this return Money.of(this.loanProductRelatedDetail.getCurrency(), this.loanProductMinMaxConstraints().getMinPrincipal());
.loanProductMinMaxConstraints().getMinPrincipal()); }
}
public Money getMaxPrincipalAmount() { public Money getMaxPrincipalAmount() {
return Money.of(this.loanProductRelatedDetail.getCurrency(), this return Money.of(this.loanProductRelatedDetail.getCurrency(), this.loanProductMinMaxConstraints().getMaxPrincipal());
.loanProductMinMaxConstraints().getMaxPrincipal()); }
}
public BigDecimal getNominalInterestRatePerPeriod() { public BigDecimal getNominalInterestRatePerPeriod() {
return this.loanProductRelatedDetail.getNominalInterestRatePerPeriod(); return this.loanProductRelatedDetail.getNominalInterestRatePerPeriod();
} }
public BigDecimal getMinNominalInterestRatePerPeriod() { public BigDecimal getMinNominalInterestRatePerPeriod() {
return this.loanProductMinMaxConstraints() return this.loanProductMinMaxConstraints().getMinNominalInterestRatePerPeriod();
.getMinNominalInterestRatePerPeriod(); }
}
public BigDecimal getMaxNominalInterestRatePerPeriod() { public BigDecimal getMaxNominalInterestRatePerPeriod() {
return this.loanProductMinMaxConstraints() return this.loanProductMinMaxConstraints().getMaxNominalInterestRatePerPeriod();
.getMaxNominalInterestRatePerPeriod(); }
}
public Integer getNumberOfRepayments() { public Integer getNumberOfRepayments() {
return this.loanProductRelatedDetail().getNumberOfRepayments(); return this.loanProductRelatedDetail().getNumberOfRepayments();
} }
public Integer getMinNumberOfRepayments() { public Integer getMinNumberOfRepayments() {
return this.loanProductMinMaxConstraints().getMinNumberOfRepayments(); return this.loanProductMinMaxConstraints().getMinNumberOfRepayments();
} }
public Integer getMaxNumberOfRepayments() { public Integer getMaxNumberOfRepayments() {
return this.loanProductMinMaxConstraints().getMaxNumberOfRepayments(); return this.loanProductMinMaxConstraints().getMaxNumberOfRepayments();
} }
public LoanProductRelatedDetail loanProductRelatedDetail() { public LoanProductRelatedDetail loanProductRelatedDetail() {
return this.loanProductRelatedDetail; return this.loanProductRelatedDetail;
} }
public LoanProductMinMaxConstraints loanProductMinMaxConstraints() { public LoanProductMinMaxConstraints loanProductMinMaxConstraints() {
// If all min and max fields are null then loanProductMinMaxConstraints // If all min and max fields are null then loanProductMinMaxConstraints
// initialising to null // initialising to null
// Reset LoanProductMinMaxConstraints with null values. // Reset LoanProductMinMaxConstraints with null values.
this.loanProductMinMaxConstraints = (this.loanProductMinMaxConstraints == null) ? new LoanProductMinMaxConstraints( this.loanProductMinMaxConstraints = (this.loanProductMinMaxConstraints == null) ? new LoanProductMinMaxConstraints(null, null,
null, null, null, null, null, null) null, null, null, null) : this.loanProductMinMaxConstraints;
: this.loanProductMinMaxConstraints; return loanProductMinMaxConstraints;
return loanProductMinMaxConstraints; }
}
} }
@@ -16,6 +16,7 @@ public class SavingsApiConstants {
// general // general
public static final String localeParamName = "locale"; public static final String localeParamName = "locale";
public static final String dateFormatParamName = "dateFormat"; public static final String dateFormatParamName = "dateFormat";
public static final String monthDayFormatParamName = "monthDayFormat";
// savings product and account parameters // savings product and account parameters
public static final String idParamName = "id"; public static final String idParamName = "id";
@@ -41,16 +42,19 @@ public class SavingsApiConstants {
public static final String lockinPeriodFrequencyTypeParamName = "lockinPeriodFrequencyType"; public static final String lockinPeriodFrequencyTypeParamName = "lockinPeriodFrequencyType";
public static final String withdrawalFeeAmountParamName = "withdrawalFeeAmount"; public static final String withdrawalFeeAmountParamName = "withdrawalFeeAmount";
public static final String withdrawalFeeTypeParamName = "withdrawalFeeType"; public static final String withdrawalFeeTypeParamName = "withdrawalFeeType";
public static final String annualFeeAmountParamName = "annualFeeAmount";
public static final String annualFeeOnMonthDayParamName = "annualFeeOnMonthDay";
// transaction parameters // transaction parameters
public static final String transactionDateParamName = "transactionDate"; public static final String transactionDateParamName = "transactionDate";
public static final String transactionAmountParamName = "transactionAmount"; public static final String transactionAmountParamName = "transactionAmount";
public static final Set<String> SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName, public static final Set<String> SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
nameParamName, descriptionParamName, currencyCodeParamName, digitsAfterDecimalParamName, nominalAnnualInterestRateParamName, monthDayFormatParamName, nameParamName, descriptionParamName, currencyCodeParamName, digitsAfterDecimalParamName,
interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName, nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName,
interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName,
lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName)); lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName,
annualFeeAmountParamName, annualFeeOnMonthDayParamName));
/** /**
* These parameters will match the class level parameters of * These parameters will match the class level parameters of
@@ -61,16 +65,18 @@ public class SavingsApiConstants {
nameParamName, descriptionParamName, "currency", digitsAfterDecimalParamName, nominalAnnualInterestRateParamName, nameParamName, descriptionParamName, "currency", digitsAfterDecimalParamName, nominalAnnualInterestRateParamName,
interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName,
interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName,
lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, "currencyOptions", lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, annualFeeAmountParamName,
"interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions", "interestCalculationTypeOptions", annualFeeOnMonthDayParamName, "currencyOptions", "interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions",
"interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions")); "interestCalculationTypeOptions", "interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions",
"withdrawalFeeTypeOptions"));
public static final Set<String> SAVINGS_ACCOUNT_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName, public static final Set<String> SAVINGS_ACCOUNT_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
dateFormatParamName, accountNoParamName, externalIdParamName, clientIdParamName, groupIdParamName, productIdParamName, dateFormatParamName, monthDayFormatParamName, accountNoParamName, externalIdParamName, clientIdParamName, groupIdParamName,
activeParamName, activationDateParamName, nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, productIdParamName, activeParamName, activationDateParamName, nominalAnnualInterestRateParamName,
interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName,
minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName,
withdrawalFeeAmountParamName, withdrawalFeeTypeParamName)); lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, annualFeeAmountParamName,
annualFeeOnMonthDayParamName));
/** /**
* These parameters will match the class level parameters of * These parameters will match the class level parameters of
@@ -82,9 +88,10 @@ public class SavingsApiConstants {
groupIdParamName, "groupName", "savingsProductId", "savingsProductName", "currency", nominalAnnualInterestRateParamName, groupIdParamName, "groupName", "savingsProductId", "savingsProductName", "currency", nominalAnnualInterestRateParamName,
interestCompoundingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, interestCompoundingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName,
minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName,
withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, "summary", "transactions", "productOptions", withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, annualFeeAmountParamName, annualFeeOnMonthDayParamName, "summary",
"interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions", "interestCalculationTypeOptions", "transactions", "productOptions", "interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions",
"interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions")); "interestCalculationTypeOptions", "interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions",
"withdrawalFeeTypeOptions"));
public static final Set<String> SAVINGS_ACCOUNT_TRANSACTION_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList( public static final Set<String> SAVINGS_ACCOUNT_TRANSACTION_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(
localeParamName, dateFormatParamName, transactionDateParamName, transactionAmountParamName)); localeParamName, dateFormatParamName, transactionDateParamName, transactionAmountParamName));
@@ -9,6 +9,7 @@ import java.math.BigDecimal;
import java.util.Collection; import java.util.Collection;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.monetary.data.CurrencyData;
@@ -39,6 +40,9 @@ public class SavingsAccountData {
private final EnumOptionData lockinPeriodFrequencyType; private final EnumOptionData lockinPeriodFrequencyType;
private final BigDecimal withdrawalFeeAmount; private final BigDecimal withdrawalFeeAmount;
private final EnumOptionData withdrawalFeeType; private final EnumOptionData withdrawalFeeType;
private final BigDecimal annualFeeAmount;
private final MonthDay annualFeeOnMonthDay;
private final LocalDate annualFeeNextDueDate;
// associations // associations
private final SavingsAccountSummaryData summary; private final SavingsAccountSummaryData summary;
@@ -68,7 +72,8 @@ public class SavingsAccountData {
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount, final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount,
final EnumOptionData withdrawalFeeType, final SavingsAccountSummaryData summary) { final EnumOptionData withdrawalFeeType, final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay,
final LocalDate annualFeeNextDueDate, final SavingsAccountSummaryData summary) {
final Collection<SavingsProductData> productOptions = null; final Collection<SavingsProductData> productOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null; final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
@@ -83,9 +88,10 @@ public class SavingsAccountData {
return new SavingsAccountData(id, accountNo, externalId, status, activationDate, groupId, groupName, clientId, clientName, return new SavingsAccountData(id, accountNo, externalId, status, activationDate, groupId, groupName, clientId, clientName,
productId, productName, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, productId, productName, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, summary, transactions, productOptions, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, annualFeeOnMonthDay,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, annualFeeNextDueDate, summary, transactions, productOptions, interestCompoundingPeriodTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions); interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
} }
public static SavingsAccountData withTemplateOptions(final SavingsAccountData account, public static SavingsAccountData withTemplateOptions(final SavingsAccountData account,
@@ -101,9 +107,10 @@ public class SavingsAccountData {
account.savingsProductName, account.currency, account.nominalAnnualInterestRate, account.interestCompoundingPeriodType, account.savingsProductName, account.currency, account.nominalAnnualInterestRate, account.interestCompoundingPeriodType,
account.interestPostingPeriodType, account.interestCalculationType, account.interestCalculationDaysInYearType, account.interestPostingPeriodType, account.interestCalculationType, account.interestCalculationDaysInYearType,
account.minRequiredOpeningBalance, account.lockinPeriodFrequency, account.lockinPeriodFrequencyType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency, account.lockinPeriodFrequencyType,
account.withdrawalFeeAmount, account.withdrawalFeeType, account.summary, transactions, productOptions, account.withdrawalFeeAmount, account.withdrawalFeeType, account.annualFeeAmount, account.annualFeeOnMonthDay,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, account.annualFeeNextDueDate, account.summary, transactions, productOptions, interestCompoundingPeriodTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions); interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
} }
public static SavingsAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId, public static SavingsAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId,
@@ -127,6 +134,9 @@ public class SavingsAccountData {
final EnumOptionData lockinPeriodFrequencyType = null; final EnumOptionData lockinPeriodFrequencyType = null;
final BigDecimal withdrawalFeeAmount = null; final BigDecimal withdrawalFeeAmount = null;
final EnumOptionData withdrawalFeeType = null; final EnumOptionData withdrawalFeeType = null;
final BigDecimal annualFeeAmount = null;
final MonthDay annualFeeOnMonthDay = null;
final LocalDate annualFeeNextDueDate = null;
final SavingsAccountSummaryData summary = null; final SavingsAccountSummaryData summary = null;
final Collection<SavingsAccountTransactionData> transactions = null; final Collection<SavingsAccountTransactionData> transactions = null;
@@ -142,9 +152,10 @@ public class SavingsAccountData {
return new SavingsAccountData(id, accountNo, externalId, status, activationDate, groupId, groupName, clientId, clientName, return new SavingsAccountData(id, accountNo, externalId, status, activationDate, groupId, groupName, clientId, clientName,
productId, productName, currency, nominalAnnualInterestRate, interestPeriodType, interestPostingPeriodType, productId, productName, currency, nominalAnnualInterestRate, interestPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, summary, transactions, productOptions, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, annualFeeOnMonthDay,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, annualFeeNextDueDate, summary, transactions, productOptions, interestCompoundingPeriodTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions); interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
} }
private SavingsAccountData(final Long id, final String accountNo, final String externalId, final SavingsAccountStatusEnumData status, private SavingsAccountData(final Long id, final String accountNo, final String externalId, final SavingsAccountStatusEnumData status,
@@ -154,6 +165,7 @@ public class SavingsAccountData {
final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType, final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType,
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency,
final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount, final EnumOptionData withdrawalFeeType, final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount, final EnumOptionData withdrawalFeeType,
final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay, final LocalDate annualFeeNextDueDate,
final SavingsAccountSummaryData summary, final Collection<SavingsAccountTransactionData> transactions, final SavingsAccountSummaryData summary, final Collection<SavingsAccountTransactionData> transactions,
final Collection<SavingsProductData> productOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions, final Collection<SavingsProductData> productOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions, final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
@@ -182,6 +194,9 @@ public class SavingsAccountData {
this.lockinPeriodFrequencyType = lockinPeriodFrequencyType; this.lockinPeriodFrequencyType = lockinPeriodFrequencyType;
this.withdrawalFeeAmount = withdrawalFeeAmount; this.withdrawalFeeAmount = withdrawalFeeAmount;
this.withdrawalFeeType = withdrawalFeeType; this.withdrawalFeeType = withdrawalFeeType;
this.annualFeeAmount = annualFeeAmount;
this.annualFeeOnMonthDay = annualFeeOnMonthDay;
this.annualFeeNextDueDate = annualFeeNextDueDate;
this.summary = summary; this.summary = summary;
this.transactions = transactions; this.transactions = transactions;
@@ -3,6 +3,8 @@ package org.mifosplatform.portfolio.savings.data;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.accountNoParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.accountNoParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activationDateParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activationDateParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activeParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.clientIdParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.clientIdParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.externalIdParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.externalIdParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.groupIdParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.groupIdParamName;
@@ -26,6 +28,7 @@ import java.util.Map;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.data.ApiParameterError; import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
@@ -173,6 +176,16 @@ public class SavingsAccountDataValidator {
baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType).isOneOfTheseValues(1, 2); baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType).isOneOfTheseValues(1, 2);
} }
if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) {
final BigDecimal annualFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(annualFeeAmountParamName, element);
baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).notNull().positiveAmount();
if (annualFeeAmount != null) {
MonthDay monthDayOfAnnualFee = fromApiJsonHelper.extractMonthDayNamed(annualFeeOnMonthDayParamName, element);
baseDataValidator.reset().parameter(annualFeeOnMonthDayParamName).value(monthDayOfAnnualFee).notNull();
}
}
throwExceptionIfValidationWarningsExist(dataValidationErrors); throwExceptionIfValidationWarningsExist(dataValidationErrors);
} }
@@ -316,15 +329,21 @@ public class SavingsAccountDataValidator {
baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType).isOneOfTheseValues(1, 2); baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType).isOneOfTheseValues(1, 2);
} }
if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) {
final BigDecimal annualFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(annualFeeAmountParamName, element);
baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).ignoreIfNull().positiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(annualFeeOnMonthDayParamName, element)) {
MonthDay monthDayOfAnnualFee = fromApiJsonHelper.extractMonthDayNamed(annualFeeOnMonthDayParamName, element);
baseDataValidator.reset().parameter(annualFeeOnMonthDayParamName).value(monthDayOfAnnualFee).ignoreIfNull();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors); throwExceptionIfValidationWarningsExist(dataValidationErrors);
} }
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) { private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
//
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
} }
} }
@@ -19,17 +19,19 @@ public class SavingsAccountSummaryData {
private final BigDecimal totalDeposits; private final BigDecimal totalDeposits;
private final BigDecimal totalWithdrawals; private final BigDecimal totalWithdrawals;
private final BigDecimal totalWithdrawalFees; private final BigDecimal totalWithdrawalFees;
private final BigDecimal totalAnnualFees;
private final BigDecimal totalInterestEarned; private final BigDecimal totalInterestEarned;
private final BigDecimal totalInterestPosted; private final BigDecimal totalInterestPosted;
private final BigDecimal accountBalance; private final BigDecimal accountBalance;
public SavingsAccountSummaryData(final CurrencyData currency, final BigDecimal totalDeposits, final BigDecimal totalWithdrawals, public SavingsAccountSummaryData(final CurrencyData currency, final BigDecimal totalDeposits, final BigDecimal totalWithdrawals,
final BigDecimal totalWithdrawalFees, final BigDecimal totalInterestEarned, final BigDecimal totalInterestPosted, final BigDecimal totalWithdrawalFees, final BigDecimal totalAnnualFees, final BigDecimal totalInterestEarned,
final BigDecimal accountBalance) { final BigDecimal totalInterestPosted, final BigDecimal accountBalance) {
this.currency = currency; this.currency = currency;
this.totalDeposits = totalDeposits; this.totalDeposits = totalDeposits;
this.totalWithdrawals = totalWithdrawals; this.totalWithdrawals = totalWithdrawals;
this.totalWithdrawalFees = totalWithdrawalFees; this.totalWithdrawalFees = totalWithdrawalFees;
this.totalAnnualFees = totalAnnualFees;
this.totalInterestEarned = totalInterestEarned; this.totalInterestEarned = totalInterestEarned;
this.totalInterestPosted = totalInterestPosted; this.totalInterestPosted = totalInterestPosted;
this.accountBalance = accountBalance; this.accountBalance = accountBalance;
@@ -8,6 +8,7 @@ package org.mifosplatform.portfolio.savings.data;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Collection; import java.util.Collection;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.monetary.data.CurrencyData;
@@ -30,6 +31,8 @@ public class SavingsProductData {
private final EnumOptionData lockinPeriodFrequencyType; private final EnumOptionData lockinPeriodFrequencyType;
private final BigDecimal withdrawalFeeAmount; private final BigDecimal withdrawalFeeAmount;
private final EnumOptionData withdrawalFeeType; private final EnumOptionData withdrawalFeeType;
private final BigDecimal annualFeeAmount;
private final MonthDay annualFeeOnMonthDay;
// template // template
@SuppressWarnings("unused") @SuppressWarnings("unused")
@@ -65,12 +68,15 @@ public class SavingsProductData {
final EnumOptionData lockinPeriodFrequencyType = null; final EnumOptionData lockinPeriodFrequencyType = null;
final BigDecimal withdrawalFeeAmount = null; final BigDecimal withdrawalFeeAmount = null;
final EnumOptionData withdrawalFeeType = null; final EnumOptionData withdrawalFeeType = null;
final BigDecimal annualFeeAmount = null;
final MonthDay annualFeeMonthDay = null;
return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, currencyOptions, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, annualFeeMonthDay, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions); interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions);
} }
/** /**
@@ -89,9 +95,9 @@ public class SavingsProductData {
existingProduct.interestPostingPeriodType, existingProduct.interestCalculationType, existingProduct.interestPostingPeriodType, existingProduct.interestCalculationType,
existingProduct.interestCalculationDaysInYearType, existingProduct.minRequiredOpeningBalance, existingProduct.interestCalculationDaysInYearType, existingProduct.minRequiredOpeningBalance,
existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType, existingProduct.withdrawalFeeAmount, existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType, existingProduct.withdrawalFeeAmount,
existingProduct.withdrawalFeeType, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, existingProduct.withdrawalFeeType, existingProduct.annualFeeAmount, existingProduct.annualFeeOnMonthDay, currencyOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
withdrawalFeeTypeOptions); interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
} }
public static SavingsProductData instance(final Long id, final String name, final String description, final CurrencyData currency, public static SavingsProductData instance(final Long id, final String name, final String description, final CurrencyData currency,
@@ -99,7 +105,7 @@ public class SavingsProductData {
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount, final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount,
final EnumOptionData withdrawalFeeType) { final EnumOptionData withdrawalFeeType, final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay) {
final Collection<CurrencyData> currencyOptions = null; final Collection<CurrencyData> currencyOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null; final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
@@ -111,9 +117,10 @@ public class SavingsProductData {
return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, currencyOptions, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, annualFeeOnMonthDay, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions); interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions);
} }
public static SavingsProductData lookup(final Long id, final String name) { public static SavingsProductData lookup(final Long id, final String name) {
@@ -130,6 +137,8 @@ public class SavingsProductData {
final EnumOptionData lockinPeriodFrequencyType = null; final EnumOptionData lockinPeriodFrequencyType = null;
final BigDecimal withdrawalFeeAmount = null; final BigDecimal withdrawalFeeAmount = null;
final EnumOptionData withdrawalFeeType = null; final EnumOptionData withdrawalFeeType = null;
final BigDecimal annualFeeAmount = null;
final MonthDay annualFeeMonthDay = null;
final Collection<CurrencyData> currencyOptions = null; final Collection<CurrencyData> currencyOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null; final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
@@ -141,19 +150,19 @@ public class SavingsProductData {
return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, currencyOptions, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions, annualFeeMonthDay, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions); interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions);
} }
private SavingsProductData(final Long id, final String name, final String description, final CurrencyData currency, private SavingsProductData(final Long id, final String name, final String description, final CurrencyData currency,
final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestCompoundingPeriodType, final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestCompoundingPeriodType,
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount,
final BigDecimal withdrawalFeeAmount, final EnumOptionData withdrawalFeeType, final EnumOptionData withdrawalFeeType, final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay,
final Collection<CurrencyData> currencyOptions, final Collection<CurrencyData> currencyOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions, final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions, final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions, final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
@@ -172,6 +181,8 @@ public class SavingsProductData {
this.lockinPeriodFrequencyType = lockinPeriodFrequencyType; this.lockinPeriodFrequencyType = lockinPeriodFrequencyType;
this.withdrawalFeeAmount = withdrawalFeeAmount; this.withdrawalFeeAmount = withdrawalFeeAmount;
this.withdrawalFeeType = withdrawalFeeType; this.withdrawalFeeType = withdrawalFeeType;
this.annualFeeAmount = annualFeeAmount;
this.annualFeeOnMonthDay = annualFeeOnMonthDay;
this.currencyOptions = currencyOptions; this.currencyOptions = currencyOptions;
this.interestCompoundingPeriodTypeOptions = interestCompoundingPeriodTypeOptions; this.interestCompoundingPeriodTypeOptions = interestCompoundingPeriodTypeOptions;
@@ -2,6 +2,8 @@ package org.mifosplatform.portfolio.savings.data;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.currencyCodeParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.currencyCodeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.descriptionParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.descriptionParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.digitsAfterDecimalParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.digitsAfterDecimalParamName;
@@ -24,6 +26,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.data.ApiParameterError; import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException; import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
@@ -136,6 +139,16 @@ public class SavingsProductDataValidator {
} }
} }
if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) {
final BigDecimal annualFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(annualFeeAmountParamName, element);
baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).notNull().positiveAmount();
if (annualFeeAmount != null) {
MonthDay monthDayOfAnnualFee = fromApiJsonHelper.extractMonthDayNamed(annualFeeOnMonthDayParamName, element);
baseDataValidator.reset().parameter(annualFeeOnMonthDayParamName).value(monthDayOfAnnualFee).notNull();
}
}
throwExceptionIfValidationWarningsExist(dataValidationErrors); throwExceptionIfValidationWarningsExist(dataValidationErrors);
} }
@@ -222,6 +235,16 @@ public class SavingsProductDataValidator {
.isOneOfTheseValues(1, 2); .isOneOfTheseValues(1, 2);
} }
if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) {
final BigDecimal annualFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(annualFeeAmountParamName, element);
baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).ignoreIfNull().positiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(annualFeeOnMonthDayParamName, element)) {
MonthDay monthDayOfAnnualFee = fromApiJsonHelper.extractMonthDayNamed(annualFeeOnMonthDayParamName, element);
baseDataValidator.reset().parameter(annualFeeOnMonthDayParamName).value(monthDayOfAnnualFee).ignoreIfNull();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors); throwExceptionIfValidationWarningsExist(dataValidationErrors);
} }
@@ -5,6 +5,12 @@
*/ */
package org.mifosplatform.portfolio.savings.domain; package org.mifosplatform.portfolio.savings.domain;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.localeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeTypeParamName;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.MathContext; import java.math.MathContext;
import java.math.RoundingMode; import java.math.RoundingMode;
@@ -34,6 +40,7 @@ import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption; import org.hibernate.annotations.LazyCollectionOption;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError; import org.mifosplatform.infrastructure.core.data.ApiParameterError;
@@ -142,6 +149,19 @@ public class SavingsAccount extends AbstractPersistable<Long> {
@Column(name = "withdrawal_fee_type_enum", nullable = true) @Column(name = "withdrawal_fee_type_enum", nullable = true)
private Integer withdrawalFeeType; private Integer withdrawalFeeType;
@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 @Embedded
private SavingsAccountSummary summary; private SavingsAccountSummary summary;
@@ -165,13 +185,15 @@ public class SavingsAccount extends AbstractPersistable<Long> {
final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType) { final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType, final BigDecimal annualFeeAmount,
final MonthDay annualFeeOnMonthDay) {
final SavingsAccountStatusType status = SavingsAccountStatusType.UNACTIVATED; final SavingsAccountStatusType status = SavingsAccountStatusType.UNACTIVATED;
final LocalDate activationDate = null; final LocalDate activationDate = null;
return new SavingsAccount(client, group, product, accountNo, externalId, status, activationDate, interestRate, return new SavingsAccount(client, group, product, accountNo, externalId, status, activationDate, interestRate,
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType); minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType,
annualFeeAmount, annualFeeOnMonthDay);
} }
private SavingsAccount(final Client client, final Group group, final SavingsProduct product, final String accountNo, private SavingsAccount(final Client client, final Group group, final SavingsProduct product, final String accountNo,
@@ -180,7 +202,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType) { final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType, final BigDecimal annualFeeAmount,
final MonthDay annualFeeOnMonthDay) {
this.client = client; this.client = client;
this.group = group; this.group = group;
this.product = product; this.product = product;
@@ -210,9 +233,27 @@ public class SavingsAccount extends AbstractPersistable<Long> {
if (withdrawalFeeType != null) { if (withdrawalFeeType != null) {
this.withdrawalFeeType = withdrawalFeeType.getValue(); this.withdrawalFeeType = withdrawalFeeType.getValue();
} }
this.annualFeeAmount = annualFeeAmount;
if (annualFeeOnMonthDay != null) {
this.annualFeeOnMonth = annualFeeOnMonthDay.getMonthOfYear();
this.annualFeeOnDay = annualFeeOnMonthDay.getDayOfMonth();
updateAnnualFeeNextDueDate();
}
this.summary = new SavingsAccountSummary(); this.summary = new SavingsAccountSummary();
} }
private void updateAnnualFeeNextDueDate() {
LocalDate nextDueLocalDate = new LocalDate().withMonthOfYear(this.annualFeeOnMonth).withDayOfMonth(this.annualFeeOnDay);
if (nextDueLocalDate.isBefore(DateUtils.getLocalDateOfTenant())) {
nextDueLocalDate = nextDueLocalDate.plusYears(1);
}
this.annualFeeNextDueDate = nextDueLocalDate.toDate();
}
/** /**
* Used after fetching/hydrating a {@link SavingsAccount} object to inject * Used after fetching/hydrating a {@link SavingsAccount} object to inject
* helper services/components used for update summary details after * helper services/components used for update summary details after
@@ -834,6 +875,10 @@ public class SavingsAccount extends AbstractPersistable<Long> {
} }
} }
final Money annualFee = Money.of(this.currency, this.annualFeeAmount);
SavingsAccountTransaction annualFeeTransaction = SavingsAccountTransaction.annualFee(this, transactionDate, annualFee);
this.transactions.add(annualFeeTransaction);
this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions);
final LocalDate today = DateUtils.getLocalDateOfTenant(); final LocalDate today = DateUtils.getLocalDateOfTenant();
@@ -842,6 +887,71 @@ public class SavingsAccount extends AbstractPersistable<Long> {
return transaction; return transaction;
} }
public SavingsAccountTransaction addAnnualFee(final DateTimeFormatter formatter, final LocalDate transactionDate) {
if (isNotActive()) {
final String defaultUserMessage = "Transaction is not allowed. Account is not active.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.savingsaccount.transaction.account.is.not.active",
defaultUserMessage, "transactionDate", transactionDate.toString(formatter));
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException(dataValidationErrors);
}
if (isDateInTheFuture(transactionDate)) {
final String defaultUserMessage = "Transaction date cannot be in the future.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.savingsaccount.transaction.in.the.future",
defaultUserMessage, "transactionDate", transactionDate.toString(formatter));
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException(dataValidationErrors);
}
if (transactionDate.isBefore(getActivationLocalDate())) {
final Object[] defaultUserArgs = Arrays.asList(transactionDate.toString(formatter),
getActivationLocalDate().toString(formatter)).toArray();
final String defaultUserMessage = "Transaction date cannot be before accounts activation date.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.savingsaccount.transaction.before.activation.date",
defaultUserMessage, "transactionDate", defaultUserArgs);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException(dataValidationErrors);
}
if (isAccountLocked(transactionDate)) {
final String defaultUserMessage = "Withdrawal is not allowed. No withdrawals are allowed until after "
+ getLockedInUntilLocalDate().toString(formatter);
final ApiParameterError error = ApiParameterError.parameterError(
"error.msg.savingsaccount.transaction.withdrawals.blocked.during.lockin.period", defaultUserMessage, "transactionDate",
transactionDate.toString(formatter), getLockedInUntilLocalDate().toString(formatter));
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException(dataValidationErrors);
}
final Money annualFee = Money.of(this.currency, this.annualFeeAmount);
SavingsAccountTransaction annualFeeTransaction = SavingsAccountTransaction.annualFee(this, transactionDate, annualFee);
this.transactions.add(annualFeeTransaction);
this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions);
final LocalDate today = DateUtils.getLocalDateOfTenant();
calculateInterest(today);
updateAnnualFeeNextDueDate();
return annualFeeTransaction;
}
private boolean isAutomaticWithdrawalFee() { private boolean isAutomaticWithdrawalFee() {
return this.withdrawalFeeType != null; return this.withdrawalFeeType != null;
} }
@@ -942,6 +1052,48 @@ public class SavingsAccount extends AbstractPersistable<Long> {
this.lockinPeriodFrequencyType = null; this.lockinPeriodFrequencyType = null;
} }
} }
if (command.isChangeInBigDecimalParameterNamed(withdrawalFeeAmountParamName, this.withdrawalFeeAmount)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(withdrawalFeeAmountParamName);
actualChanges.put(withdrawalFeeAmountParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
this.withdrawalFeeAmount = newValue;
}
if (command.isChangeInIntegerParameterNamed(withdrawalFeeTypeParamName, this.withdrawalFeeType)) {
final Integer newValue = command.integerValueOfParameterNamed(withdrawalFeeTypeParamName);
actualChanges.put(withdrawalFeeTypeParamName, newValue);
this.withdrawalFeeType = SavingsWithdrawalFeesType.fromInt(newValue).getValue();
}
if (command.isChangeInBigDecimalParameterNamed(annualFeeAmountParamName, this.annualFeeAmount)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(annualFeeAmountParamName);
actualChanges.put(annualFeeAmountParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
this.annualFeeAmount = newValue;
}
if (command.isChangeInIntegerParameterNamed(annualFeeOnMonthDayParamName, this.annualFeeOnDay)) {
final MonthDay monthDay = command.extractMonthDayNamed(annualFeeOnMonthDayParamName);
final String actualValueEntered = command.stringValueOfParameterNamed(annualFeeOnMonthDayParamName);
final Integer newValue = monthDay.getDayOfMonth();
actualChanges.put(annualFeeOnMonthDayParamName, actualValueEntered);
actualChanges.put(localeParamName, localeAsInput);
this.annualFeeOnDay = newValue;
updateAnnualFeeNextDueDate();
}
if (command.isChangeInIntegerParameterNamed(annualFeeOnMonthDayParamName, this.annualFeeOnMonth)) {
final MonthDay monthDay = command.extractMonthDayNamed(annualFeeOnMonthDayParamName);
final String actualValueEntered = command.stringValueOfParameterNamed(annualFeeOnMonthDayParamName);
final Integer newValue = monthDay.getMonthOfYear();
actualChanges.put(annualFeeOnMonthDayParamName, actualValueEntered);
actualChanges.put(localeParamName, localeAsInput);
this.annualFeeOnMonth = newValue;
updateAnnualFeeNextDueDate();
}
} }
public void update(final Client client) { public void update(final Client client) {
@@ -7,6 +7,8 @@ package org.mifosplatform.portfolio.savings.domain;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activationDateParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activationDateParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activeParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationDaysInYearTypeParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationTypeParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCompoundingPeriodTypeParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
@@ -22,6 +24,7 @@ import java.math.BigDecimal;
import java.util.Locale; import java.util.Locale;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
@@ -161,7 +164,7 @@ public class SavingsAccountAssembler {
} else { } else {
withdrawalFeeAmount = product.withdrawalFeeAmount(); withdrawalFeeAmount = product.withdrawalFeeAmount();
} }
SavingsWithdrawalFeesType withdrawalFeeType = null; SavingsWithdrawalFeesType withdrawalFeeType = null;
if (command.parameterExists(withdrawalFeeAmountParamName)) { if (command.parameterExists(withdrawalFeeAmountParamName)) {
final Integer withdrawalFeeTypeValue = command.integerValueOfParameterNamed(withdrawalFeeTypeParamName); final Integer withdrawalFeeTypeValue = command.integerValueOfParameterNamed(withdrawalFeeTypeParamName);
@@ -172,10 +175,24 @@ public class SavingsAccountAssembler {
withdrawalFeeType = product.withdrawalFeeType(); withdrawalFeeType = product.withdrawalFeeType();
} }
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 SavingsAccount account = SavingsAccount.createNewAccount(client, group, product, accountNo, externalId, interestRate, final SavingsAccount account = SavingsAccount.createNewAccount(client, group, product, accountNo, externalId, interestRate,
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType); minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType,
annualFeeAmount, monthDayOfAnnualFee);
account.setHelpers(this.savingsAccountTransactionSummaryWrapper); account.setHelpers(this.savingsAccountTransactionSummaryWrapper);
if (active) { if (active) {
@@ -32,6 +32,9 @@ public final class SavingsAccountSummary {
@Column(name = "total_withdrawal_fees_derived", scale = 6, precision = 19) @Column(name = "total_withdrawal_fees_derived", scale = 6, precision = 19)
private BigDecimal totalWithdrawalFees; private BigDecimal totalWithdrawalFees;
@Column(name = "total_annual_fees_derived", scale = 6, precision = 19)
private BigDecimal totalAnnualFees;
@Column(name = "account_balance_derived", scale = 6, precision = 19) @Column(name = "account_balance_derived", scale = 6, precision = 19)
private BigDecimal accountBalance = BigDecimal.ZERO; private BigDecimal accountBalance = BigDecimal.ZERO;
@@ -46,9 +49,10 @@ public final class SavingsAccountSummary {
this.totalWithdrawals = wrapper.calculateTotalWithdrawals(currency, transactions); this.totalWithdrawals = wrapper.calculateTotalWithdrawals(currency, transactions);
this.totalInterestPosted = wrapper.calculateTotalInterestPosted(currency, transactions); this.totalInterestPosted = wrapper.calculateTotalInterestPosted(currency, transactions);
this.totalWithdrawalFees = wrapper.calculateTotalWithdrawalFees(currency, transactions); this.totalWithdrawalFees = wrapper.calculateTotalWithdrawalFees(currency, transactions);
this.totalAnnualFees = wrapper.calculateTotalAnnualFees(currency, transactions);
this.accountBalance = Money.of(currency, this.totalDeposits).plus(this.totalInterestPosted).minus(this.totalWithdrawals) this.accountBalance = Money.of(currency, this.totalDeposits).plus(this.totalInterestPosted).minus(this.totalWithdrawals)
.minus(this.totalWithdrawalFees).getAmount(); .minus(this.totalWithdrawalFees).minus(this.totalAnnualFees).getAmount();
} }
public void updateFromInterestPeriodSummaries(final MonetaryCurrency currency, public void updateFromInterestPeriodSummaries(final MonetaryCurrency currency,
@@ -88,6 +88,12 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
isReversed); isReversed);
} }
public static SavingsAccountTransaction annualFee(final SavingsAccount savingsAccount, final LocalDate date, final Money amount) {
final boolean isReversed = false;
return new SavingsAccountTransaction(savingsAccount, SavingsAccountTransactionType.ANNUAL_FEE.getValue(), date, amount,
isReversed);
}
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Integer typeOf, final LocalDate transactionLocalDate, private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Integer typeOf, final LocalDate transactionLocalDate,
final Money amount, final boolean isReversed) { final Money amount, final boolean isReversed) {
this.savingsAccount = savingsAccount; this.savingsAccount = savingsAccount;
@@ -125,6 +131,10 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
return SavingsAccountTransactionType.fromInt(this.typeOf).isWithdrawalFee() && isNotReversed(); return SavingsAccountTransactionType.fromInt(this.typeOf).isWithdrawalFee() && isNotReversed();
} }
public boolean isAnnualFee() {
return SavingsAccountTransactionType.fromInt(this.typeOf).isAnnualFee() && isNotReversed();
}
public boolean isNotReversed() { public boolean isNotReversed() {
return !isReversed(); return !isReversed();
} }
@@ -53,4 +53,14 @@ public final class SavingsAccountTransactionSummaryWrapper {
} }
return total.getAmountDefaultedToNullIfZero(); return total.getAmountDefaultedToNullIfZero();
} }
public BigDecimal calculateTotalAnnualFees(final MonetaryCurrency currency, final List<SavingsAccountTransaction> transactions) {
Money total = Money.zero(currency);
for (SavingsAccountTransaction transaction : transactions) {
if (transaction.isAnnualFee() && transaction.isNotReversed()) {
total = total.plus(transaction.getAmount(currency));
}
}
return total.getAmountDefaultedToNullIfZero();
}
} }
@@ -15,7 +15,8 @@ public enum SavingsAccountTransactionType {
DEPOSIT(1, "savingsAccountTransactionType.deposit"), // DEPOSIT(1, "savingsAccountTransactionType.deposit"), //
WITHDRAWAL(2, "savingsAccountTransactionType.withdrawal"), // WITHDRAWAL(2, "savingsAccountTransactionType.withdrawal"), //
INTEREST_POSTING(3, "savingsAccountTransactionType.interestPosting"), // INTEREST_POSTING(3, "savingsAccountTransactionType.interestPosting"), //
WITHDRAWAL_FEE(4, "savingsAccountTransactionType.withdrawalFee"); WITHDRAWAL_FEE(4, "savingsAccountTransactionType.withdrawalFee"), //
ANNUAL_FEE(5, "savingsAccountTransactionType.annualFee");
private final Integer value; private final Integer value;
private final String code; private final String code;
@@ -51,6 +52,9 @@ public enum SavingsAccountTransactionType {
case 4: case 4:
loanTransactionType = SavingsAccountTransactionType.WITHDRAWAL_FEE; loanTransactionType = SavingsAccountTransactionType.WITHDRAWAL_FEE;
break; break;
case 5:
loanTransactionType = SavingsAccountTransactionType.ANNUAL_FEE;
break;
} }
return loanTransactionType; return loanTransactionType;
} }
@@ -70,4 +74,8 @@ public enum SavingsAccountTransactionType {
public boolean isWithdrawalFee() { public boolean isWithdrawalFee() {
return this.value.equals(SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue()); return this.value.equals(SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue());
} }
public boolean isAnnualFee() {
return this.value.equals(SavingsAccountTransactionType.ANNUAL_FEE.getValue());
}
} }
@@ -5,6 +5,8 @@
*/ */
package org.mifosplatform.portfolio.savings.domain; package org.mifosplatform.portfolio.savings.domain;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.currencyCodeParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.currencyCodeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.descriptionParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.descriptionParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.digitsAfterDecimalParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.digitsAfterDecimalParamName;
@@ -30,6 +32,7 @@ import javax.persistence.Embedded;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.exception.GeneralPlatformDomainRuleException; import org.mifosplatform.infrastructure.core.exception.GeneralPlatformDomainRuleException;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
@@ -55,7 +58,7 @@ public class SavingsProduct extends AbstractPersistable<Long> {
/** /**
* The interest period is the span of time at the end of which savings in a * The interest period is the span of time at the end of which savings in a
* client's account earn interest. * client's account earn interest.
* *
* A value from the {@link SavingsCompoundingInterestPeriodType} * A value from the {@link SavingsCompoundingInterestPeriodType}
* enumeration. * enumeration.
*/ */
@@ -96,16 +99,26 @@ public class SavingsProduct extends AbstractPersistable<Long> {
@Column(name = "withdrawal_fee_type_enum", nullable = true) @Column(name = "withdrawal_fee_type_enum", nullable = true)
private Integer withdrawalFeeType; private Integer withdrawalFeeType;
@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;
public static SavingsProduct createNew(final String name, final String description, final MonetaryCurrency currency, public static SavingsProduct createNew(final String name, final String description, final MonetaryCurrency currency,
final BigDecimal interestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, final BigDecimal interestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType) { final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType, final BigDecimal annualFeeAmount,
final MonthDay annualFeeOnMonthDay) {
return new SavingsProduct(name, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, return new SavingsProduct(name, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType); lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, annualFeeOnMonthDay);
} }
protected SavingsProduct() { protected SavingsProduct() {
@@ -118,7 +131,8 @@ public class SavingsProduct extends AbstractPersistable<Long> {
final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType) { final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType, final BigDecimal annualFeeAmount,
final MonthDay annualFeeOnMonthDay) {
this.name = name; this.name = name;
this.description = description; this.description = description;
@@ -143,9 +157,15 @@ public class SavingsProduct extends AbstractPersistable<Long> {
if (withdrawalFeeType != null) { if (withdrawalFeeType != null) {
this.withdrawalFeeType = withdrawalFeeType.getValue(); this.withdrawalFeeType = withdrawalFeeType.getValue();
} }
this.annualFeeAmount = annualFeeAmount;
if (annualFeeOnMonthDay != null) {
this.annualFeeOnMonth = annualFeeOnMonthDay.getMonthOfYear();
this.annualFeeOnDay = annualFeeOnMonthDay.getDayOfMonth();
}
validateLockinDetails(); validateLockinDetails();
validateWithdrawalFeeDetails(); validateWithdrawalFeeDetails();
validateAnnualFeeDetails();
} }
public MonetaryCurrency currency() { public MonetaryCurrency currency() {
@@ -200,6 +220,18 @@ public class SavingsProduct extends AbstractPersistable<Long> {
return type; 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<String, Object> update(final JsonCommand command) { public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(10); final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(10);
@@ -299,12 +331,51 @@ public class SavingsProduct extends AbstractPersistable<Long> {
this.withdrawalFeeType = SavingsWithdrawalFeesType.fromInt(newValue).getValue(); this.withdrawalFeeType = SavingsWithdrawalFeesType.fromInt(newValue).getValue();
} }
if (command.isChangeInBigDecimalParameterNamed(annualFeeAmountParamName, this.annualFeeAmount)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(annualFeeAmountParamName);
actualChanges.put(annualFeeAmountParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
this.annualFeeAmount = newValue;
}
if (command.isChangeInIntegerParameterNamed(annualFeeOnMonthDayParamName, this.annualFeeOnDay)) {
final MonthDay monthDay = command.extractMonthDayNamed(annualFeeOnMonthDayParamName);
final String actualValueEntered = command.stringValueOfParameterNamed(annualFeeOnMonthDayParamName);
final Integer newValue = monthDay.getDayOfMonth();
actualChanges.put(annualFeeOnMonthDayParamName, actualValueEntered);
actualChanges.put(localeParamName, localeAsInput);
this.annualFeeOnDay = newValue;
}
if (command.isChangeInIntegerParameterNamed(annualFeeOnMonthDayParamName, this.annualFeeOnMonth)) {
final MonthDay monthDay = command.extractMonthDayNamed(annualFeeOnMonthDayParamName);
final String actualValueEntered = command.stringValueOfParameterNamed(annualFeeOnMonthDayParamName);
final Integer newValue = monthDay.getMonthOfYear();
actualChanges.put(annualFeeOnMonthDayParamName, actualValueEntered);
actualChanges.put(localeParamName, localeAsInput);
this.annualFeeOnMonth = newValue;
}
validateLockinDetails(); validateLockinDetails();
validateWithdrawalFeeDetails(); validateWithdrawalFeeDetails();
validateAnnualFeeDetails();
return actualChanges; return actualChanges;
} }
private void validateAnnualFeeDetails() {
if (isInvalidConfigurationOfAnnualFeeSettings()) {
Object[] defaultUserMessageArgs = new Object[] { annualFeeAmountParamName };
throw new GeneralPlatformDomainRuleException("error.msg.product.savings.invalid.annualfee.settings",
"Invalid configuration of annual fee settings.", defaultUserMessageArgs);
}
}
private boolean isInvalidConfigurationOfAnnualFeeSettings() {
return (this.annualFeeAmount == null && (this.annualFeeOnDay != null || this.annualFeeOnMonth != null))
|| ((this.annualFeeOnDay == null || this.annualFeeOnMonth == null) && this.annualFeeAmount != null);
}
private void validateWithdrawalFeeDetails() { private void validateWithdrawalFeeDetails() {
if (isInvalidConfigurationOfWithdrawalFeeSettings()) { if (isInvalidConfigurationOfWithdrawalFeeSettings()) {
Object[] defaultUserMessageArgs = new Object[] { withdrawalFeeAmountParamName }; Object[] defaultUserMessageArgs = new Object[] { withdrawalFeeAmountParamName };
@@ -330,4 +401,4 @@ public class SavingsProduct extends AbstractPersistable<Long> {
return (this.lockinPeriodFrequency == null && this.lockinPeriodFrequencyType != null) return (this.lockinPeriodFrequency == null && this.lockinPeriodFrequencyType != null)
|| (this.lockinPeriodFrequencyType == null && this.lockinPeriodFrequency != null); || (this.lockinPeriodFrequencyType == null && this.lockinPeriodFrequency != null);
} }
} }
@@ -1,5 +1,7 @@
package org.mifosplatform.portfolio.savings.domain; package org.mifosplatform.portfolio.savings.domain;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.currencyCodeParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.currencyCodeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.descriptionParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.descriptionParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.digitsAfterDecimalParamName; import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.digitsAfterDecimalParamName;
@@ -17,6 +19,7 @@ import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdr
import java.math.BigDecimal; import java.math.BigDecimal;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -76,8 +79,12 @@ public class SavingsProductAssembler {
withdrawalFeeType = SavingsWithdrawalFeesType.fromInt(withdrawalFeeTypeValue); withdrawalFeeType = SavingsWithdrawalFeesType.fromInt(withdrawalFeeTypeValue);
} }
final BigDecimal annualFeeAmount = command.bigDecimalValueOfParameterNamed(annualFeeAmountParamName);
final MonthDay monthDayOfAnnualFee = command.extractMonthDayNamed(annualFeeOnMonthDayParamName);
return SavingsProduct.createNew(name, description, currency, interestRate, interestCompoundingPeriodType, return SavingsProduct.createNew(name, description, currency, interestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType); lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount,
monthDayOfAnnualFee);
} }
} }
@@ -11,6 +11,7 @@ import java.sql.SQLException;
import java.util.Collection; import java.util.Collection;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.infrastructure.core.service.DateUtils;
@@ -114,9 +115,14 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
sqlBuilder.append("sa.lockin_period_frequency_enum as lockinPeriodFrequencyType, "); sqlBuilder.append("sa.lockin_period_frequency_enum as lockinPeriodFrequencyType, ");
sqlBuilder.append("sa.withdrawal_fee_amount as withdrawalFeeAmount,"); sqlBuilder.append("sa.withdrawal_fee_amount as withdrawalFeeAmount,");
sqlBuilder.append("sa.withdrawal_fee_type_enum as withdrawalFeeTypeEnum, "); sqlBuilder.append("sa.withdrawal_fee_type_enum as withdrawalFeeTypeEnum, ");
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_deposits_derived as totalDeposits, ");
sqlBuilder.append("sa.total_withdrawals_derived as totalWithdrawals, "); sqlBuilder.append("sa.total_withdrawals_derived as totalWithdrawals, ");
sqlBuilder.append("sa.total_withdrawal_fees_derived as totalWithdrawalFees, "); sqlBuilder.append("sa.total_withdrawal_fees_derived as totalWithdrawalFees, ");
sqlBuilder.append("sa.total_annual_fees_derived as totalAnnualFees, ");
sqlBuilder.append("sa.total_interest_earned_derived as totalInterestEarned, "); sqlBuilder.append("sa.total_interest_earned_derived as totalInterestEarned, ");
sqlBuilder.append("sa.total_interest_posted_derived as totalInterestPosted, "); sqlBuilder.append("sa.total_interest_posted_derived as totalInterestPosted, ");
sqlBuilder.append("sa.account_balance_derived as accountBalance "); sqlBuilder.append("sa.account_balance_derived as accountBalance ");
@@ -195,21 +201,34 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
withdrawalFeeType = SavingsEnumerations.withdrawalFeeType(withdrawalFeeTypeValue); withdrawalFeeType = SavingsEnumerations.withdrawalFeeType(withdrawalFeeTypeValue);
} }
final BigDecimal annualFeeAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "annualFeeAmount");
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);
}
final LocalDate annualFeeNextDueDate = JdbcSupport.getLocalDate(rs, "annualFeeNextDueDate");
final BigDecimal totalDeposits = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalDeposits"); final BigDecimal totalDeposits = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalDeposits");
final BigDecimal totalWithdrawals = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalWithdrawals"); final BigDecimal totalWithdrawals = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalWithdrawals");
final BigDecimal totalWithdrawalFees = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalWithdrawalFees"); final BigDecimal totalWithdrawalFees = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalWithdrawalFees");
final BigDecimal totalAnnualFees = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalAnnualFees");
final BigDecimal totalInterestEarned = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalInterestEarned"); final BigDecimal totalInterestEarned = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalInterestEarned");
final BigDecimal totalInterestPosted = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalInterestPosted"); final BigDecimal totalInterestPosted = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalInterestPosted");
final BigDecimal accountBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "accountBalance"); final BigDecimal accountBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "accountBalance");
final SavingsAccountSummaryData summary = new SavingsAccountSummaryData(currency, totalDeposits, totalWithdrawals, final SavingsAccountSummaryData summary = new SavingsAccountSummaryData(currency, totalDeposits, totalWithdrawals,
totalWithdrawalFees, totalInterestEarned, totalInterestPosted, accountBalance); totalWithdrawalFees, totalAnnualFees, totalInterestEarned, totalInterestPosted, accountBalance);
return SavingsAccountData.instance(id, accountNo, externalId, status, activationDate, groupId, groupName, clientId, clientName, return SavingsAccountData.instance(id, accountNo, externalId, status, activationDate, groupId, groupName, clientId, clientName,
productId, productName, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, productId, productName, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, summary); lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, annualFeeOnMonthDay,
annualFeeNextDueDate, summary);
} }
} }
@@ -425,7 +444,10 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
sqlBuilder.append("sp.lockin_period_frequency as lockinPeriodFrequency,"); sqlBuilder.append("sp.lockin_period_frequency as lockinPeriodFrequency,");
sqlBuilder.append("sp.lockin_period_frequency_enum as lockinPeriodFrequencyType, "); sqlBuilder.append("sp.lockin_period_frequency_enum as lockinPeriodFrequencyType, ");
sqlBuilder.append("sp.withdrawal_fee_amount as withdrawalFeeAmount,"); sqlBuilder.append("sp.withdrawal_fee_amount as withdrawalFeeAmount,");
sqlBuilder.append("sp.withdrawal_fee_type_enum as withdrawalFeeTypeEnum "); sqlBuilder.append("sp.withdrawal_fee_type_enum as withdrawalFeeTypeEnum, ");
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("from m_savings_product sp ");
sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code "); sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code ");
@@ -484,6 +506,15 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
withdrawalFeeType = SavingsEnumerations.withdrawalFeeType(withdrawalFeeTypeValue); withdrawalFeeType = SavingsEnumerations.withdrawalFeeType(withdrawalFeeTypeValue);
} }
final BigDecimal annualFeeAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "annualFeeAmount");
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; Long clientId = null;
String clientName = null; String clientName = null;
if (client != null) { if (client != null) {
@@ -500,11 +531,14 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final SavingsAccountStatusEnumData status = null; final SavingsAccountStatusEnumData status = null;
final LocalDate activationDate = null; final LocalDate activationDate = null;
final LocalDate annualFeeNextDueDate = null;
final SavingsAccountSummaryData summary = null; final SavingsAccountSummaryData summary = null;
return SavingsAccountData.instance(null, null, null, status, activationDate, groupId, groupName, clientId, clientName, return SavingsAccountData.instance(null, null, null, status, activationDate, groupId, groupName, clientId, clientName,
productId, productName, currency, nominalAnnualIterestRate, interestCompoundingPeriodType, interestPostingPeriodType, productId, productName, currency, nominalAnnualIterestRate, interestCompoundingPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, summary); lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, annualFeeOnMonthDay,
annualFeeNextDueDate, summary);
} }
} }
} }
@@ -79,7 +79,10 @@ public class SavingsEnumerations {
case WITHDRAWAL_FEE: case WITHDRAWAL_FEE:
optionData = new SavingsAccountTransactionEnumData(SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue().longValue(), optionData = new SavingsAccountTransactionEnumData(SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue().longValue(),
SavingsAccountTransactionType.WITHDRAWAL_FEE.getCode(), "Withdrawal fee"); SavingsAccountTransactionType.WITHDRAWAL_FEE.getCode(), "Withdrawal fee");
break;
case ANNUAL_FEE:
optionData = new SavingsAccountTransactionEnumData(SavingsAccountTransactionType.ANNUAL_FEE.getValue().longValue(),
SavingsAccountTransactionType.ANNUAL_FEE.getCode(), "Annual fee");
break; break;
} }
return optionData; return optionData;
@@ -10,6 +10,7 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Collection; import java.util.Collection;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource; import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
@@ -85,7 +86,10 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
sqlBuilder.append("sp.lockin_period_frequency as lockinPeriodFrequency,"); sqlBuilder.append("sp.lockin_period_frequency as lockinPeriodFrequency,");
sqlBuilder.append("sp.lockin_period_frequency_enum as lockinPeriodFrequencyType, "); sqlBuilder.append("sp.lockin_period_frequency_enum as lockinPeriodFrequencyType, ");
sqlBuilder.append("sp.withdrawal_fee_amount as withdrawalFeeAmount,"); sqlBuilder.append("sp.withdrawal_fee_amount as withdrawalFeeAmount,");
sqlBuilder.append("sp.withdrawal_fee_type_enum as withdrawalFeeTypeEnum "); sqlBuilder.append("sp.withdrawal_fee_type_enum as withdrawalFeeTypeEnum, ");
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("from m_savings_product sp ");
sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code "); sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code ");
@@ -147,9 +151,19 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
withdrawalFeeType = SavingsEnumerations.withdrawalFeeType(withdrawalFeeTypeValue); withdrawalFeeType = SavingsEnumerations.withdrawalFeeType(withdrawalFeeTypeValue);
} }
final BigDecimal annualFeeAmount = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "annualFeeAmount");
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);
}
return SavingsProductData.instance(id, name, description, currency, nominalAnnualInterestRate, compoundingInterestPeriodType, return SavingsProductData.instance(id, name, description, currency, nominalAnnualInterestRate, compoundingInterestPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType); lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount,
annualFeeOnMonthDay);
} }
} }