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")
@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;
if (src != null) {
@@ -23,7 +23,7 @@ public class JodaLocalDateAdapter implements JsonSerializer<LocalDate> {
@SuppressWarnings("unused")
@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;
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.StringUtils;
import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.infrastructure.security.domain.BasicPasswordEncodablePlatformUser;
import org.mifosplatform.infrastructure.security.domain.PlatformUser;
@@ -261,6 +262,10 @@ public final class JsonCommand {
return this.fromApiJsonHelper.extractLocalDateNamed(parameterName, parsedCommand);
}
public MonthDay extractMonthDayNamed(final String parameterName) {
return this.fromApiJsonHelper.extractMonthDayNamed(parameterName, parsedCommand);
}
public Date DateValueOfParameterNamed(final String parameterName) {
LocalDate localDate = this.fromApiJsonHelper.extractLocalDateNamed(parameterName, parsedCommand);
if (localDate == null) { return null; }
@@ -7,8 +7,10 @@ package org.mifosplatform.infrastructure.core.serialization;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.api.JodaDateTimeAdapter;
import org.mifosplatform.infrastructure.core.api.JodaLocalDateAdapter;
import org.mifosplatform.infrastructure.core.api.JodaMonthDayAdapter;
import org.springframework.stereotype.Component;
import com.google.gson.Gson;
@@ -28,6 +30,7 @@ public final class CommandProcessingResultJsonSerializer {
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(LocalDate.class, new JodaLocalDateAdapter());
builder.registerTypeAdapter(DateTime.class, new JodaDateTimeAdapter());
builder.registerTypeAdapter(MonthDay.class, new JodaMonthDayAdapter());
builder.serializeNulls();
this.gson = builder.create();
@@ -7,8 +7,10 @@ package org.mifosplatform.infrastructure.core.serialization;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.api.JodaDateTimeAdapter;
import org.mifosplatform.infrastructure.core.api.JodaLocalDateAdapter;
import org.mifosplatform.infrastructure.core.api.JodaMonthDayAdapter;
import org.springframework.stereotype.Component;
import com.google.gson.Gson;
@@ -28,6 +30,7 @@ public final class ExcludeNothingWithPrettyPrintingOffJsonSerializerGoogleGson {
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(LocalDate.class, new JodaLocalDateAdapter());
builder.registerTypeAdapter(DateTime.class, new JodaDateTimeAdapter());
builder.registerTypeAdapter(MonthDay.class, new JodaMonthDayAdapter());
this.gson = builder.create();
}
@@ -7,8 +7,10 @@ package org.mifosplatform.infrastructure.core.serialization;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.api.JodaDateTimeAdapter;
import org.mifosplatform.infrastructure.core.api.JodaLocalDateAdapter;
import org.mifosplatform.infrastructure.core.api.JodaMonthDayAdapter;
import org.springframework.stereotype.Component;
import com.google.gson.Gson;
@@ -28,6 +30,7 @@ public final class ExcludeNothingWithPrettyPrintingOnJsonSerializerGoogleGson {
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(LocalDate.class, new JodaLocalDateAdapter());
builder.registerTypeAdapter(DateTime.class, new JodaDateTimeAdapter());
builder.registerTypeAdapter(MonthDay.class, new JodaMonthDayAdapter());
builder.setPrettyPrinting();
this.gson = builder.create();
@@ -16,6 +16,7 @@ import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.UnsupportedParameterException;
import org.springframework.stereotype.Component;
@@ -114,6 +115,10 @@ public class FromJsonHelper {
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) {
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.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.api.JodaDateTimeAdapter;
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.exception.UnsupportedParameterException;
import org.springframework.stereotype.Service;
@@ -31,6 +33,7 @@ public final class GoogleGsonSerializerHelper {
final GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(LocalDate.class, new JodaLocalDateAdapter());
builder.registerTypeAdapter(DateTime.class, new JodaDateTimeAdapter());
builder.registerTypeAdapter(MonthDay.class, new JodaMonthDayAdapter());
if (prettyPrint) {
builder.setPrettyPrinting();
}
@@ -61,6 +64,7 @@ public final class GoogleGsonSerializerHelper {
final GsonBuilder builder = new GsonBuilder().addSerializationExclusionStrategy(strategy);
builder.registerTypeAdapter(LocalDate.class, new JodaLocalDateAdapter());
builder.registerTypeAdapter(DateTime.class, new JodaDateTimeAdapter());
builder.registerTypeAdapter(MonthDay.class, new JodaMonthDayAdapter());
if (prettyPrint) {
builder.setPrettyPrinting();
}
@@ -18,7 +18,9 @@ import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.springframework.format.number.NumberFormatter;
@@ -181,6 +183,20 @@ public class JsonParserHelper {
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) {
Locale clientApplicationLocale = null;
if (element.isJsonObject()) {
@@ -252,6 +268,40 @@ public class JsonParserHelper {
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,
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") })
public class LoanProduct extends AbstractPersistable<Long> {
@ManyToOne
@JoinColumn(name = "fund_id", nullable = true)
private Fund fund;
@ManyToOne
@JoinColumn(name = "fund_id", nullable = true)
private Fund fund;
@ManyToOne
@JoinColumn(name = "loan_transaction_strategy_id", nullable = true)
private LoanTransactionProcessingStrategy transactionProcessingStrategy;
@ManyToOne
@JoinColumn(name = "loan_transaction_strategy_id", nullable = true)
private LoanTransactionProcessingStrategy transactionProcessingStrategy;
@Column(name = "name", nullable = false, unique = true)
private String name;
@Column(name = "name", nullable = false, unique = true)
private String name;
@Column(name = "description")
private String description;
@Column(name = "description")
private String description;
@ManyToMany
@JoinTable(name = "m_product_loan_charge", joinColumns = @JoinColumn(name = "product_loan_id"), inverseJoinColumns = @JoinColumn(name = "charge_id"))
private List<Charge> charges;
@ManyToMany
@JoinTable(name = "m_product_loan_charge", joinColumns = @JoinColumn(name = "product_loan_id"), inverseJoinColumns = @JoinColumn(name = "charge_id"))
private List<Charge> charges;
@Embedded
private final LoanProductRelatedDetail loanProductRelatedDetail;
@Embedded
private final LoanProductRelatedDetail loanProductRelatedDetail;
@Embedded
private LoanProductMinMaxConstraints loanProductMinMaxConstraints;
@Embedded
private LoanProductMinMaxConstraints loanProductMinMaxConstraints;
@Column(name = "accounting_type", nullable = false)
private Integer accountingRule;
@Column(name = "accounting_type", nullable = false)
private Integer accountingRule;
public static LoanProduct assembleFromJson(
final Fund fund,
final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy,
final List<Charge> productCharges, final JsonCommand command,
final AprCalculator aprCalculator) {
public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy,
final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator) {
final String name = command.stringValueOfParameterNamed("name");
final String description = command
.stringValueOfParameterNamed("description");
final String currencyCode = command
.stringValueOfParameterNamed("currencyCode");
final Integer digitsAfterDecimal = command
.integerValueOfParameterNamed("digitsAfterDecimal");
final String name = command.stringValueOfParameterNamed("name");
final String description = command.stringValueOfParameterNamed("description");
final String currencyCode = command.stringValueOfParameterNamed("currencyCode");
final Integer digitsAfterDecimal = command.integerValueOfParameterNamed("digitsAfterDecimal");
final MonetaryCurrency currency = new MonetaryCurrency(currencyCode,
digitsAfterDecimal);
final BigDecimal principal = command
.bigDecimalValueOfParameterNamed("principal");
final BigDecimal minPrincipal = command
.bigDecimalValueOfParameterNamed("minPrincipal");
final BigDecimal maxPrincipal = command
.bigDecimalValueOfParameterNamed("maxPrincipal");
final MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal);
final BigDecimal principal = command.bigDecimalValueOfParameterNamed("principal");
final BigDecimal minPrincipal = command.bigDecimalValueOfParameterNamed("minPrincipal");
final BigDecimal maxPrincipal = command.bigDecimalValueOfParameterNamed("maxPrincipal");
final InterestMethod interestMethod = InterestMethod.fromInt(command
.integerValueOfParameterNamed("interestType"));
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod
.fromInt(command
.integerValueOfParameterNamed("interestCalculationPeriodType"));
final AmortizationMethod amortizationMethod = AmortizationMethod
.fromInt(command
.integerValueOfParameterNamed("amortizationType"));
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType
.fromInt(command
.integerValueOfParameterNamed("repaymentFrequencyType"));
final PeriodFrequencyType interestFrequencyType = PeriodFrequencyType
.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 InterestMethod interestMethod = InterestMethod.fromInt(command.integerValueOfParameterNamed("interestType"));
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command
.integerValueOfParameterNamed("interestCalculationPeriodType"));
final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.integerValueOfParameterNamed("amortizationType"));
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.fromInt(command
.integerValueOfParameterNamed("repaymentFrequencyType"));
final PeriodFrequencyType interestFrequencyType = PeriodFrequencyType.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
.integerValueOfParameterNamed("repaymentEvery");
final Integer numberOfRepayments = command
.integerValueOfParameterNamed("numberOfRepayments");
final Integer minNumberOfRepayments = command
.integerValueOfParameterNamed("minNumberOfRepayments");
final Integer maxNumberOfRepayments = command
.integerValueOfParameterNamed("maxNumberOfRepayments");
final BigDecimal inArrearsTolerance = command
.bigDecimalValueOfParameterNamed("inArrearsTolerance");
final AccountingRuleType accountingRuleType = AccountingRuleType
.fromInt(command.integerValueOfParameterNamed("accountingRule"));
final Integer repaymentEvery = command.integerValueOfParameterNamed("repaymentEvery");
final Integer numberOfRepayments = command.integerValueOfParameterNamed("numberOfRepayments");
final Integer minNumberOfRepayments = command.integerValueOfParameterNamed("minNumberOfRepayments");
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,
description, currency, principal, minPrincipal, maxPrincipal,
interestRatePerPeriod, minInterestRatePerPeriod,
maxInterestRatePerPeriod, interestFrequencyType,
annualInterestRate, interestMethod,
interestCalculationPeriodMethod, repaymentEvery,
repaymentFrequencyType, numberOfRepayments,
minNumberOfRepayments, maxNumberOfRepayments,
amortizationMethod, inArrearsTolerance, productCharges,
accountingRuleType);
}
return new LoanProduct(fund, loanTransactionProcessingStrategy, name, description, currency, principal, minPrincipal, maxPrincipal,
interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType, annualInterestRate,
interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentFrequencyType, numberOfRepayments,
minNumberOfRepayments, maxNumberOfRepayments, amortizationMethod, inArrearsTolerance, productCharges, accountingRuleType);
}
protected LoanProduct() {
this.loanProductRelatedDetail = null;
this.loanProductMinMaxConstraints = null;
}
protected LoanProduct() {
this.loanProductRelatedDetail = null;
this.loanProductMinMaxConstraints = null;
}
public LoanProduct(
final Fund fund,
final LoanTransactionProcessingStrategy transactionProcessingStrategy,
final String name,
final String description,
final MonetaryCurrency currency,
final BigDecimal defaultPrincipal,
final BigDecimal defaultMinPrincipal,
final BigDecimal defaultMaxPrincipal,
final BigDecimal defaultNominalInterestRatePerPeriod,
final BigDecimal defaultMinNominalInterestRatePerPeriod,
final BigDecimal defaultMaxNominalInterestRatePerPeriod,
final PeriodFrequencyType interestPeriodFrequencyType,
final BigDecimal defaultAnnualNominalInterestRate,
final InterestMethod interestMethod,
final InterestCalculationPeriodMethod interestCalculationPeriodMethod,
final Integer repayEvery,
final PeriodFrequencyType repaymentFrequencyType,
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;
}
public LoanProduct(final Fund fund, final LoanTransactionProcessingStrategy transactionProcessingStrategy, final String name,
final String description, final MonetaryCurrency currency, final BigDecimal defaultPrincipal,
final BigDecimal defaultMinPrincipal, final BigDecimal defaultMaxPrincipal,
final BigDecimal defaultNominalInterestRatePerPeriod, final BigDecimal defaultMinNominalInterestRatePerPeriod,
final BigDecimal defaultMaxNominalInterestRatePerPeriod, final PeriodFrequencyType interestPeriodFrequencyType,
final BigDecimal defaultAnnualNominalInterestRate, final InterestMethod interestMethod,
final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final Integer repayEvery,
final PeriodFrequencyType repaymentFrequencyType, 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) {
this.charges = charges;
}
if (charges != null) {
this.charges = charges;
}
this.loanProductRelatedDetail = new LoanProductRelatedDetail(currency,
defaultPrincipal, defaultNominalInterestRatePerPeriod,
interestPeriodFrequencyType, defaultAnnualNominalInterestRate,
interestMethod, interestCalculationPeriodMethod, repayEvery,
repaymentFrequencyType, defaultNumberOfInstallments,
amortizationMethod, inArrearsTolerance);
this.loanProductRelatedDetail = new LoanProductRelatedDetail(currency, defaultPrincipal, defaultNominalInterestRatePerPeriod,
interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery,
repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod, inArrearsTolerance);
this.loanProductMinMaxConstraints = new LoanProductMinMaxConstraints(
defaultMinPrincipal, defaultMaxPrincipal,
defaultMinNominalInterestRatePerPeriod,
defaultMaxNominalInterestRatePerPeriod,
defaultMinNumberOfInstallments, defaultMaxNumberOfInstallments);
this.loanProductMinMaxConstraints = new LoanProductMinMaxConstraints(defaultMinPrincipal, defaultMaxPrincipal,
defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod, defaultMinNumberOfInstallments,
defaultMaxNumberOfInstallments);
if (accountingRuleType != null) {
this.accountingRule = accountingRuleType.getValue();
}
}
if (accountingRuleType != null) {
this.accountingRule = accountingRuleType.getValue();
}
}
public MonetaryCurrency getCurrency() {
return this.loanProductRelatedDetail.getCurrency();
}
public MonetaryCurrency getCurrency() {
return this.loanProductRelatedDetail.getCurrency();
}
public void update(final Fund fund) {
this.fund = fund;
}
public void update(final Fund fund) {
this.fund = fund;
}
public void update(final LoanTransactionProcessingStrategy strategy) {
this.transactionProcessingStrategy = strategy;
}
public void update(final LoanTransactionProcessingStrategy strategy) {
this.transactionProcessingStrategy = strategy;
}
public boolean update(final List<Charge> newProductCharges) {
if (newProductCharges == null)
return false;
public boolean update(final List<Charge> newProductCharges) {
if (newProductCharges == null) return false;
boolean updated = false;
if (this.charges != null) {
final Set<Charge> currentSetOfCharges = new HashSet<Charge>(
this.charges);
final Set<Charge> newSetOfCharges = new HashSet<Charge>(
newProductCharges);
boolean updated = false;
if (this.charges != null) {
final Set<Charge> currentSetOfCharges = new HashSet<Charge>(this.charges);
final Set<Charge> newSetOfCharges = new HashSet<Charge>(newProductCharges);
if (!(currentSetOfCharges.equals(newSetOfCharges))) {
updated = true;
this.charges = newProductCharges;
}
} else {
updated = true;
this.charges = newProductCharges;
}
return updated;
}
if (!(currentSetOfCharges.equals(newSetOfCharges))) {
updated = true;
this.charges = newProductCharges;
}
} else {
updated = true;
this.charges = newProductCharges;
}
return updated;
}
public Integer getAccountingType() {
return this.accountingRule;
}
public Integer getAccountingType() {
return this.accountingRule;
}
public Map<String, Object> update(final JsonCommand command,
final AprCalculator aprCalculator) {
public Map<String, Object> update(final JsonCommand command, final AprCalculator aprCalculator) {
final Map<String, Object> actualChanges = this.loanProductRelatedDetail
.update(command, aprCalculator);
actualChanges.putAll(this.loanProductMinMaxConstraints()
.update(command));
final Map<String, Object> actualChanges = this.loanProductRelatedDetail.update(command, aprCalculator);
actualChanges.putAll(this.loanProductMinMaxConstraints().update(command));
final String accountingTypeParamName = "accountingRule";
if (command.isChangeInIntegerParameterNamed(accountingTypeParamName,
this.accountingRule)) {
final Integer newValue = command
.integerValueOfParameterNamed(accountingTypeParamName);
actualChanges.put(accountingTypeParamName, newValue);
this.accountingRule = newValue;
}
final String accountingTypeParamName = "accountingRule";
if (command.isChangeInIntegerParameterNamed(accountingTypeParamName, this.accountingRule)) {
final Integer newValue = command.integerValueOfParameterNamed(accountingTypeParamName);
actualChanges.put(accountingTypeParamName, newValue);
this.accountingRule = newValue;
}
final String nameParamName = "name";
if (command.isChangeInStringParameterNamed(nameParamName, this.name)) {
final String newValue = command
.stringValueOfParameterNamed(nameParamName);
actualChanges.put(nameParamName, newValue);
this.name = newValue;
}
final String nameParamName = "name";
if (command.isChangeInStringParameterNamed(nameParamName, this.name)) {
final String newValue = command.stringValueOfParameterNamed(nameParamName);
actualChanges.put(nameParamName, newValue);
this.name = newValue;
}
final String descriptionParamName = "description";
if (command.isChangeInStringParameterNamed(descriptionParamName,
this.description)) {
final String newValue = command
.stringValueOfParameterNamed(descriptionParamName);
actualChanges.put(descriptionParamName, newValue);
this.description = newValue;
}
final String descriptionParamName = "description";
if (command.isChangeInStringParameterNamed(descriptionParamName, this.description)) {
final String newValue = command.stringValueOfParameterNamed(descriptionParamName);
actualChanges.put(descriptionParamName, newValue);
this.description = newValue;
}
Long existingFundId = null;
if (this.fund != null) {
existingFundId = this.fund.getId();
}
final String fundIdParamName = "fundId";
if (command.isChangeInLongParameterNamed(fundIdParamName,
existingFundId)) {
final Long newValue = command
.longValueOfParameterNamed(fundIdParamName);
actualChanges.put(fundIdParamName, newValue);
}
Long existingFundId = null;
if (this.fund != null) {
existingFundId = this.fund.getId();
}
final String fundIdParamName = "fundId";
if (command.isChangeInLongParameterNamed(fundIdParamName, existingFundId)) {
final Long newValue = command.longValueOfParameterNamed(fundIdParamName);
actualChanges.put(fundIdParamName, newValue);
}
Long existingStrategyId = null;
if (this.transactionProcessingStrategy != null) {
existingStrategyId = this.transactionProcessingStrategy.getId();
}
final String transactionProcessingStrategyParamName = "transactionProcessingStrategyId";
if (command.isChangeInLongParameterNamed(
transactionProcessingStrategyParamName, existingStrategyId)) {
final Long newValue = command
.longValueOfParameterNamed(transactionProcessingStrategyParamName);
actualChanges.put(transactionProcessingStrategyParamName, newValue);
}
Long existingStrategyId = null;
if (this.transactionProcessingStrategy != null) {
existingStrategyId = this.transactionProcessingStrategy.getId();
}
final String transactionProcessingStrategyParamName = "transactionProcessingStrategyId";
if (command.isChangeInLongParameterNamed(transactionProcessingStrategyParamName, existingStrategyId)) {
final Long newValue = command.longValueOfParameterNamed(transactionProcessingStrategyParamName);
actualChanges.put(transactionProcessingStrategyParamName, newValue);
}
final String chargesParamName = "charges";
if (command.hasParameter(chargesParamName)) {
JsonArray jsonArray = command
.arrayOfParameterNamed(chargesParamName);
if (jsonArray != null) {
final String chargesParamName = "charges";
if (command.hasParameter(chargesParamName)) {
JsonArray jsonArray = command.arrayOfParameterNamed(chargesParamName);
if (jsonArray != null) {
}
actualChanges.put(chargesParamName,
command.jsonFragment(chargesParamName));
}
}
actualChanges.put(chargesParamName, command.jsonFragment(chargesParamName));
}
return actualChanges;
}
return actualChanges;
}
public boolean isCashBasedAccountingEnabled() {
return AccountingRuleType.CASH_BASED.getValue().equals(
this.accountingRule);
}
public boolean isCashBasedAccountingEnabled() {
return AccountingRuleType.CASH_BASED.getValue().equals(this.accountingRule);
}
public boolean isAccrualBasedAccountingEnabled() {
return AccountingRuleType.ACCRUAL_BASED.getValue().equals(
this.accountingRule);
}
public boolean isAccrualBasedAccountingEnabled() {
return AccountingRuleType.ACCRUAL_BASED.getValue().equals(this.accountingRule);
}
public Money getPrincipalAmount() {
return this.loanProductRelatedDetail.getPrincipal();
}
public Money getPrincipalAmount() {
return this.loanProductRelatedDetail.getPrincipal();
}
public Money getMinPrincipalAmount() {
return Money.of(this.loanProductRelatedDetail.getCurrency(), this
.loanProductMinMaxConstraints().getMinPrincipal());
}
public Money getMinPrincipalAmount() {
return Money.of(this.loanProductRelatedDetail.getCurrency(), this.loanProductMinMaxConstraints().getMinPrincipal());
}
public Money getMaxPrincipalAmount() {
return Money.of(this.loanProductRelatedDetail.getCurrency(), this
.loanProductMinMaxConstraints().getMaxPrincipal());
}
public Money getMaxPrincipalAmount() {
return Money.of(this.loanProductRelatedDetail.getCurrency(), this.loanProductMinMaxConstraints().getMaxPrincipal());
}
public BigDecimal getNominalInterestRatePerPeriod() {
return this.loanProductRelatedDetail.getNominalInterestRatePerPeriod();
}
public BigDecimal getNominalInterestRatePerPeriod() {
return this.loanProductRelatedDetail.getNominalInterestRatePerPeriod();
}
public BigDecimal getMinNominalInterestRatePerPeriod() {
return this.loanProductMinMaxConstraints()
.getMinNominalInterestRatePerPeriod();
}
public BigDecimal getMinNominalInterestRatePerPeriod() {
return this.loanProductMinMaxConstraints().getMinNominalInterestRatePerPeriod();
}
public BigDecimal getMaxNominalInterestRatePerPeriod() {
return this.loanProductMinMaxConstraints()
.getMaxNominalInterestRatePerPeriod();
}
public BigDecimal getMaxNominalInterestRatePerPeriod() {
return this.loanProductMinMaxConstraints().getMaxNominalInterestRatePerPeriod();
}
public Integer getNumberOfRepayments() {
return this.loanProductRelatedDetail().getNumberOfRepayments();
}
public Integer getNumberOfRepayments() {
return this.loanProductRelatedDetail().getNumberOfRepayments();
}
public Integer getMinNumberOfRepayments() {
return this.loanProductMinMaxConstraints().getMinNumberOfRepayments();
}
public Integer getMinNumberOfRepayments() {
return this.loanProductMinMaxConstraints().getMinNumberOfRepayments();
}
public Integer getMaxNumberOfRepayments() {
return this.loanProductMinMaxConstraints().getMaxNumberOfRepayments();
}
public Integer getMaxNumberOfRepayments() {
return this.loanProductMinMaxConstraints().getMaxNumberOfRepayments();
}
public LoanProductRelatedDetail loanProductRelatedDetail() {
return this.loanProductRelatedDetail;
}
public LoanProductRelatedDetail loanProductRelatedDetail() {
return this.loanProductRelatedDetail;
}
public LoanProductMinMaxConstraints loanProductMinMaxConstraints() {
// If all min and max fields are null then loanProductMinMaxConstraints
// initialising to null
// Reset LoanProductMinMaxConstraints with null values.
this.loanProductMinMaxConstraints = (this.loanProductMinMaxConstraints == null) ? new LoanProductMinMaxConstraints(
null, null, null, null, null, null)
: this.loanProductMinMaxConstraints;
return loanProductMinMaxConstraints;
}
public LoanProductMinMaxConstraints loanProductMinMaxConstraints() {
// If all min and max fields are null then loanProductMinMaxConstraints
// initialising to null
// Reset LoanProductMinMaxConstraints with null values.
this.loanProductMinMaxConstraints = (this.loanProductMinMaxConstraints == null) ? new LoanProductMinMaxConstraints(null, null,
null, null, null, null) : this.loanProductMinMaxConstraints;
return loanProductMinMaxConstraints;
}
}
@@ -16,6 +16,7 @@ public class SavingsApiConstants {
// general
public static final String localeParamName = "locale";
public static final String dateFormatParamName = "dateFormat";
public static final String monthDayFormatParamName = "monthDayFormat";
// savings product and account parameters
public static final String idParamName = "id";
@@ -41,16 +42,19 @@ public class SavingsApiConstants {
public static final String lockinPeriodFrequencyTypeParamName = "lockinPeriodFrequencyType";
public static final String withdrawalFeeAmountParamName = "withdrawalFeeAmount";
public static final String withdrawalFeeTypeParamName = "withdrawalFeeType";
public static final String annualFeeAmountParamName = "annualFeeAmount";
public static final String annualFeeOnMonthDayParamName = "annualFeeOnMonthDay";
// transaction parameters
public static final String transactionDateParamName = "transactionDate";
public static final String transactionAmountParamName = "transactionAmount";
public static final Set<String> SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
nameParamName, descriptionParamName, currencyCodeParamName, digitsAfterDecimalParamName, nominalAnnualInterestRateParamName,
interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName,
interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName,
lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName));
monthDayFormatParamName, nameParamName, descriptionParamName, currencyCodeParamName, digitsAfterDecimalParamName,
nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName,
interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName,
lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName,
annualFeeAmountParamName, annualFeeOnMonthDayParamName));
/**
* These parameters will match the class level parameters of
@@ -61,16 +65,18 @@ public class SavingsApiConstants {
nameParamName, descriptionParamName, "currency", digitsAfterDecimalParamName, nominalAnnualInterestRateParamName,
interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName,
interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName,
lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, "currencyOptions",
"interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions", "interestCalculationTypeOptions",
"interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions"));
lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, annualFeeAmountParamName,
annualFeeOnMonthDayParamName, "currencyOptions", "interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions",
"interestCalculationTypeOptions", "interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions",
"withdrawalFeeTypeOptions"));
public static final Set<String> SAVINGS_ACCOUNT_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
dateFormatParamName, accountNoParamName, externalIdParamName, clientIdParamName, groupIdParamName, productIdParamName,
activeParamName, activationDateParamName, nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName,
interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName,
minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName,
withdrawalFeeAmountParamName, withdrawalFeeTypeParamName));
dateFormatParamName, monthDayFormatParamName, accountNoParamName, externalIdParamName, clientIdParamName, groupIdParamName,
productIdParamName, activeParamName, activationDateParamName, nominalAnnualInterestRateParamName,
interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName,
interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName,
lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, annualFeeAmountParamName,
annualFeeOnMonthDayParamName));
/**
* These parameters will match the class level parameters of
@@ -82,9 +88,10 @@ public class SavingsApiConstants {
groupIdParamName, "groupName", "savingsProductId", "savingsProductName", "currency", nominalAnnualInterestRateParamName,
interestCompoundingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName,
minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName,
withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, "summary", "transactions", "productOptions",
"interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions", "interestCalculationTypeOptions",
"interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions"));
withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, annualFeeAmountParamName, annualFeeOnMonthDayParamName, "summary",
"transactions", "productOptions", "interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions",
"interestCalculationTypeOptions", "interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions",
"withdrawalFeeTypeOptions"));
public static final Set<String> SAVINGS_ACCOUNT_TRANSACTION_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(
localeParamName, dateFormatParamName, transactionDateParamName, transactionAmountParamName));
@@ -9,6 +9,7 @@ import java.math.BigDecimal;
import java.util.Collection;
import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
@@ -39,6 +40,9 @@ public class SavingsAccountData {
private final EnumOptionData lockinPeriodFrequencyType;
private final BigDecimal withdrawalFeeAmount;
private final EnumOptionData withdrawalFeeType;
private final BigDecimal annualFeeAmount;
private final MonthDay annualFeeOnMonthDay;
private final LocalDate annualFeeNextDueDate;
// associations
private final SavingsAccountSummaryData summary;
@@ -68,7 +72,8 @@ public class SavingsAccountData {
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount,
final EnumOptionData withdrawalFeeType, final SavingsAccountSummaryData summary) {
final EnumOptionData withdrawalFeeType, final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay,
final LocalDate annualFeeNextDueDate, final SavingsAccountSummaryData summary) {
final Collection<SavingsProductData> productOptions = 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,
productId, productName, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, summary, transactions, productOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, annualFeeOnMonthDay,
annualFeeNextDueDate, summary, transactions, productOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
}
public static SavingsAccountData withTemplateOptions(final SavingsAccountData account,
@@ -101,9 +107,10 @@ public class SavingsAccountData {
account.savingsProductName, account.currency, account.nominalAnnualInterestRate, account.interestCompoundingPeriodType,
account.interestPostingPeriodType, account.interestCalculationType, account.interestCalculationDaysInYearType,
account.minRequiredOpeningBalance, account.lockinPeriodFrequency, account.lockinPeriodFrequencyType,
account.withdrawalFeeAmount, account.withdrawalFeeType, account.summary, transactions, productOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
account.withdrawalFeeAmount, account.withdrawalFeeType, account.annualFeeAmount, account.annualFeeOnMonthDay,
account.annualFeeNextDueDate, account.summary, transactions, productOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
}
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 BigDecimal withdrawalFeeAmount = null;
final EnumOptionData withdrawalFeeType = null;
final BigDecimal annualFeeAmount = null;
final MonthDay annualFeeOnMonthDay = null;
final LocalDate annualFeeNextDueDate = null;
final SavingsAccountSummaryData summary = 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,
productId, productName, currency, nominalAnnualInterestRate, interestPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, summary, transactions, productOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, annualFeeOnMonthDay,
annualFeeNextDueDate, summary, transactions, productOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
}
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 BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency,
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 Collection<SavingsProductData> productOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
@@ -182,6 +194,9 @@ public class SavingsAccountData {
this.lockinPeriodFrequencyType = lockinPeriodFrequencyType;
this.withdrawalFeeAmount = withdrawalFeeAmount;
this.withdrawalFeeType = withdrawalFeeType;
this.annualFeeAmount = annualFeeAmount;
this.annualFeeOnMonthDay = annualFeeOnMonthDay;
this.annualFeeNextDueDate = annualFeeNextDueDate;
this.summary = summary;
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.activationDateParamName;
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.externalIdParamName;
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.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
@@ -173,6 +176,16 @@ public class SavingsAccountDataValidator {
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);
}
@@ -316,15 +329,21 @@ public class SavingsAccountDataValidator {
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);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
//
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
}
@@ -19,17 +19,19 @@ public class SavingsAccountSummaryData {
private final BigDecimal totalDeposits;
private final BigDecimal totalWithdrawals;
private final BigDecimal totalWithdrawalFees;
private final BigDecimal totalAnnualFees;
private final BigDecimal totalInterestEarned;
private final BigDecimal totalInterestPosted;
private final BigDecimal accountBalance;
public SavingsAccountSummaryData(final CurrencyData currency, final BigDecimal totalDeposits, final BigDecimal totalWithdrawals,
final BigDecimal totalWithdrawalFees, final BigDecimal totalInterestEarned, final BigDecimal totalInterestPosted,
final BigDecimal accountBalance) {
final BigDecimal totalWithdrawalFees, final BigDecimal totalAnnualFees, final BigDecimal totalInterestEarned,
final BigDecimal totalInterestPosted, final BigDecimal accountBalance) {
this.currency = currency;
this.totalDeposits = totalDeposits;
this.totalWithdrawals = totalWithdrawals;
this.totalWithdrawalFees = totalWithdrawalFees;
this.totalAnnualFees = totalAnnualFees;
this.totalInterestEarned = totalInterestEarned;
this.totalInterestPosted = totalInterestPosted;
this.accountBalance = accountBalance;
@@ -8,6 +8,7 @@ package org.mifosplatform.portfolio.savings.data;
import java.math.BigDecimal;
import java.util.Collection;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
@@ -30,6 +31,8 @@ public class SavingsProductData {
private final EnumOptionData lockinPeriodFrequencyType;
private final BigDecimal withdrawalFeeAmount;
private final EnumOptionData withdrawalFeeType;
private final BigDecimal annualFeeAmount;
private final MonthDay annualFeeOnMonthDay;
// template
@SuppressWarnings("unused")
@@ -65,12 +68,15 @@ public class SavingsProductData {
final EnumOptionData lockinPeriodFrequencyType = null;
final BigDecimal withdrawalFeeAmount = null;
final EnumOptionData withdrawalFeeType = null;
final BigDecimal annualFeeAmount = null;
final MonthDay annualFeeMonthDay = null;
return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, currencyOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount,
annualFeeMonthDay, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions);
}
/**
@@ -89,9 +95,9 @@ public class SavingsProductData {
existingProduct.interestPostingPeriodType, existingProduct.interestCalculationType,
existingProduct.interestCalculationDaysInYearType, existingProduct.minRequiredOpeningBalance,
existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType, existingProduct.withdrawalFeeAmount,
existingProduct.withdrawalFeeType, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions);
existingProduct.withdrawalFeeType, existingProduct.annualFeeAmount, existingProduct.annualFeeOnMonthDay, currencyOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
}
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 interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
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<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
@@ -111,9 +117,10 @@ public class SavingsProductData {
return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, currencyOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount,
annualFeeOnMonthDay, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions);
}
public static SavingsProductData lookup(final Long id, final String name) {
@@ -130,6 +137,8 @@ public class SavingsProductData {
final EnumOptionData lockinPeriodFrequencyType = null;
final BigDecimal withdrawalFeeAmount = null;
final EnumOptionData withdrawalFeeType = null;
final BigDecimal annualFeeAmount = null;
final MonthDay annualFeeMonthDay = null;
final Collection<CurrencyData> currencyOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
@@ -141,19 +150,19 @@ public class SavingsProductData {
return new SavingsProductData(id, name, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, currencyOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount,
annualFeeMonthDay, currencyOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions);
}
private SavingsProductData(final Long id, final String name, final String description, final CurrencyData currency,
final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestCompoundingPeriodType,
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType,
final BigDecimal withdrawalFeeAmount, final EnumOptionData withdrawalFeeType,
final Collection<CurrencyData> currencyOptions,
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount,
final EnumOptionData withdrawalFeeType, final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay,
final Collection<CurrencyData> currencyOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
@@ -172,6 +181,8 @@ public class SavingsProductData {
this.lockinPeriodFrequencyType = lockinPeriodFrequencyType;
this.withdrawalFeeAmount = withdrawalFeeAmount;
this.withdrawalFeeType = withdrawalFeeType;
this.annualFeeAmount = annualFeeAmount;
this.annualFeeOnMonthDay = annualFeeOnMonthDay;
this.currencyOptions = currencyOptions;
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_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.descriptionParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.digitsAfterDecimalParamName;
@@ -24,6 +26,7 @@ import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
@@ -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);
}
@@ -222,6 +235,16 @@ public class SavingsProductDataValidator {
.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);
}
@@ -5,6 +5,12 @@
*/
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.MathContext;
import java.math.RoundingMode;
@@ -34,6 +40,7 @@ import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
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)
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
private SavingsAccountSummary summary;
@@ -165,13 +185,15 @@ public class SavingsAccount extends AbstractPersistable<Long> {
final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
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 LocalDate activationDate = null;
return new SavingsAccount(client, group, product, accountNo, externalId, status, activationDate, interestRate,
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,
@@ -180,7 +202,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
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.group = group;
this.product = product;
@@ -210,9 +233,27 @@ public class SavingsAccount extends AbstractPersistable<Long> {
if (withdrawalFeeType != null) {
this.withdrawalFeeType = withdrawalFeeType.getValue();
}
this.annualFeeAmount = annualFeeAmount;
if (annualFeeOnMonthDay != null) {
this.annualFeeOnMonth = annualFeeOnMonthDay.getMonthOfYear();
this.annualFeeOnDay = annualFeeOnMonthDay.getDayOfMonth();
updateAnnualFeeNextDueDate();
}
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
* 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);
final LocalDate today = DateUtils.getLocalDateOfTenant();
@@ -842,6 +887,71 @@ public class SavingsAccount extends AbstractPersistable<Long> {
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() {
return this.withdrawalFeeType != null;
}
@@ -942,6 +1052,48 @@ public class SavingsAccount extends AbstractPersistable<Long> {
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) {
@@ -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.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.interestCalculationTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
@@ -22,6 +24,7 @@ import java.math.BigDecimal;
import java.util.Locale;
import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
@@ -161,7 +164,7 @@ public class SavingsAccountAssembler {
} else {
withdrawalFeeAmount = product.withdrawalFeeAmount();
}
SavingsWithdrawalFeesType withdrawalFeeType = null;
if (command.parameterExists(withdrawalFeeAmountParamName)) {
final Integer withdrawalFeeTypeValue = command.integerValueOfParameterNamed(withdrawalFeeTypeParamName);
@@ -172,10 +175,24 @@ public class SavingsAccountAssembler {
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,
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType);
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType,
annualFeeAmount, monthDayOfAnnualFee);
account.setHelpers(this.savingsAccountTransactionSummaryWrapper);
if (active) {
@@ -32,6 +32,9 @@ public final class SavingsAccountSummary {
@Column(name = "total_withdrawal_fees_derived", scale = 6, precision = 19)
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)
private BigDecimal accountBalance = BigDecimal.ZERO;
@@ -46,9 +49,10 @@ public final class SavingsAccountSummary {
this.totalWithdrawals = wrapper.calculateTotalWithdrawals(currency, transactions);
this.totalInterestPosted = wrapper.calculateTotalInterestPosted(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)
.minus(this.totalWithdrawalFees).getAmount();
.minus(this.totalWithdrawalFees).minus(this.totalAnnualFees).getAmount();
}
public void updateFromInterestPeriodSummaries(final MonetaryCurrency currency,
@@ -88,6 +88,12 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
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,
final Money amount, final boolean isReversed) {
this.savingsAccount = savingsAccount;
@@ -125,6 +131,10 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
return SavingsAccountTransactionType.fromInt(this.typeOf).isWithdrawalFee() && isNotReversed();
}
public boolean isAnnualFee() {
return SavingsAccountTransactionType.fromInt(this.typeOf).isAnnualFee() && isNotReversed();
}
public boolean isNotReversed() {
return !isReversed();
}
@@ -53,4 +53,14 @@ public final class SavingsAccountTransactionSummaryWrapper {
}
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"), //
WITHDRAWAL(2, "savingsAccountTransactionType.withdrawal"), //
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 String code;
@@ -51,6 +52,9 @@ public enum SavingsAccountTransactionType {
case 4:
loanTransactionType = SavingsAccountTransactionType.WITHDRAWAL_FEE;
break;
case 5:
loanTransactionType = SavingsAccountTransactionType.ANNUAL_FEE;
break;
}
return loanTransactionType;
}
@@ -70,4 +74,8 @@ public enum SavingsAccountTransactionType {
public boolean isWithdrawalFee() {
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;
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.descriptionParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.digitsAfterDecimalParamName;
@@ -30,6 +32,7 @@ import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Table;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.exception.GeneralPlatformDomainRuleException;
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
* client's account earn interest.
*
*
* A value from the {@link SavingsCompoundingInterestPeriodType}
* enumeration.
*/
@@ -96,16 +99,26 @@ public class SavingsProduct extends AbstractPersistable<Long> {
@Column(name = "withdrawal_fee_type_enum", nullable = true)
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,
final BigDecimal interestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final 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,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType);
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, annualFeeOnMonthDay);
}
protected SavingsProduct() {
@@ -118,7 +131,8 @@ public class SavingsProduct extends AbstractPersistable<Long> {
final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
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.description = description;
@@ -143,9 +157,15 @@ public class SavingsProduct extends AbstractPersistable<Long> {
if (withdrawalFeeType != null) {
this.withdrawalFeeType = withdrawalFeeType.getValue();
}
this.annualFeeAmount = annualFeeAmount;
if (annualFeeOnMonthDay != null) {
this.annualFeeOnMonth = annualFeeOnMonthDay.getMonthOfYear();
this.annualFeeOnDay = annualFeeOnMonthDay.getDayOfMonth();
}
validateLockinDetails();
validateWithdrawalFeeDetails();
validateAnnualFeeDetails();
}
public MonetaryCurrency currency() {
@@ -200,6 +220,18 @@ public class SavingsProduct extends AbstractPersistable<Long> {
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) {
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();
}
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();
validateWithdrawalFeeDetails();
validateAnnualFeeDetails();
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() {
if (isInvalidConfigurationOfWithdrawalFeeSettings()) {
Object[] defaultUserMessageArgs = new Object[] { withdrawalFeeAmountParamName };
@@ -330,4 +401,4 @@ public class SavingsProduct extends AbstractPersistable<Long> {
return (this.lockinPeriodFrequency == null && this.lockinPeriodFrequencyType != null)
|| (this.lockinPeriodFrequencyType == null && this.lockinPeriodFrequency != null);
}
}
}
@@ -1,5 +1,7 @@
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.descriptionParamName;
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 org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.springframework.stereotype.Component;
@@ -76,8 +79,12 @@ public class SavingsProductAssembler {
withdrawalFeeType = SavingsWithdrawalFeesType.fromInt(withdrawalFeeTypeValue);
}
final BigDecimal annualFeeAmount = command.bigDecimalValueOfParameterNamed(annualFeeAmountParamName);
final MonthDay monthDayOfAnnualFee = command.extractMonthDayNamed(annualFeeOnMonthDayParamName);
return SavingsProduct.createNew(name, description, currency, interestRate, interestCompoundingPeriodType,
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 org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.DateUtils;
@@ -114,9 +115,14 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
sqlBuilder.append("sa.lockin_period_frequency_enum as lockinPeriodFrequencyType, ");
sqlBuilder.append("sa.withdrawal_fee_amount as withdrawalFeeAmount,");
sqlBuilder.append("sa.withdrawal_fee_type_enum as withdrawalFeeTypeEnum, ");
sqlBuilder.append("sa.annual_fee_amount as annualFeeAmount,");
sqlBuilder.append("sa.annual_fee_on_month as annualFeeOnMonth, ");
sqlBuilder.append("sa.annual_fee_on_day as annualFeeOnDay, ");
sqlBuilder.append("sa.annual_fee_next_due_date as annualFeeNextDueDate, ");
sqlBuilder.append("sa.total_deposits_derived as totalDeposits, ");
sqlBuilder.append("sa.total_withdrawals_derived as totalWithdrawals, ");
sqlBuilder.append("sa.total_withdrawal_fees_derived as totalWithdrawalFees, ");
sqlBuilder.append("sa.total_annual_fees_derived as totalAnnualFees, ");
sqlBuilder.append("sa.total_interest_earned_derived as totalInterestEarned, ");
sqlBuilder.append("sa.total_interest_posted_derived as totalInterestPosted, ");
sqlBuilder.append("sa.account_balance_derived as accountBalance ");
@@ -195,21 +201,34 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
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 totalWithdrawals = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalWithdrawals");
final BigDecimal totalWithdrawalFees = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalWithdrawalFees");
final BigDecimal totalAnnualFees = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalAnnualFees");
final BigDecimal totalInterestEarned = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalInterestEarned");
final BigDecimal totalInterestPosted = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalInterestPosted");
final BigDecimal accountBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "accountBalance");
final SavingsAccountSummaryData summary = new SavingsAccountSummaryData(currency, totalDeposits, totalWithdrawals,
totalWithdrawalFees, totalInterestEarned, totalInterestPosted, accountBalance);
totalWithdrawalFees, totalAnnualFees, totalInterestEarned, totalInterestPosted, accountBalance);
return SavingsAccountData.instance(id, accountNo, externalId, status, activationDate, groupId, groupName, clientId, clientName,
productId, productName, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, 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_enum as lockinPeriodFrequencyType, ");
sqlBuilder.append("sp.withdrawal_fee_amount as withdrawalFeeAmount,");
sqlBuilder.append("sp.withdrawal_fee_type_enum as withdrawalFeeTypeEnum ");
sqlBuilder.append("sp.withdrawal_fee_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("join m_currency curr on curr.code = sp.currency_code ");
@@ -484,6 +506,15 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
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;
String clientName = null;
if (client != null) {
@@ -500,11 +531,14 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final SavingsAccountStatusEnumData status = null;
final LocalDate activationDate = null;
final LocalDate annualFeeNextDueDate = null;
final SavingsAccountSummaryData summary = null;
return SavingsAccountData.instance(null, null, null, status, activationDate, groupId, groupName, clientId, clientName,
productId, productName, currency, nominalAnnualIterestRate, interestCompoundingPeriodType, interestPostingPeriodType,
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:
optionData = new SavingsAccountTransactionEnumData(SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue().longValue(),
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;
}
return optionData;
@@ -10,6 +10,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
@@ -85,7 +86,10 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
sqlBuilder.append("sp.lockin_period_frequency as lockinPeriodFrequency,");
sqlBuilder.append("sp.lockin_period_frequency_enum as lockinPeriodFrequencyType, ");
sqlBuilder.append("sp.withdrawal_fee_amount as withdrawalFeeAmount,");
sqlBuilder.append("sp.withdrawal_fee_type_enum as withdrawalFeeTypeEnum ");
sqlBuilder.append("sp.withdrawal_fee_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("join m_currency curr on curr.code = sp.currency_code ");
@@ -147,9 +151,19 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
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,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType);
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount,
annualFeeOnMonthDay);
}
}