Move withdrawal and annual fee charges to charges workflow

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