MIFOSX-250: add support for daily balance and average daily balance methods. support for daily,weekly,monthly interest periods.
This commit is contained in:
@@ -723,6 +723,9 @@ CREATE TABLE `m_savings_product` (
|
||||
`currency_digits` smallint(5) NOT NULL,
|
||||
`nominal_interest_rate_per_period` decimal(19,6) NOT NULL,
|
||||
`nominal_interest_rate_period_frequency_enum` smallint(5) NOT NULL,
|
||||
`interest_period_enum` SMALLINT(5) NOT NULL,
|
||||
`interest_calculation_type_enum` SMALLINT(5) NOT NULL,
|
||||
`interest_calculation_days_in_year_type_enum` SMALLINT(5) NOT NULL,
|
||||
`min_required_opening_balance` decimal(19,6) DEFAULT NULL,
|
||||
`lockin_period_frequency` decimal(19,6) DEFAULT NULL,
|
||||
`lockin_period_frequency_enum` smallint(5) DEFAULT NULL,
|
||||
@@ -730,7 +733,6 @@ CREATE TABLE `m_savings_product` (
|
||||
UNIQUE KEY `sp_unq_name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
CREATE TABLE `m_savings_account` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
`account_no` varchar(20) NOT NULL,
|
||||
@@ -745,12 +747,16 @@ CREATE TABLE `m_savings_account` (
|
||||
`nominal_interest_rate_per_period` decimal(19,6) NOT NULL,
|
||||
`nominal_interest_rate_period_frequency_enum` smallint(5) NOT NULL,
|
||||
`annual_nominal_interest_rate` decimal(19,6) NOT NULL,
|
||||
`interest_period_enum` SMALLINT(5) NOT NULL,
|
||||
`interest_calculation_type_enum` SMALLINT(5) NOT NULL,
|
||||
`interest_calculation_days_in_year_type_enum` SMALLINT(5) NOT NULL,
|
||||
`min_required_opening_balance` decimal(19,6) DEFAULT NULL,
|
||||
`lockin_period_frequency` decimal(19,6) DEFAULT NULL,
|
||||
`lockin_period_frequency_enum` smallint(5) DEFAULT NULL,
|
||||
`lockedin_until_date_derived` DATE DEFAULT NULL,
|
||||
`total_deposits_derived` decimal(19,6) DEFAULT NULL,
|
||||
`total_withdrawals_derived` decimal(19,6) DEFAULT NULL,
|
||||
`total_interest_earned_derived` decimal(19,6) DEFAULT NULL,
|
||||
`total_interest_posted_derived` decimal(19,6) DEFAULT NULL,
|
||||
`account_balance_derived` decimal(19,6) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
@@ -771,6 +777,10 @@ CREATE TABLE `m_savings_account_transaction` (
|
||||
`transaction_date` date NOT NULL,
|
||||
`amount` decimal(19,6) NOT NULL,
|
||||
`is_reversed` tinyint(1) NOT NULL,
|
||||
`running_balance_derived` DECIMAL(19,6) NULL,
|
||||
`balance_number_of_days_derived` INT NULL,
|
||||
`balance_end_date_derived` DATE NULL,
|
||||
`cumulative_balance_derived` DECIMAL(19,6) NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `FKSAT0000000001` (`savings_account_id`),
|
||||
CONSTRAINT `FKSAT0000000001` FOREIGN KEY (`savings_account_id`) REFERENCES `m_savings_account` (`id`)
|
||||
|
||||
@@ -265,7 +265,9 @@ INSERT INTO `m_permission`
|
||||
('transaction_savings', 'WITHDRAWAL_SAVINGSACCOUNT', 'SAVINGSACCOUNT', 'WITHDRAWAL', '1'),
|
||||
('transaction_savings', 'WITHDRAWAL_SAVINGSACCOUNT_CHECKER', 'SAVINGSACCOUNT', 'WITHDRAWAL', '0'),
|
||||
('transaction_savings', 'ACTIVATE_SAVINGSACCOUNT', 'SAVINGSACCOUNT', 'ACTIVATE', '1'),
|
||||
('transaction_savings', 'ACTIVATE_SAVINGSACCOUNT_CHECKER', 'SAVINGSACCOUNT', 'ACTIVATE', '0');
|
||||
('transaction_savings', 'ACTIVATE_SAVINGSACCOUNT_CHECKER', 'SAVINGSACCOUNT', 'ACTIVATE', '0'),
|
||||
('transaction_savings', 'CALCULATEINTEREST_SAVINGSACCOUNT', 'SAVINGSACCOUNT', 'CALCULATEINTEREST', '1'),
|
||||
('transaction_savings', 'CALCULATEINTEREST_SAVINGSACCOUNT_CHECKER', 'SAVINGSACCOUNT', 'CALCULATEINTEREST', '0');
|
||||
|
||||
-- == accounting related permissions
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
ALTER TABLE `m_savings_product`
|
||||
ADD COLUMN `interest_period_enum` SMALLINT(5) NOT NULL AFTER `nominal_interest_rate_period_frequency_enum`,
|
||||
ADD COLUMN `interest_calculation_type_enum` SMALLINT(5) NOT NULL AFTER `interest_period_enum`;
|
||||
|
||||
|
||||
ALTER TABLE `m_savings_account`
|
||||
ADD COLUMN `interest_period_enum` SMALLINT(5) NOT NULL AFTER `annual_nominal_interest_rate`,
|
||||
ADD COLUMN `interest_calculation_type_enum` SMALLINT(5) NOT NULL AFTER `interest_period_enum`;
|
||||
|
||||
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
|
||||
VALUES
|
||||
('transaction_savings', 'CALCULATEINTEREST_SAVINGSACCOUNT', 'SAVINGSACCOUNT', 'CALCULATEINTEREST', '1'),
|
||||
('transaction_savings', 'CALCULATEINTEREST_SAVINGSACCOUNT_CHECKER', 'SAVINGSACCOUNT', 'CALCULATEINTEREST', '0');
|
||||
|
||||
|
||||
ALTER TABLE `m_savings_account_transaction`
|
||||
ADD COLUMN `running_balance_derived` DECIMAL(19,6) NULL AFTER `is_reversed`,
|
||||
ADD COLUMN `balance_number_of_days_derived` INT NULL AFTER `running_balance_derived`,
|
||||
ADD COLUMN `balance_end_date_derived` DATE NULL AFTER `balance_number_of_days_derived`,
|
||||
ADD COLUMN `cumulative_balance_derived` DECIMAL(19,6) NULL AFTER `balance_end_date_derived`;
|
||||
|
||||
ALTER TABLE `m_savings_account`
|
||||
ADD COLUMN `total_interest_earned_derived` DECIMAL(19,6) DEFAULT NULL AFTER `total_withdrawals_derived`;
|
||||
|
||||
|
||||
ALTER TABLE `m_savings_product`
|
||||
ADD COLUMN `interest_calculation_days_in_year_type_enum` SMALLINT(5) NOT NULL AFTER `interest_calculation_type_enum`;
|
||||
|
||||
ALTER TABLE `m_savings_account`
|
||||
ADD COLUMN `interest_calculation_days_in_year_type_enum` SMALLINT(5) NOT NULL AFTER `interest_calculation_type_enum`;
|
||||
@@ -446,6 +446,10 @@ public class CommandWrapper {
|
||||
return this.actionName.equalsIgnoreCase("WITHDRAWAL") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
|
||||
}
|
||||
|
||||
public boolean isSavingsAccountInterestCalculation() {
|
||||
return this.actionName.equalsIgnoreCase("CALCULATEINTEREST") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
|
||||
}
|
||||
|
||||
public boolean isCalendarResource() {
|
||||
return this.entityName.equalsIgnoreCase("CALENDAR");
|
||||
}
|
||||
|
||||
+9
@@ -713,6 +713,15 @@ public class CommandWrapperBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder savingsAccountInterestCalculation(final Long accountId) {
|
||||
this.actionName = "CALCULATEINTEREST";
|
||||
this.entityName = "SAVINGSACCOUNT";
|
||||
this.savingsId = accountId;
|
||||
this.entityId = null;
|
||||
this.href = "/savingsaccounts/" + accountId + "?command=calculateInterest";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createCalendar(final String supportedEntityType, final Long supportedEntityId) {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "CALENDAR";
|
||||
|
||||
+2
@@ -368,6 +368,8 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
||||
handler = applicationContext.getBean("withdrawSavingsAccountCommandHandler", NewCommandSourceHandler.class);
|
||||
} else if (wrapper.isSavingsAccountActivation()) {
|
||||
handler = applicationContext.getBean("activateSavingsAccountCommandHandler", NewCommandSourceHandler.class);
|
||||
} else if (wrapper.isSavingsAccountInterestCalculation()) {
|
||||
handler = applicationContext.getBean("calculateInterestSavingsAccountCommandHandler", NewCommandSourceHandler.class);
|
||||
} else {
|
||||
throw new UnsupportedCommandException(wrapper.commandName());
|
||||
}
|
||||
|
||||
+22
@@ -7,6 +7,7 @@ package org.mifosplatform.infrastructure.core.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.ParseException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import net.fortuna.ical4j.model.ValidationException;
|
||||
@@ -191,6 +192,27 @@ public class DataValidatorBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public DataValidatorBuilder inOfTheseValues(final Object... values) {
|
||||
if (value == null && ignoreNullValue) { return this; }
|
||||
|
||||
if (value != null) {
|
||||
List<Object> valuesList = Arrays.asList(values);
|
||||
|
||||
if (!valuesList.contains(this.value)) {
|
||||
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
|
||||
.append(".is.not.one.of.expected.enumerations");
|
||||
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(" must be one of [ ")
|
||||
.append(values).append(" ] ").append(".");
|
||||
|
||||
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
|
||||
defaultEnglishMessage.toString(), parameter, this.value, values);
|
||||
|
||||
dataValidationErrors.add(error);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public DataValidatorBuilder positiveAmount() {
|
||||
if (value == null && ignoreNullValue) { return this; }
|
||||
|
||||
|
||||
+9
@@ -141,6 +141,9 @@ public class SavingsAccountsApiResource {
|
||||
Collection<SavingsAccountTransactionData> transactions = null;
|
||||
Collection<SavingsProductData> productOptions = null;
|
||||
Collection<EnumOptionData> interestRatePeriodFrequencyTypeOptions = null;
|
||||
Collection<EnumOptionData> interestPeriodTypeOptions = null;
|
||||
Collection<EnumOptionData> interestCalculationTypeOptions = null;
|
||||
Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
|
||||
Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
|
||||
|
||||
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
|
||||
@@ -164,10 +167,13 @@ public class SavingsAccountsApiResource {
|
||||
if (settings.isTemplate()) {
|
||||
productOptions = this.savingsProductReadPlatformService.retrieveAllForLookup();
|
||||
interestRatePeriodFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveInterestRatePeriodFrequencyTypeOptions();
|
||||
interestPeriodTypeOptions = this.dropdownReadPlatformService.retrieveInterestPeriodTypeOptions();
|
||||
interestCalculationTypeOptions = this.dropdownReadPlatformService.retrieveInterestCalculationTypeOptions();
|
||||
lockinPeriodFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveLockinPeriodFrequencyTypeOptions();
|
||||
}
|
||||
|
||||
return SavingsAccountData.withTemplateOptions(savingsAccount, productOptions, interestRatePeriodFrequencyTypeOptions,
|
||||
interestPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
|
||||
lockinPeriodFrequencyTypeOptions, transactions);
|
||||
}
|
||||
|
||||
@@ -198,6 +204,9 @@ public class SavingsAccountsApiResource {
|
||||
if (is(commandParam, "activate")) {
|
||||
final CommandWrapper commandRequest = builder.savingsAccountActivation(accountId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "calculateInterest")) {
|
||||
final CommandWrapper commandRequest = builder.savingsAccountInterestCalculation(accountId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
|
||||
+12
-5
@@ -33,6 +33,9 @@ public class SavingsApiConstants {
|
||||
public static final String digitsAfterDecimalParamName = "digitsAfterDecimal";
|
||||
public static final String interestRateParamName = "interestRate";
|
||||
public static final String interestRatePeriodFrequencyTypeParamName = "interestRatePeriodFrequencyType";
|
||||
public static final String interestPeriodTypeParamName = "interestPeriodType";
|
||||
public static final String interestCalculationTypeParamName = "interestCalculationType";
|
||||
public static final String interestCalculationDaysInYearTypeParamName = "interestCalculationDaysInYearType";
|
||||
public static final String minRequiredOpeningBalanceParamName = "minRequiredOpeningBalance";
|
||||
public static final String lockinPeriodFrequencyParamName = "lockinPeriodFrequency";
|
||||
public static final String lockinPeriodFrequencyTypeParamName = "lockinPeriodFrequencyType";
|
||||
@@ -43,7 +46,8 @@ public class SavingsApiConstants {
|
||||
|
||||
public static final Set<String> SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
|
||||
nameParamName, descriptionParamName, currencyCodeParamName, digitsAfterDecimalParamName, interestRateParamName,
|
||||
interestRatePeriodFrequencyTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName,
|
||||
interestRatePeriodFrequencyTypeParamName, interestPeriodTypeParamName, interestCalculationTypeParamName,
|
||||
interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName,
|
||||
lockinPeriodFrequencyTypeParamName));
|
||||
|
||||
/**
|
||||
@@ -53,13 +57,15 @@ public class SavingsApiConstants {
|
||||
*/
|
||||
public static final Set<String> SAVINGS_PRODUCT_RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(idParamName,
|
||||
nameParamName, descriptionParamName, "currency", digitsAfterDecimalParamName, interestRateParamName,
|
||||
interestRatePeriodFrequencyTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName,
|
||||
interestRatePeriodFrequencyTypeParamName, interestPeriodTypeParamName, interestCalculationTypeParamName,
|
||||
interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName,
|
||||
lockinPeriodFrequencyTypeParamName, "currencyOptions", "interestRatePeriodFrequencyTypeOptions",
|
||||
"lockinPeriodFrequencyTypeOptions"));
|
||||
|
||||
public static final Set<String> SAVINGS_ACCOUNT_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
|
||||
dateFormatParamName, accountNoParamName, externalIdParamName, clientIdParamName, groupIdParamName, productIdParamName,
|
||||
activeParamName, activationDateParamName, interestRateParamName, interestRatePeriodFrequencyTypeParamName,
|
||||
interestPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName,
|
||||
minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName));
|
||||
|
||||
/**
|
||||
@@ -70,9 +76,10 @@ public class SavingsApiConstants {
|
||||
public static final Set<String> SAVINGS_ACCOUNT_RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(idParamName,
|
||||
accountNoParamName, externalIdParamName, statusParamName, activationDateParamName, clientIdParamName, "clientName",
|
||||
groupIdParamName, "groupName", "savingsProductId", "savingsProductName", "currency", interestRateParamName,
|
||||
interestRatePeriodFrequencyTypeParamName, "annualInterestRate", minRequiredOpeningBalanceParamName,
|
||||
lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, "summary", "transactions", "productOptions",
|
||||
"interestRatePeriodFrequencyTypeOptions", "lockinPeriodFrequencyTypeOptions"));
|
||||
interestRatePeriodFrequencyTypeParamName, "annualInterestRate", interestPeriodTypeParamName, interestCalculationTypeParamName,
|
||||
interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName,
|
||||
lockinPeriodFrequencyTypeParamName, "summary", "transactions", "productOptions", "interestRatePeriodFrequencyTypeOptions",
|
||||
"lockinPeriodFrequencyTypeOptions"));
|
||||
|
||||
public static final Set<String> SAVINGS_ACCOUNT_TRANSACTION_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(
|
||||
localeParamName, dateFormatParamName, transactionDateParamName, transactionAmountParamName));
|
||||
|
||||
+28
-5
@@ -30,8 +30,11 @@ import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSeria
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.organisation.monetary.service.CurrencyReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsProductData;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationDaysInYearType;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationType;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsInterestPeriodType;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsPeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.savings.service.SavingsDropdownReadPlatformService;
|
||||
import org.mifosplatform.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.mifosplatform.portfolio.savings.service.SavingsProductReadPlatformService;
|
||||
@@ -144,21 +147,41 @@ public class SavingsProductsApiResource {
|
||||
private SavingsProductData handleTemplateRelatedData(final SavingsProductData savingsProduct) {
|
||||
|
||||
final EnumOptionData interestRatePeriodFrequencyType = SavingsEnumerations
|
||||
.interestRatePeriodFrequencyType(PeriodFrequencyType.YEARS);
|
||||
.interestRatePeriodFrequencyType(SavingsPeriodFrequencyType.YEARS);
|
||||
|
||||
final EnumOptionData interestPeriodType = SavingsEnumerations.interestPeriodType(SavingsInterestPeriodType.MONTHLY);
|
||||
|
||||
final EnumOptionData interestCalculationType = SavingsEnumerations
|
||||
.interestCalculationType(SavingsInterestCalculationType.AVERAGE_DAILY_BALANCE);
|
||||
|
||||
final EnumOptionData interestCalculationDaysInYearType = SavingsEnumerations
|
||||
.interestCalculationDaysInYearType(SavingsInterestCalculationDaysInYearType.DAYS_365);
|
||||
|
||||
final Collection<CurrencyData> currencyOptions = this.currencyReadPlatformService.retrieveAllowedCurrencies();
|
||||
final Collection<EnumOptionData> interestRatePeriodFrequencyTypeOptions = this.dropdownReadPlatformService
|
||||
.retrieveInterestRatePeriodFrequencyTypeOptions();
|
||||
|
||||
final Collection<EnumOptionData> interestPeriodTypeOptions = this.dropdownReadPlatformService.retrieveInterestPeriodTypeOptions();
|
||||
|
||||
final Collection<EnumOptionData> interestCalculationTypeOptions = this.dropdownReadPlatformService
|
||||
.retrieveInterestCalculationTypeOptions();
|
||||
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = this.dropdownReadPlatformService
|
||||
.retrieveInterestCalculationDaysInYearTypeOptions();
|
||||
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = this.dropdownReadPlatformService
|
||||
.retrieveLockinPeriodFrequencyTypeOptions();
|
||||
|
||||
SavingsProductData savingsProductToReturn = null;
|
||||
if (savingsProduct != null) {
|
||||
savingsProductToReturn = SavingsProductData.withTemplate(savingsProduct, currencyOptions,
|
||||
interestRatePeriodFrequencyTypeOptions, lockinPeriodFrequencyTypeOptions);
|
||||
interestRatePeriodFrequencyTypeOptions, interestPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions);
|
||||
} else {
|
||||
savingsProductToReturn = SavingsProductData.template(CurrencyData.blank(), interestRatePeriodFrequencyType, currencyOptions,
|
||||
interestRatePeriodFrequencyTypeOptions, lockinPeriodFrequencyTypeOptions);
|
||||
savingsProductToReturn = SavingsProductData.template(CurrencyData.blank(), interestRatePeriodFrequencyType, interestPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, currencyOptions, interestRatePeriodFrequencyTypeOptions,
|
||||
interestPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
|
||||
lockinPeriodFrequencyTypeOptions);
|
||||
}
|
||||
|
||||
return savingsProductToReturn;
|
||||
|
||||
+42
-11
@@ -33,6 +33,9 @@ public class SavingsAccountData {
|
||||
private final BigDecimal interestRate;
|
||||
private final EnumOptionData interestRatePeriodFrequencyType;
|
||||
private final BigDecimal annualInterestRate;
|
||||
private final EnumOptionData interestPeriodType;
|
||||
private final EnumOptionData interestCalculationType;
|
||||
private final EnumOptionData interestCalculationDaysInYearType;
|
||||
private final BigDecimal minRequiredOpeningBalance;
|
||||
private final Integer lockinPeriodFrequency;
|
||||
private final EnumOptionData lockinPeriodFrequencyType;
|
||||
@@ -44,35 +47,47 @@ public class SavingsAccountData {
|
||||
// template
|
||||
private final Collection<SavingsProductData> productOptions;
|
||||
private final Collection<EnumOptionData> interestRatePeriodFrequencyTypeOptions;
|
||||
private final Collection<EnumOptionData> interestPeriodTypeOptions;
|
||||
private final Collection<EnumOptionData> interestCalculationTypeOptions;
|
||||
private final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions;
|
||||
private final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions;
|
||||
|
||||
public static SavingsAccountData instance(final Long id, final String accountNo, final String externalId,
|
||||
final SavingsAccountStatusEnumData status, final LocalDate activationDate, final Long groupId, final String groupName,
|
||||
final Long clientId, final String clientName, final Long productId, final String productName, final CurrencyData currency,
|
||||
final BigDecimal interestRate, final EnumOptionData interestRatePeriodFrequencyType, final BigDecimal annualInterestRate,
|
||||
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency,
|
||||
final EnumOptionData lockinPeriodFrequencyType, final SavingsAccountSummaryData summary) {
|
||||
final EnumOptionData interestPeriodType, final EnumOptionData interestCalculationType,
|
||||
final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
|
||||
final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final SavingsAccountSummaryData summary) {
|
||||
|
||||
final Collection<SavingsProductData> productOptions = null;
|
||||
final Collection<EnumOptionData> interestRatePeriodFrequencyTypeOptions = null;
|
||||
final Collection<EnumOptionData> interestPeriodTypeOptions = null;
|
||||
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
|
||||
final Collection<SavingsAccountTransactionData> transactions = null;
|
||||
|
||||
return new SavingsAccountData(id, accountNo, externalId, status, activationDate, groupId, groupName, clientId, clientName,
|
||||
productId, productName, currency, interestRate, interestRatePeriodFrequencyType, annualInterestRate,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, summary, productOptions,
|
||||
interestRatePeriodFrequencyTypeOptions, lockinPeriodFrequencyTypeOptions, transactions);
|
||||
productId, productName, currency, interestRate, interestRatePeriodFrequencyType, annualInterestRate, interestPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType, summary, productOptions, interestRatePeriodFrequencyTypeOptions, interestPeriodTypeOptions,
|
||||
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, transactions);
|
||||
}
|
||||
|
||||
public static SavingsAccountData withTemplateOptions(final SavingsAccountData account,
|
||||
final Collection<SavingsProductData> productOptions, final Collection<EnumOptionData> interestRatePeriodFrequencyTypeOptions,
|
||||
final Collection<EnumOptionData> interestPeriodTypeOptions, final Collection<EnumOptionData> interestCalculationTypeOptions,
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<SavingsAccountTransactionData> transactions) {
|
||||
|
||||
return new SavingsAccountData(account.id, account.accountNo, account.externalId, account.status, account.activationDate,
|
||||
account.groupId, account.groupName, account.clientId, account.clientName, account.savingsProductId,
|
||||
account.savingsProductName, account.currency, account.interestRate, account.interestRatePeriodFrequencyType,
|
||||
account.annualInterestRate, account.minRequiredOpeningBalance, account.lockinPeriodFrequency,
|
||||
account.annualInterestRate, account.interestPeriodType, account.interestCalculationType,
|
||||
account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency,
|
||||
account.lockinPeriodFrequencyType, account.summary, productOptions, interestRatePeriodFrequencyTypeOptions,
|
||||
interestPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
|
||||
lockinPeriodFrequencyTypeOptions, transactions);
|
||||
}
|
||||
|
||||
@@ -90,6 +105,9 @@ public class SavingsAccountData {
|
||||
final BigDecimal interestRate = null;
|
||||
final EnumOptionData interestRatePeriodFrequencyType = null;
|
||||
final BigDecimal annualInterestRate = null;
|
||||
final EnumOptionData interestPeriodType = null;
|
||||
final EnumOptionData interestCalculationType = null;
|
||||
final EnumOptionData interestCalculationDaysInYearType = null;
|
||||
final BigDecimal minRequiredOpeningBalance = null;
|
||||
final Integer lockinPeriodFrequency = null;
|
||||
final EnumOptionData lockinPeriodFrequencyType = null;
|
||||
@@ -97,22 +115,29 @@ public class SavingsAccountData {
|
||||
final Collection<SavingsProductData> productOptions = null;
|
||||
final Collection<CurrencyData> currencyOptions = null;
|
||||
final Collection<EnumOptionData> interestRatePeriodFrequencyTypeOptions = null;
|
||||
final Collection<EnumOptionData> interestPeriodTypeOptions = null;
|
||||
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
|
||||
final Collection<SavingsAccountTransactionData> transactions = null;
|
||||
|
||||
return new SavingsAccountData(id, accountNo, externalId, status, activationDate, groupId, groupName, clientId, clientName,
|
||||
productId, productName, currency, interestRate, interestRatePeriodFrequencyType, annualInterestRate,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, summary, productOptions,
|
||||
interestRatePeriodFrequencyTypeOptions, lockinPeriodFrequencyTypeOptions, transactions);
|
||||
productId, productName, currency, interestRate, interestRatePeriodFrequencyType, annualInterestRate, interestPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType, summary, productOptions, interestRatePeriodFrequencyTypeOptions, interestPeriodTypeOptions,
|
||||
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, transactions);
|
||||
}
|
||||
|
||||
private SavingsAccountData(final Long id, final String accountNo, final String externalId, final SavingsAccountStatusEnumData status,
|
||||
final LocalDate activationDate, final Long groupId, final String groupName, final Long clientId, final String clientName,
|
||||
final Long productId, final String productName, final CurrencyData currency, final BigDecimal interestRate,
|
||||
final EnumOptionData interestRatePeriodFrequencyType, final BigDecimal annualInterestRate,
|
||||
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency,
|
||||
final EnumOptionData lockinPeriodFrequencyType, final SavingsAccountSummaryData summary,
|
||||
final EnumOptionData interestPeriodType, final EnumOptionData interestCalculationType,
|
||||
final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
|
||||
final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final SavingsAccountSummaryData summary,
|
||||
final Collection<SavingsProductData> productOptions, final Collection<EnumOptionData> interestRatePeriodFrequencyTypeOptions,
|
||||
final Collection<EnumOptionData> interestPeriodTypeOptions, final Collection<EnumOptionData> interestCalculationTypeOptions,
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<SavingsAccountTransactionData> transactions) {
|
||||
this.id = id;
|
||||
this.accountNo = accountNo;
|
||||
@@ -129,6 +154,9 @@ public class SavingsAccountData {
|
||||
this.interestRate = interestRate;
|
||||
this.interestRatePeriodFrequencyType = interestRatePeriodFrequencyType;
|
||||
this.annualInterestRate = annualInterestRate;
|
||||
this.interestPeriodType = interestPeriodType;
|
||||
this.interestCalculationType = interestCalculationType;
|
||||
this.interestCalculationDaysInYearType = interestCalculationDaysInYearType;
|
||||
this.minRequiredOpeningBalance = minRequiredOpeningBalance;
|
||||
this.lockinPeriodFrequency = lockinPeriodFrequency;
|
||||
this.lockinPeriodFrequencyType = lockinPeriodFrequencyType;
|
||||
@@ -136,6 +164,9 @@ public class SavingsAccountData {
|
||||
|
||||
this.productOptions = productOptions;
|
||||
this.interestRatePeriodFrequencyTypeOptions = interestRatePeriodFrequencyTypeOptions;
|
||||
this.interestPeriodTypeOptions = interestPeriodTypeOptions;
|
||||
this.interestCalculationTypeOptions = interestCalculationTypeOptions;
|
||||
this.interestCalculationDaysInYearTypeOptions = interestCalculationDaysInYearTypeOptions;
|
||||
this.lockinPeriodFrequencyTypeOptions = lockinPeriodFrequencyTypeOptions;
|
||||
this.transactions = transactions;
|
||||
}
|
||||
|
||||
+33
@@ -6,6 +6,9 @@ import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.active
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.clientIdParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.externalIdParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.groupIdParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestPeriodTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestRateParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestRatePeriodFrequencyTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyParamName;
|
||||
@@ -103,6 +106,17 @@ public class SavingsAccountDataValidator {
|
||||
.inMinMaxRange(1, 3);
|
||||
}
|
||||
|
||||
final Integer interestPeriodType = fromApiJsonHelper.extractIntegerSansLocaleNamed(interestPeriodTypeParamName, element);
|
||||
baseDataValidator.reset().parameter(interestPeriodTypeParamName).value(interestPeriodType).notNull().inMinMaxRange(1, 7);
|
||||
|
||||
final Integer interestCalculationType = fromApiJsonHelper.extractIntegerSansLocaleNamed(interestCalculationTypeParamName, element);
|
||||
baseDataValidator.reset().parameter(interestCalculationTypeParamName).value(interestCalculationType).notNull().inMinMaxRange(1, 2);
|
||||
|
||||
final Integer interestCalculationDaysInYearType = fromApiJsonHelper.extractIntegerSansLocaleNamed(
|
||||
interestCalculationDaysInYearTypeParamName, element);
|
||||
baseDataValidator.reset().parameter(interestCalculationDaysInYearTypeParamName).value(interestCalculationDaysInYearType).notNull()
|
||||
.inOfTheseValues(360, 365);
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) {
|
||||
final BigDecimal minOpeningBalance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minRequiredOpeningBalanceParamName,
|
||||
element);
|
||||
@@ -213,6 +227,25 @@ public class SavingsAccountDataValidator {
|
||||
.inMinMaxRange(1, 3);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(interestPeriodTypeParamName, element)) {
|
||||
final Integer interestPeriodType = fromApiJsonHelper.extractIntegerSansLocaleNamed(interestPeriodTypeParamName, element);
|
||||
baseDataValidator.reset().parameter(interestPeriodTypeParamName).value(interestPeriodType).notNull().inMinMaxRange(1, 7);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(interestCalculationTypeParamName, element)) {
|
||||
final Integer interestCalculationType = fromApiJsonHelper.extractIntegerSansLocaleNamed(interestCalculationTypeParamName,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(interestCalculationTypeParamName).value(interestCalculationType).notNull()
|
||||
.inMinMaxRange(1, 2);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(interestCalculationDaysInYearTypeParamName, element)) {
|
||||
final Integer interestCalculationDaysInYearType = fromApiJsonHelper.extractIntegerSansLocaleNamed(
|
||||
interestCalculationDaysInYearTypeParamName, element);
|
||||
baseDataValidator.reset().parameter(interestCalculationDaysInYearTypeParamName).value(interestCalculationDaysInYearType)
|
||||
.notNull().inOfTheseValues(360, 365);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) {
|
||||
final BigDecimal minOpeningBalance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minRequiredOpeningBalanceParamName,
|
||||
element);
|
||||
|
||||
+3
-1
@@ -18,14 +18,16 @@ public class SavingsAccountSummaryData {
|
||||
private final CurrencyData currency;
|
||||
private final BigDecimal totalDeposits;
|
||||
private final BigDecimal totalWithdrawals;
|
||||
private final BigDecimal totalInterestEarned;
|
||||
private final BigDecimal totalInterestPosted;
|
||||
private final BigDecimal accountBalance;
|
||||
|
||||
public SavingsAccountSummaryData(final CurrencyData currency, final BigDecimal totalDeposits, final BigDecimal totalWithdrawals,
|
||||
final BigDecimal totalInterestPosted, final BigDecimal accountBalance) {
|
||||
final BigDecimal totalInterestEarned, final BigDecimal totalInterestPosted, final BigDecimal accountBalance) {
|
||||
this.currency = currency;
|
||||
this.totalDeposits = totalDeposits;
|
||||
this.totalWithdrawals = totalWithdrawals;
|
||||
this.totalInterestEarned = totalInterestEarned;
|
||||
this.totalInterestPosted = totalInterestPosted;
|
||||
this.accountBalance = accountBalance;
|
||||
}
|
||||
|
||||
+58
-17
@@ -14,7 +14,6 @@ import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
/**
|
||||
* Immutable data object represent a savings product.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class SavingsProductData {
|
||||
|
||||
private final Long id;
|
||||
@@ -23,31 +22,47 @@ public class SavingsProductData {
|
||||
private final CurrencyData currency;
|
||||
private final BigDecimal interestRate;
|
||||
private final EnumOptionData interestRatePeriodFrequencyType;
|
||||
private final EnumOptionData interestPeriodType;
|
||||
private final EnumOptionData interestCalculationType;
|
||||
private final EnumOptionData interestCalculationDaysInYearType;
|
||||
private final BigDecimal minRequiredOpeningBalance;
|
||||
private final Integer lockinPeriodFrequency;
|
||||
private final EnumOptionData lockinPeriodFrequencyType;
|
||||
|
||||
// template
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<CurrencyData> currencyOptions;
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<EnumOptionData> interestRatePeriodFrequencyTypeOptions;
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<EnumOptionData> interestPeriodTypeOptions;
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<EnumOptionData> interestCalculationTypeOptions;
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions;
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions;
|
||||
|
||||
public static SavingsProductData template(final CurrencyData currency, final EnumOptionData interestRatePeriodFrequencyType,
|
||||
final Collection<CurrencyData> currencyOptions, final Collection<EnumOptionData> interestRatePeriodFrequencyTypeOptions,
|
||||
final EnumOptionData interestPeriodType, final EnumOptionData interestCalculationType,
|
||||
final EnumOptionData interestCalculationDaysInYearType, final Collection<CurrencyData> currencyOptions,
|
||||
final Collection<EnumOptionData> interestRatePeriodFrequencyTypeOptions,
|
||||
final Collection<EnumOptionData> interestPeriodTypeOptions, final Collection<EnumOptionData> interestCalculationTypeOptions,
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions) {
|
||||
|
||||
final Long id = null;
|
||||
final String name = null;
|
||||
final String description = null;
|
||||
final Integer digitsAfterDecimal = null;
|
||||
final BigDecimal interestRate = null;
|
||||
final BigDecimal minRequiredOpeningBalance = null;
|
||||
final Integer lockinPeriodFrequency = null;
|
||||
final EnumOptionData lockinPeriodFrequencyType = null;
|
||||
|
||||
return new SavingsProductData(id, name, description, currency, interestRate, interestRatePeriodFrequencyType,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, currencyOptions,
|
||||
interestRatePeriodFrequencyTypeOptions, lockinPeriodFrequencyTypeOptions);
|
||||
return new SavingsProductData(id, name, description, currency, interestRate, interestRatePeriodFrequencyType, interestPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType, currencyOptions, interestRatePeriodFrequencyTypeOptions, interestPeriodTypeOptions,
|
||||
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,25 +71,35 @@ public class SavingsProductData {
|
||||
*/
|
||||
public static SavingsProductData withTemplate(final SavingsProductData existingProduct, final Collection<CurrencyData> currencyOptions,
|
||||
final Collection<EnumOptionData> interestRatePeriodFrequencyTypeOptions,
|
||||
final Collection<EnumOptionData> interestPeriodTypeOptions, final Collection<EnumOptionData> interestCalculationTypeOptions,
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions) {
|
||||
|
||||
return new SavingsProductData(existingProduct.id, existingProduct.name, existingProduct.description, existingProduct.currency,
|
||||
existingProduct.interestRate, existingProduct.interestRatePeriodFrequencyType, existingProduct.minRequiredOpeningBalance,
|
||||
existingProduct.lockinPeriodFrequency, existingProduct.lockinPeriodFrequencyType, currencyOptions,
|
||||
interestRatePeriodFrequencyTypeOptions, lockinPeriodFrequencyTypeOptions);
|
||||
existingProduct.interestRate, existingProduct.interestRatePeriodFrequencyType, existingProduct.interestPeriodType,
|
||||
existingProduct.interestCalculationType, existingProduct.interestCalculationDaysInYearType,
|
||||
existingProduct.minRequiredOpeningBalance, existingProduct.lockinPeriodFrequency,
|
||||
existingProduct.lockinPeriodFrequencyType, currencyOptions, interestRatePeriodFrequencyTypeOptions,
|
||||
interestPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
|
||||
lockinPeriodFrequencyTypeOptions);
|
||||
}
|
||||
|
||||
public static SavingsProductData instance(final Long id, final String name, final String description, final CurrencyData currency,
|
||||
final BigDecimal interestRate, final EnumOptionData interestRatePeriodFrequencyType,
|
||||
final BigDecimal interestRate, final EnumOptionData interestRatePeriodFrequencyType, final EnumOptionData interestPeriodType,
|
||||
final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType,
|
||||
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType) {
|
||||
|
||||
final Collection<CurrencyData> currencyOptions = null;
|
||||
final Collection<EnumOptionData> interestRatePeriodFrequencyTypeOptions = null;
|
||||
final Collection<EnumOptionData> interestPeriodTypeOptions = null;
|
||||
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
|
||||
|
||||
return new SavingsProductData(id, name, description, currency, interestRate, interestRatePeriodFrequencyType,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, currencyOptions,
|
||||
interestRatePeriodFrequencyTypeOptions, lockinPeriodFrequencyTypeOptions);
|
||||
return new SavingsProductData(id, name, description, currency, interestRate, interestRatePeriodFrequencyType, interestPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType, currencyOptions, interestRatePeriodFrequencyTypeOptions, interestPeriodTypeOptions,
|
||||
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions);
|
||||
}
|
||||
|
||||
public static SavingsProductData lookup(final Long id, final String name) {
|
||||
@@ -83,24 +108,34 @@ public class SavingsProductData {
|
||||
final String description = null;
|
||||
final BigDecimal interestRate = null;
|
||||
final EnumOptionData interestRatePeriodFrequencyType = null;
|
||||
final EnumOptionData interestPeriodType = null;
|
||||
final EnumOptionData interestCalculationType = null;
|
||||
final EnumOptionData interestCalculationDaysInYearType = null;
|
||||
final BigDecimal minRequiredOpeningBalance = null;
|
||||
final Integer lockinPeriodFrequency = null;
|
||||
final EnumOptionData lockinPeriodFrequencyType = null;
|
||||
|
||||
final Collection<CurrencyData> currencyOptions = null;
|
||||
final Collection<EnumOptionData> interestRatePeriodFrequencyTypeOptions = null;
|
||||
final Collection<EnumOptionData> interestPeriodTypeOptions = null;
|
||||
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
|
||||
|
||||
return new SavingsProductData(id, name, description, currency, interestRate, interestRatePeriodFrequencyType,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, currencyOptions,
|
||||
interestRatePeriodFrequencyTypeOptions, lockinPeriodFrequencyTypeOptions);
|
||||
return new SavingsProductData(id, name, description, currency, interestRate, interestRatePeriodFrequencyType, interestPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType, currencyOptions, interestRatePeriodFrequencyTypeOptions, interestPeriodTypeOptions,
|
||||
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions);
|
||||
}
|
||||
|
||||
private SavingsProductData(final Long id, final String name, final String description, final CurrencyData currency,
|
||||
final BigDecimal interestRate, final EnumOptionData interestRatePeriodFrequencyType,
|
||||
final BigDecimal interestRate, final EnumOptionData interestRatePeriodFrequencyType, final EnumOptionData interestPeriodType,
|
||||
final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType,
|
||||
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency,
|
||||
final EnumOptionData lockinPeriodFrequencyType, final Collection<CurrencyData> currencyOptions,
|
||||
final Collection<EnumOptionData> interestRatePeriodFrequencyTypeOptions,
|
||||
final Collection<EnumOptionData> interestPeriodTypeOptions, final Collection<EnumOptionData> interestCalculationTypeOptions,
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
@@ -108,12 +143,18 @@ public class SavingsProductData {
|
||||
this.currency = currency;
|
||||
this.interestRate = interestRate;
|
||||
this.interestRatePeriodFrequencyType = interestRatePeriodFrequencyType;
|
||||
this.interestPeriodType = interestPeriodType;
|
||||
this.interestCalculationType = interestCalculationType;
|
||||
this.interestCalculationDaysInYearType = interestCalculationDaysInYearType;
|
||||
this.minRequiredOpeningBalance = minRequiredOpeningBalance;
|
||||
this.lockinPeriodFrequency = lockinPeriodFrequency;
|
||||
this.lockinPeriodFrequencyType = lockinPeriodFrequencyType;
|
||||
|
||||
this.currencyOptions = currencyOptions;
|
||||
this.interestRatePeriodFrequencyTypeOptions = interestRatePeriodFrequencyTypeOptions;
|
||||
this.interestPeriodTypeOptions = interestPeriodTypeOptions;
|
||||
this.interestCalculationTypeOptions = interestCalculationTypeOptions;
|
||||
this.interestCalculationDaysInYearTypeOptions = interestCalculationDaysInYearTypeOptions;
|
||||
this.lockinPeriodFrequencyTypeOptions = lockinPeriodFrequencyTypeOptions;
|
||||
}
|
||||
}
|
||||
+38
-3
@@ -1,10 +1,13 @@
|
||||
package org.mifosplatform.portfolio.savings.data;
|
||||
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.currencyCodeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.descriptionParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.digitsAfterDecimalParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestPeriodTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestRateParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestRatePeriodFrequencyTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyParamName;
|
||||
@@ -48,7 +51,8 @@ public class SavingsProductDataValidator {
|
||||
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(SAVINGS_PRODUCT_RESOURCE_NAME);
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource(SAVINGS_PRODUCT_RESOURCE_NAME);
|
||||
final JsonElement element = fromApiJsonHelper.parse(json);
|
||||
|
||||
final String name = fromApiJsonHelper.extractStringNamed(nameParamName, element);
|
||||
@@ -71,6 +75,17 @@ public class SavingsProductDataValidator {
|
||||
baseDataValidator.reset().parameter(interestRatePeriodFrequencyTypeParamName).value(interestRatePeriodFrequencyType).notNull()
|
||||
.inMinMaxRange(1, 3);
|
||||
|
||||
final Integer interestPeriodType = fromApiJsonHelper.extractIntegerSansLocaleNamed(interestPeriodTypeParamName, element);
|
||||
baseDataValidator.reset().parameter(interestPeriodTypeParamName).value(interestPeriodType).notNull().inMinMaxRange(1, 7);
|
||||
|
||||
final Integer interestCalculationType = fromApiJsonHelper.extractIntegerSansLocaleNamed(interestCalculationTypeParamName, element);
|
||||
baseDataValidator.reset().parameter(interestCalculationTypeParamName).value(interestCalculationType).notNull().inMinMaxRange(1, 2);
|
||||
|
||||
final Integer interestCalculationDaysInYearType = fromApiJsonHelper.extractIntegerSansLocaleNamed(
|
||||
interestCalculationDaysInYearTypeParamName, element);
|
||||
baseDataValidator.reset().parameter(interestCalculationDaysInYearTypeParamName).value(interestCalculationDaysInYearType).notNull()
|
||||
.inOfTheseValues(360, 365);
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) {
|
||||
final BigDecimal minOpeningBalance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minRequiredOpeningBalanceParamName,
|
||||
element);
|
||||
@@ -114,7 +129,8 @@ public class SavingsProductDataValidator {
|
||||
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(SAVINGS_PRODUCT_RESOURCE_NAME);
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource(SAVINGS_PRODUCT_RESOURCE_NAME);
|
||||
final JsonElement element = fromApiJsonHelper.parse(json);
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(nameParamName, element)) {
|
||||
@@ -149,6 +165,25 @@ public class SavingsProductDataValidator {
|
||||
.inMinMaxRange(1, 3);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(interestPeriodTypeParamName, element)) {
|
||||
final Integer interestPeriodType = fromApiJsonHelper.extractIntegerSansLocaleNamed(interestPeriodTypeParamName, element);
|
||||
baseDataValidator.reset().parameter(interestPeriodTypeParamName).value(interestPeriodType).notNull().inMinMaxRange(1, 7);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(interestCalculationTypeParamName, element)) {
|
||||
final Integer interestCalculationType = fromApiJsonHelper.extractIntegerSansLocaleNamed(interestCalculationTypeParamName,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(interestCalculationTypeParamName).value(interestCalculationType).notNull()
|
||||
.inMinMaxRange(1, 2);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(interestCalculationDaysInYearTypeParamName, element)) {
|
||||
final Integer interestCalculationDaysInYearType = fromApiJsonHelper.extractIntegerSansLocaleNamed(
|
||||
interestCalculationDaysInYearTypeParamName, element);
|
||||
baseDataValidator.reset().parameter(interestCalculationDaysInYearTypeParamName).value(interestCalculationDaysInYearType)
|
||||
.notNull().inOfTheseValues(360, 365);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) {
|
||||
|
||||
final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, element);
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
import org.joda.time.Days;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
public class InterestPeriodInterval {
|
||||
|
||||
private final LocalDate startDate;
|
||||
private final LocalDate endDate;
|
||||
|
||||
public static InterestPeriodInterval create(final LocalDate startDate, final LocalDate endDate) {
|
||||
return new InterestPeriodInterval(startDate, endDate);
|
||||
}
|
||||
|
||||
public InterestPeriodInterval(final LocalDate startDate, final LocalDate endDate) {
|
||||
this.startDate = startDate;
|
||||
this.endDate = endDate;
|
||||
}
|
||||
|
||||
public LocalDate startDate() {
|
||||
return this.startDate;
|
||||
}
|
||||
|
||||
public LocalDate endDate() {
|
||||
return this.endDate;
|
||||
}
|
||||
|
||||
public Integer daysInPeriodInclusiveOfEndDate() {
|
||||
return daysBetween() + 1;
|
||||
}
|
||||
|
||||
private Integer daysBetween() {
|
||||
return Days.daysBetween(this.startDate, this.endDate).getDays();
|
||||
}
|
||||
|
||||
public boolean contains(final LocalDate target) {
|
||||
return isBetweenInclusive(this.startDate, this.endDate, target);
|
||||
}
|
||||
|
||||
private boolean isBetweenInclusive(final LocalDate start, final LocalDate end, final LocalDate target) {
|
||||
return !target.isBefore(start) && !target.isAfter(end);
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
|
||||
public class InterestPeriodSummary {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final Date startDate;
|
||||
@SuppressWarnings("unused")
|
||||
private final Date endDate;
|
||||
@SuppressWarnings("unused")
|
||||
private final Integer numberOfDays;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal startingBalance;
|
||||
private final BigDecimal closingBalance;
|
||||
private final BigDecimal interestEarnedUnrounded;
|
||||
private final BigDecimal interestEarned;
|
||||
@SuppressWarnings("unused")
|
||||
private final List<SavingsAccountDailyBalance> dailyBalances;
|
||||
|
||||
public static InterestPeriodSummary from(final InterestPeriodInterval interestPeriodInterval, final Money startingBalance,
|
||||
final List<SavingsAccountDailyBalance> dailyBalances, final BigDecimal totalInterestEarnedUnrounded) {
|
||||
|
||||
return new InterestPeriodSummary(interestPeriodInterval, startingBalance, dailyBalances, totalInterestEarnedUnrounded);
|
||||
}
|
||||
|
||||
private InterestPeriodSummary(final InterestPeriodInterval interestPeriodInterval, final Money startingBalance,
|
||||
final List<SavingsAccountDailyBalance> dailyBalances, final BigDecimal totalInterestEarnedUnrounded) {
|
||||
this.startDate = interestPeriodInterval.startDate().toDate();
|
||||
this.endDate = interestPeriodInterval.endDate().toDate();
|
||||
this.numberOfDays = interestPeriodInterval.daysInPeriodInclusiveOfEndDate();
|
||||
this.startingBalance = startingBalance.getAmount();
|
||||
|
||||
final MonetaryCurrency currency = startingBalance.getCurrency();
|
||||
|
||||
this.interestEarnedUnrounded = totalInterestEarnedUnrounded;
|
||||
this.interestEarned = Money.of(currency, this.interestEarnedUnrounded).getAmount();
|
||||
|
||||
Money closingBalanceForPeriod = startingBalance.copy();
|
||||
|
||||
for (SavingsAccountDailyBalance balance : dailyBalances) {
|
||||
closingBalanceForPeriod = balance.dailyRunningBalance(currency);
|
||||
}
|
||||
|
||||
this.closingBalance = closingBalanceForPeriod.getAmount();
|
||||
this.dailyBalances = dailyBalances;
|
||||
}
|
||||
|
||||
public Money closingBalance(final MonetaryCurrency currency) {
|
||||
return Money.of(currency, this.closingBalance);
|
||||
}
|
||||
|
||||
public BigDecimal interest() {
|
||||
return this.interestEarned;
|
||||
}
|
||||
}
|
||||
+262
-14
@@ -6,6 +6,8 @@
|
||||
package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
@@ -29,6 +31,7 @@ import javax.persistence.UniqueConstraint;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.hibernate.annotations.LazyCollection;
|
||||
import org.hibernate.annotations.LazyCollectionOption;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
@@ -84,7 +87,28 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
@Column(name = "nominal_interest_rate_period_frequency_enum", nullable = false)
|
||||
private Integer interestRatePeriodFrequencyType;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
/**
|
||||
* The interest period is the span of time at the end of which savings in a
|
||||
* client’s account earn interest.
|
||||
*
|
||||
* A value from the {@link SavingsInterestPeriodType} enumeration.
|
||||
*/
|
||||
@Column(name = "interest_period_enum", nullable = false)
|
||||
private Integer interestPeriodType;
|
||||
|
||||
/**
|
||||
* A value from the {@link SavingsInterestCalculationType} enumeration.
|
||||
*/
|
||||
@Column(name = "interest_calculation_type_enum", nullable = false)
|
||||
private Integer interestCalculationType;
|
||||
|
||||
/**
|
||||
* A value from the {@link SavingsInterestCalculationDaysInYearType}
|
||||
* enumeration.
|
||||
*/
|
||||
@Column(name = "interest_calculation_days_in_year_type_enum", nullable = false)
|
||||
private Integer interestCalculationDaysInYearType;
|
||||
|
||||
@Column(name = "annual_nominal_interest_rate", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal annualInterestRate;
|
||||
|
||||
@@ -124,26 +148,25 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
public static SavingsAccount createNewAccount(final Client client, final Group group, final SavingsProduct product,
|
||||
final String accountNo, final String externalId, final boolean active, final LocalDate activationDate,
|
||||
final BigDecimal interestRate, final PeriodFrequencyType interestRatePeriodFrequencyType, final BigDecimal annualInterestRate,
|
||||
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency,
|
||||
final PeriodFrequencyType lockinPeriodFrequencyType) {
|
||||
|
||||
SavingsAccountStatusType status = SavingsAccountStatusType.UNACTIVATED;
|
||||
if (active) {
|
||||
status = SavingsAccountStatusType.ACTIVE;
|
||||
}
|
||||
final String accountNo, final String externalId, final BigDecimal interestRate,
|
||||
final PeriodFrequencyType interestRatePeriodFrequencyType, final BigDecimal annualInterestRate,
|
||||
final SavingsInterestPeriodType interestPeriodType, final SavingsInterestCalculationType interestCalculationType,
|
||||
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
|
||||
final Integer lockinPeriodFrequency, final PeriodFrequencyType lockinPeriodFrequencyType) {
|
||||
|
||||
final SavingsAccountStatusType status = SavingsAccountStatusType.UNACTIVATED;
|
||||
final LocalDate activationDate = null;
|
||||
return new SavingsAccount(client, group, product, accountNo, externalId, status, activationDate, interestRate,
|
||||
interestRatePeriodFrequencyType, annualInterestRate, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType);
|
||||
interestRatePeriodFrequencyType, annualInterestRate, interestPeriodType, interestCalculationType,
|
||||
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType);
|
||||
}
|
||||
|
||||
private SavingsAccount(final Client client, final Group group, final SavingsProduct product, final String accountNo,
|
||||
final String externalId, final SavingsAccountStatusType status, final LocalDate activationDate, final BigDecimal interestRate,
|
||||
final PeriodFrequencyType interestRatePeriodFrequencyType, final BigDecimal annualInterestRate,
|
||||
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency,
|
||||
final PeriodFrequencyType lockinPeriodFrequencyType) {
|
||||
final SavingsInterestPeriodType interestPeriodType, final SavingsInterestCalculationType interestCalculationType,
|
||||
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
|
||||
final Integer lockinPeriodFrequency, final PeriodFrequencyType lockinPeriodFrequencyType) {
|
||||
this.client = client;
|
||||
this.group = group;
|
||||
this.product = product;
|
||||
@@ -162,6 +185,9 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
this.interestRate = interestRate;
|
||||
this.interestRatePeriodFrequencyType = interestRatePeriodFrequencyType.getValue();
|
||||
this.annualInterestRate = annualInterestRate;
|
||||
this.interestPeriodType = interestPeriodType.getValue();
|
||||
this.interestCalculationType = interestCalculationType.getValue();
|
||||
this.interestCalculationDaysInYearType = interestCalculationDaysInYearType.getValue();
|
||||
this.minRequiredOpeningBalance = minRequiredOpeningBalance;
|
||||
this.lockinPeriodFrequency = lockinPeriodFrequency;
|
||||
if (lockinPeriodFrequencyType != null) {
|
||||
@@ -249,6 +275,224 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
return lockedInUntilLocalDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* All interest calculation based on END-OF-DAY-BALANCE.
|
||||
*
|
||||
* Interest calculation is performed on-the-fly over all account
|
||||
* transactions.
|
||||
*/
|
||||
/*
|
||||
* The daily balance method is the application of a daily interest rate to
|
||||
* the full amount of principal in the account each day. For the days the
|
||||
* account is overdrawn, a zero balance is used to calculate the interest
|
||||
* for those days.
|
||||
*/
|
||||
public void calculateInterest() {
|
||||
|
||||
// no openingBalance concept supported yet but probably will to all for
|
||||
// migrations.
|
||||
final Money openingAccountBalance = Money.zero(this.currency);
|
||||
|
||||
// 1. update existing transactions so derived balance fields are
|
||||
// correct.
|
||||
recalculateDailyBalances(openingAccountBalance);
|
||||
|
||||
// 2. determine interest periods
|
||||
final SavingsInterestPeriodType interestPeriodType = SavingsInterestPeriodType.fromInt(this.interestPeriodType);
|
||||
final SavingsInterestCalculationDaysInYearType daysInYearType = SavingsInterestCalculationDaysInYearType
|
||||
.fromInt(this.interestCalculationDaysInYearType);
|
||||
final List<InterestPeriodInterval> interestPeriods = determineInterestPeriods(getActivationLocalDate(), interestPeriodType);
|
||||
|
||||
final List<InterestPeriodSummary> interestPeriodSummaries = deriveInterestPeriodSummaries(openingAccountBalance, interestPeriods,
|
||||
this.annualInterestRate, daysInYearType);
|
||||
|
||||
this.summary.updateFromInterestPeriodSummaries(currency, interestPeriodSummaries);
|
||||
}
|
||||
|
||||
private List<InterestPeriodInterval> determineInterestPeriods(final LocalDate activationLocalDate,
|
||||
final SavingsInterestPeriodType interestPeriodType) {
|
||||
|
||||
LocalDate today = DateUtils.getLocalDateOfTenant();
|
||||
LocalDate periodStartDate = activationLocalDate;
|
||||
LocalDate periodEndDate = periodStartDate;
|
||||
|
||||
final List<InterestPeriodInterval> interestPeriods = new ArrayList<InterestPeriodInterval>();
|
||||
|
||||
while (!periodStartDate.isAfter(today) && !periodEndDate.isAfter(today)) {
|
||||
|
||||
periodEndDate = determineInterestPeriodEndDateFrom(periodStartDate, interestPeriodType);
|
||||
interestPeriods.add(InterestPeriodInterval.create(periodStartDate, periodEndDate));
|
||||
|
||||
// move periodStartDate forward to day after this period
|
||||
periodStartDate = periodEndDate.plusDays(1);
|
||||
}
|
||||
|
||||
return interestPeriods;
|
||||
}
|
||||
|
||||
private LocalDate determineInterestPeriodEndDateFrom(final LocalDate periodStartDate, final SavingsInterestPeriodType interestPeriodType) {
|
||||
|
||||
LocalDate periodEndDate = DateUtils.getLocalDateOfTenant();
|
||||
|
||||
switch (interestPeriodType) {
|
||||
case INVALID:
|
||||
break;
|
||||
case DAILY:
|
||||
periodEndDate = periodStartDate;
|
||||
break;
|
||||
case WEEKLY:
|
||||
periodEndDate = periodStartDate.dayOfWeek().withMaximumValue();
|
||||
break;
|
||||
case BIWEEKLY:
|
||||
final LocalDate closestEndOfWeek = periodStartDate.dayOfWeek().withMaximumValue();
|
||||
periodEndDate = closestEndOfWeek.plusWeeks(1);
|
||||
break;
|
||||
case MONTHLY:
|
||||
// produce period end date on last day of current month
|
||||
periodEndDate = periodStartDate.dayOfMonth().withMaximumValue();
|
||||
break;
|
||||
case QUATERLY:
|
||||
// jan 1st to mar 31st, 1st apr to jun 30, jul 1st to sept 30,
|
||||
// oct 1st to dec 31
|
||||
int year = periodStartDate.getYearOfEra();
|
||||
int monthofYear = periodStartDate.getMonthOfYear();
|
||||
if (monthofYear <= 3) {
|
||||
periodEndDate = new DateTime().withDate(year, 3, 31).toLocalDate();
|
||||
} else if (monthofYear <= 6) {
|
||||
periodEndDate = new DateTime().withDate(year, 6, 30).toLocalDate();
|
||||
} else if (monthofYear <= 9) {
|
||||
periodEndDate = new DateTime().withDate(year, 9, 30).toLocalDate();
|
||||
} else if (monthofYear <= 12) {
|
||||
periodEndDate = new DateTime().withDate(year, 12, 31).toLocalDate();
|
||||
}
|
||||
break;
|
||||
case SEMIANNUAL:
|
||||
// jan 1st to 30, jul 1st to dec 31
|
||||
year = periodStartDate.getYearOfEra();
|
||||
monthofYear = periodStartDate.getMonthOfYear();
|
||||
if (monthofYear <= 6) {
|
||||
periodEndDate = new DateTime().withDate(year, 6, 30).toLocalDate();
|
||||
} else if (monthofYear <= 12) {
|
||||
periodEndDate = new DateTime().withDate(year, 12, 31).toLocalDate();
|
||||
}
|
||||
break;
|
||||
case ANNUAL:
|
||||
periodEndDate = periodStartDate.monthOfYear().withMaximumValue();
|
||||
periodEndDate = periodStartDate.dayOfMonth().withMaximumValue();
|
||||
break;
|
||||
}
|
||||
|
||||
return periodEndDate;
|
||||
}
|
||||
|
||||
private BigDecimal determinePeriodsInOneYear(final SavingsInterestCalculationDaysInYearType daysInYearType) {
|
||||
|
||||
BigDecimal periodsInOneYear = BigDecimal.ZERO;
|
||||
|
||||
switch (daysInYearType) {
|
||||
case DAYS_360:
|
||||
periodsInOneYear = BigDecimal.valueOf(360l);
|
||||
break;
|
||||
case DAYS_365:
|
||||
periodsInOneYear = BigDecimal.valueOf(365l);
|
||||
break;
|
||||
case INVALID:
|
||||
break;
|
||||
}
|
||||
|
||||
return periodsInOneYear;
|
||||
}
|
||||
|
||||
private List<InterestPeriodSummary> deriveInterestPeriodSummaries(final Money openingAccountBalance,
|
||||
final List<InterestPeriodInterval> interestPeriods, final BigDecimal annualInterestRate,
|
||||
final SavingsInterestCalculationDaysInYearType daysInYearType) {
|
||||
|
||||
final List<InterestPeriodSummary> summaries = new ArrayList<InterestPeriodSummary>();
|
||||
|
||||
final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN);
|
||||
final BigDecimal percentageDivisor = BigDecimal.valueOf(100l);
|
||||
final BigDecimal periodsInOneYear = determinePeriodsInOneYear(daysInYearType);
|
||||
final BigDecimal periodsInOneYearAsFraction = BigDecimal.ONE.divide(periodsInOneYear, mc);
|
||||
final BigDecimal annualInterestRateAsFraction = annualInterestRate.divide(percentageDivisor, mc);
|
||||
|
||||
final SavingsInterestCalculationType interestCalculationType = SavingsInterestCalculationType.fromInt(this.interestCalculationType);
|
||||
|
||||
final SavingsInterestCalculatorFactory interestCalculatorFactory = new SavingsInterestCalculatorFactory();
|
||||
final SavingsInterestCalculator interestCalculator = interestCalculatorFactory.createFrom(interestCalculationType,
|
||||
periodsInOneYearAsFraction, annualInterestRateAsFraction);
|
||||
|
||||
Money periodOpeningBalance = openingAccountBalance.copy();
|
||||
for (InterestPeriodInterval interestPeriodInterval : interestPeriods) {
|
||||
|
||||
InterestPeriodSummary interestPeriodSummary = calculateInterestPeriodSummaryFor(interestPeriodInterval, periodOpeningBalance,
|
||||
interestCalculator);
|
||||
|
||||
periodOpeningBalance = interestPeriodSummary.closingBalance(this.currency);
|
||||
|
||||
summaries.add(interestPeriodSummary);
|
||||
}
|
||||
|
||||
return summaries;
|
||||
}
|
||||
|
||||
private InterestPeriodSummary calculateInterestPeriodSummaryFor(final InterestPeriodInterval interestPeriodInterval,
|
||||
final Money startingBalanceForPeriod, final SavingsInterestCalculator interestCalculator) {
|
||||
|
||||
List<SavingsAccountDailyBalance> dailyBalances = new ArrayList<SavingsAccountDailyBalance>();
|
||||
for (SavingsAccountTransaction transaction : this.transactions) {
|
||||
|
||||
if (transaction.isAcceptableForDailyBalance(interestPeriodInterval)) {
|
||||
SavingsAccountDailyBalance dailyBalance = transaction.toDailyBalance(interestPeriodInterval.endDate());
|
||||
dailyBalances.add(dailyBalance);
|
||||
}
|
||||
}
|
||||
|
||||
if (dailyBalances.isEmpty()) {
|
||||
SavingsAccountDailyBalance dailyBalance = SavingsAccountDailyBalance.createFrom(interestPeriodInterval,
|
||||
startingBalanceForPeriod.getAmount());
|
||||
dailyBalances.add(dailyBalance);
|
||||
}
|
||||
|
||||
final BigDecimal totalInterestEarnedUnrounded = interestCalculator.calculate(dailyBalances,
|
||||
interestPeriodInterval.daysInPeriodInclusiveOfEndDate());
|
||||
|
||||
return InterestPeriodSummary.from(interestPeriodInterval, startingBalanceForPeriod, dailyBalances, totalInterestEarnedUnrounded);
|
||||
}
|
||||
|
||||
private void recalculateDailyBalances(final Money openingAccountBalance) {
|
||||
|
||||
Money runningBalance = openingAccountBalance.copy();
|
||||
for (SavingsAccountTransaction transaction : this.transactions) {
|
||||
|
||||
if (transaction.isReversed()) {
|
||||
transaction.zeroBalanceFields();
|
||||
} else {
|
||||
Money transactionAmount = Money.zero(this.currency);
|
||||
if (transaction.isDeposit()) {
|
||||
transactionAmount = transactionAmount.plus(transaction.getAmount(this.currency));
|
||||
} else if (transaction.isWithdrawal()) {
|
||||
transactionAmount = transactionAmount.minus(transaction.getAmount(this.currency));
|
||||
} else if (transaction.isInterestPosting()) {
|
||||
transactionAmount = transactionAmount.plus(transaction.getAmount(this.currency));
|
||||
}
|
||||
|
||||
runningBalance = runningBalance.plus(transactionAmount);
|
||||
transaction.updateRunningBalance(runningBalance);
|
||||
}
|
||||
}
|
||||
|
||||
// loop over transactions in reverse
|
||||
LocalDate endOfBalanceDate = DateUtils.getLocalDateOfTenant();
|
||||
for (int i = this.transactions.size() - 1; i >= 0; i--) {
|
||||
SavingsAccountTransaction transaction = this.transactions.get(i);
|
||||
transaction.updateCumulativeBalanceAndDates(this.currency, endOfBalanceDate);
|
||||
|
||||
// this transactions transaction date is end of balance date for
|
||||
// previous transaction.
|
||||
endOfBalanceDate = transaction.localDate().minusDays(1);
|
||||
}
|
||||
}
|
||||
|
||||
public SavingsAccountTransaction deposit(final DateTimeFormatter formatter, final LocalDate transactionDate,
|
||||
final BigDecimal transactionAmount) {
|
||||
|
||||
@@ -293,6 +537,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
|
||||
this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions);
|
||||
|
||||
calculateInterest();
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
@@ -368,6 +614,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
|
||||
this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions);
|
||||
|
||||
calculateInterest();
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
|
||||
+26
-4
@@ -7,6 +7,9 @@ package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activationDateParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestPeriodTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestRateParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestRatePeriodFrequencyTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyParamName;
|
||||
@@ -112,6 +115,25 @@ public class SavingsAccountAssembler {
|
||||
|
||||
final BigDecimal annualInterestRate = this.aprCalculator.calculateFrom(interestRatePeriodFrequencyType, interestRate);
|
||||
|
||||
SavingsInterestPeriodType interestPeriodType = null;
|
||||
final Integer interestPeriodTypeValue = command.integerValueOfParameterNamed(interestPeriodTypeParamName);
|
||||
if (interestPeriodTypeValue != null) {
|
||||
interestPeriodType = SavingsInterestPeriodType.fromInt(interestPeriodTypeValue);
|
||||
}
|
||||
|
||||
SavingsInterestCalculationType interestCalculationType = null;
|
||||
final Integer interestCalculationTypeValue = command.integerValueOfParameterNamed(interestCalculationTypeParamName);
|
||||
if (interestCalculationTypeValue != null) {
|
||||
interestCalculationType = SavingsInterestCalculationType.fromInt(interestCalculationTypeValue);
|
||||
}
|
||||
|
||||
SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType = null;
|
||||
final Integer interestCalculationDaysInYearTypeValue = command
|
||||
.integerValueOfParameterNamed(interestCalculationDaysInYearTypeParamName);
|
||||
if (interestCalculationDaysInYearTypeValue != null) {
|
||||
interestCalculationDaysInYearType = SavingsInterestCalculationDaysInYearType.fromInt(interestCalculationDaysInYearTypeValue);
|
||||
}
|
||||
|
||||
BigDecimal minRequiredOpeningBalance = null;
|
||||
if (command.parameterExists(minRequiredOpeningBalanceParamName)) {
|
||||
minRequiredOpeningBalance = command.bigDecimalValueOfParameterNamed(minRequiredOpeningBalanceParamName);
|
||||
@@ -137,12 +159,12 @@ public class SavingsAccountAssembler {
|
||||
lockinPeriodFrequencyType = product.lockinPeriodFrequencyType();
|
||||
}
|
||||
|
||||
final SavingsAccount account = SavingsAccount.createNewAccount(client, group, product, accountNo, externalId, active,
|
||||
activationDate, interestRate, interestRatePeriodFrequencyType, annualInterestRate, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType);
|
||||
final SavingsAccount account = SavingsAccount.createNewAccount(client, group, product, accountNo, externalId, interestRate,
|
||||
interestRatePeriodFrequencyType, annualInterestRate, interestPeriodType, interestCalculationType,
|
||||
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType);
|
||||
account.setHelpers(this.savingsAccountTransactionSummaryWrapper);
|
||||
|
||||
if (account.isActive()) {
|
||||
if (active) {
|
||||
final Locale locale = command.extractLocale();
|
||||
final DateTimeFormatter formatter = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
|
||||
account.activate(formatter, activationDate);
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
|
||||
public class SavingsAccountDailyBalance {
|
||||
|
||||
private final InterestPeriodInterval interestPeriodInterval;
|
||||
private final BigDecimal runningBalance;
|
||||
private final BigDecimal cumulativeBalance;
|
||||
|
||||
public static SavingsAccountDailyBalance createFrom(final InterestPeriodInterval interestPeriodInterval, final BigDecimal runningBalance) {
|
||||
return new SavingsAccountDailyBalance(interestPeriodInterval, runningBalance);
|
||||
}
|
||||
|
||||
private SavingsAccountDailyBalance(final InterestPeriodInterval interestPeriodInterval, final BigDecimal runningBalance) {
|
||||
this.interestPeriodInterval = interestPeriodInterval;
|
||||
this.runningBalance = runningBalance;
|
||||
final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN);
|
||||
final BigDecimal numberOfDaysBigDecimal = BigDecimal.valueOf(interestPeriodInterval.daysInPeriodInclusiveOfEndDate());
|
||||
this.cumulativeBalance = runningBalance.multiply(numberOfDaysBigDecimal, mc);
|
||||
}
|
||||
|
||||
public Money dailyRunningBalance(final MonetaryCurrency currency) {
|
||||
return Money.of(currency, runningBalance);
|
||||
}
|
||||
|
||||
public BigDecimal cumulativeBalance() {
|
||||
return this.cumulativeBalance;
|
||||
}
|
||||
|
||||
public Integer numberOfDays() {
|
||||
return interestPeriodInterval.daysInPeriodInclusiveOfEndDate();
|
||||
}
|
||||
|
||||
public boolean isGreaterThanZero() {
|
||||
return this.runningBalance.compareTo(BigDecimal.ZERO) > 0;
|
||||
}
|
||||
|
||||
public BigDecimal interestUsingDailyBalanceMethod(final BigDecimal dailyInterestRate, final Integer numberOfDays) {
|
||||
final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN);
|
||||
final BigDecimal numberOfDaysBigDecimal = BigDecimal.valueOf(numberOfDays.longValue());
|
||||
return this.runningBalance.multiply(dailyInterestRate, mc).multiply(numberOfDaysBigDecimal, mc);
|
||||
}
|
||||
}
|
||||
+13
@@ -22,6 +22,10 @@ public final class SavingsAccountSummary {
|
||||
@Column(name = "total_withdrawals_derived", scale = 6, precision = 19)
|
||||
private BigDecimal totalWithdrawals;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "total_interest_earned_derived", scale = 6, precision = 19)
|
||||
private BigDecimal totalInterestEarned;
|
||||
|
||||
@Column(name = "total_interest_posted_derived", scale = 6, precision = 19)
|
||||
private BigDecimal totalInterestPosted;
|
||||
|
||||
@@ -43,6 +47,15 @@ public final class SavingsAccountSummary {
|
||||
.getAmount();
|
||||
}
|
||||
|
||||
public void updateFromInterestPeriodSummaries(final MonetaryCurrency currency, final List<InterestPeriodSummary> interestPeriodSummaries) {
|
||||
Money totalInterestEarned = Money.zero(currency);
|
||||
for (InterestPeriodSummary interestPeriodSummary : interestPeriodSummaries) {
|
||||
totalInterestEarned = totalInterestEarned.plus(interestPeriodSummary.interest());
|
||||
}
|
||||
|
||||
this.totalInterestEarned = totalInterestEarned.getAmountDefaultedToNullIfZero();
|
||||
}
|
||||
|
||||
public boolean isLessThanOrEqualToAccountBalance(final Money amount) {
|
||||
final Money accountBalance = getAccountBalance(amount.getCurrency());
|
||||
return accountBalance.isGreaterThanOrEqualTo(amount);
|
||||
|
||||
+69
-3
@@ -37,7 +37,6 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
||||
@Column(name = "transaction_type_enum", nullable = false)
|
||||
private final Integer typeOf;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "transaction_date", nullable = false)
|
||||
private final Date dateOf;
|
||||
@@ -48,6 +47,20 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
||||
@Column(name = "is_reversed", nullable = false)
|
||||
private boolean reversed;
|
||||
|
||||
@Column(name = "running_balance_derived", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal runningBalance;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "cumulative_balance_derived", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal cumulativeBalance;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "balance_end_date_derived", nullable = false)
|
||||
private Date balanceEndDate;
|
||||
|
||||
@Column(name = "balance_number_of_days_derived", nullable = false)
|
||||
private Integer balanceNumberOfDays;
|
||||
|
||||
protected SavingsAccountTransaction() {
|
||||
this.dateOf = null;
|
||||
this.typeOf = null;
|
||||
@@ -63,15 +76,19 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
||||
return new SavingsAccountTransaction(savingsAccount, SavingsAccountTransactionType.WITHDRAWAL.getValue(), date, amount, isReversed);
|
||||
}
|
||||
|
||||
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Integer typeOf, final LocalDate dateOf,
|
||||
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Integer typeOf, final LocalDate transactionLocalDate,
|
||||
final Money amount, final boolean isReversed) {
|
||||
this.savingsAccount = savingsAccount;
|
||||
this.typeOf = typeOf;
|
||||
this.dateOf = dateOf.toDate();
|
||||
this.dateOf = transactionLocalDate.toDate();
|
||||
this.amount = amount.getAmount();
|
||||
this.reversed = isReversed;
|
||||
}
|
||||
|
||||
public LocalDate localDate() {
|
||||
return new LocalDate(this.dateOf);
|
||||
}
|
||||
|
||||
public void reverse() {
|
||||
this.reversed = true;
|
||||
}
|
||||
@@ -99,4 +116,53 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
||||
public boolean isReversed() {
|
||||
return this.reversed;
|
||||
}
|
||||
|
||||
public void zeroBalanceFields() {
|
||||
this.runningBalance = null;
|
||||
this.cumulativeBalance = null;
|
||||
this.balanceEndDate = null;
|
||||
this.balanceNumberOfDays = null;
|
||||
}
|
||||
|
||||
public void updateRunningBalance(final Money balance) {
|
||||
this.runningBalance = balance.getAmount();
|
||||
}
|
||||
|
||||
public void updateCumulativeBalanceAndDates(final MonetaryCurrency currency, final LocalDate endOfBalanceDate) {
|
||||
this.balanceEndDate = endOfBalanceDate.toDate();
|
||||
this.balanceNumberOfDays = InterestPeriodInterval.create(getTransactionLocalDate(), endOfBalanceDate)
|
||||
.daysInPeriodInclusiveOfEndDate();
|
||||
this.cumulativeBalance = Money.of(currency, this.runningBalance).multipliedBy(this.balanceNumberOfDays).getAmount();
|
||||
}
|
||||
|
||||
private LocalDate getTransactionLocalDate() {
|
||||
return new LocalDate(this.dateOf);
|
||||
}
|
||||
|
||||
private LocalDate getEndOfBalanceLocalDate() {
|
||||
LocalDate endDate = null;
|
||||
if (this.balanceEndDate != null) {
|
||||
endDate = new LocalDate(this.balanceEndDate);
|
||||
}
|
||||
return endDate;
|
||||
}
|
||||
|
||||
public boolean isAcceptableForDailyBalance(final InterestPeriodInterval interestPeriodInterval) {
|
||||
return isNotReversed() && interestPeriodInterval.contains(getTransactionLocalDate()) && isABalanceForAtLeastOneDay();
|
||||
}
|
||||
|
||||
private boolean isABalanceForAtLeastOneDay() {
|
||||
return this.balanceNumberOfDays != null && this.balanceNumberOfDays > 0;
|
||||
}
|
||||
|
||||
public SavingsAccountDailyBalance toDailyBalance(final LocalDate periodEnd) {
|
||||
|
||||
LocalDate balanceValidTo = getEndOfBalanceLocalDate();
|
||||
if (periodEnd.isBefore(getEndOfBalanceLocalDate())) {
|
||||
balanceValidTo = periodEnd;
|
||||
}
|
||||
|
||||
final InterestPeriodInterval interestPeriodInterval = InterestPeriodInterval.create(getTransactionLocalDate(), balanceValidTo);
|
||||
return SavingsAccountDailyBalance.createFrom(interestPeriodInterval, this.runningBalance);
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
/**
|
||||
* <ul>
|
||||
* People typically use either of the following settings when calculating there
|
||||
* interest using the daily method:
|
||||
* <li>360 and</li>
|
||||
* <li>365</li>
|
||||
* </ul>
|
||||
*/
|
||||
public enum SavingsInterestCalculationDaysInYearType {
|
||||
|
||||
INVALID(0, "savingsInterestCalculationDaysInYearType.invalid"), //
|
||||
DAYS_360(360, "savingsInterestCalculationDaysInYearType.days360"), //
|
||||
DAYS_365(365, "savingsInterestCalculationDaysInYearType.days365");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
|
||||
private SavingsInterestCalculationDaysInYearType(final Integer value, final String code) {
|
||||
this.value = value;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public static SavingsInterestCalculationDaysInYearType fromInt(final Integer type) {
|
||||
SavingsInterestCalculationDaysInYearType repaymentFrequencyType = SavingsInterestCalculationDaysInYearType.INVALID;
|
||||
if (type != null) {
|
||||
switch (type) {
|
||||
case 360:
|
||||
repaymentFrequencyType = SavingsInterestCalculationDaysInYearType.DAYS_360;
|
||||
break;
|
||||
case 365:
|
||||
repaymentFrequencyType = SavingsInterestCalculationDaysInYearType.DAYS_365;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return repaymentFrequencyType;
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
/**
|
||||
* <ul>
|
||||
* There are two methods to calculate the interest on a savings account:
|
||||
* <li>The daily balance method; and</li>
|
||||
* <li>The average daily balance method.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* The interest calculation must be based on a point in time for determining the
|
||||
* balance in the account, such as:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>beginning-of-day balance</li>
|
||||
* <li>end-of-day balance</li>
|
||||
* <li>close-of-business-day balance</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Any one of the three may be used, but must be applied consistently.
|
||||
* End-of-day balance is used by default at present.
|
||||
* </p>
|
||||
*/
|
||||
public enum SavingsInterestCalculationType {
|
||||
|
||||
INVALID(0, "savingsInterestCalculationType.invalid"), //
|
||||
DAILY_BALANCE(1, "savingsInterestCalculationType.dailybalance"), //
|
||||
AVERAGE_DAILY_BALANCE(2, "savingsInterestCalculationType.averagedailybalance");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
|
||||
private SavingsInterestCalculationType(final Integer value, final String code) {
|
||||
this.value = value;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public static SavingsInterestCalculationType fromInt(final Integer type) {
|
||||
SavingsInterestCalculationType repaymentFrequencyType = SavingsInterestCalculationType.INVALID;
|
||||
if (type != null) {
|
||||
switch (type) {
|
||||
case 1:
|
||||
repaymentFrequencyType = SavingsInterestCalculationType.DAILY_BALANCE;
|
||||
break;
|
||||
case 2:
|
||||
repaymentFrequencyType = SavingsInterestCalculationType.AVERAGE_DAILY_BALANCE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return repaymentFrequencyType;
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
public interface SavingsInterestCalculator {
|
||||
|
||||
BigDecimal calculate(List<SavingsAccountDailyBalance> dailyBalances, Integer numberOfDays);
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class SavingsInterestCalculatorFactory {
|
||||
|
||||
public SavingsInterestCalculator createFrom(final SavingsInterestCalculationType interestCalculationType,
|
||||
final BigDecimal periodsInOneYearAsFraction, final BigDecimal annualInterestRateAsFraction) {
|
||||
|
||||
SavingsInterestCalculator savingsInterestCalculator = new SavingsInterestCalculatorForDailyBalance(periodsInOneYearAsFraction,
|
||||
annualInterestRateAsFraction);
|
||||
switch (interestCalculationType) {
|
||||
case AVERAGE_DAILY_BALANCE:
|
||||
savingsInterestCalculator = new SavingsInterestCalculatorForAverageBalance(periodsInOneYearAsFraction,
|
||||
annualInterestRateAsFraction);
|
||||
break;
|
||||
case DAILY_BALANCE:
|
||||
savingsInterestCalculator = new SavingsInterestCalculatorForDailyBalance(periodsInOneYearAsFraction,
|
||||
annualInterestRateAsFraction);
|
||||
break;
|
||||
case INVALID:
|
||||
break;
|
||||
}
|
||||
|
||||
return savingsInterestCalculator;
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Calculates interest on a savings account using the <b>Average Balance</b>
|
||||
* method.
|
||||
*/
|
||||
public class SavingsInterestCalculatorForAverageBalance implements SavingsInterestCalculator {
|
||||
|
||||
private final BigDecimal periodsInOneYearAsFraction;
|
||||
private final BigDecimal annualInterestRateAsFraction;
|
||||
|
||||
public SavingsInterestCalculatorForAverageBalance(final BigDecimal periodsInOneYearAsFraction,
|
||||
final BigDecimal annualInterestRateAsFraction) {
|
||||
this.periodsInOneYearAsFraction = periodsInOneYearAsFraction;
|
||||
this.annualInterestRateAsFraction = annualInterestRateAsFraction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal calculate(final List<SavingsAccountDailyBalance> dailyBalances, final Integer numberOfDays) {
|
||||
|
||||
final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN);
|
||||
final BigDecimal dailyInterestRate = annualInterestRateAsFraction.multiply(periodsInOneYearAsFraction, mc);
|
||||
final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays.longValue()));
|
||||
|
||||
final BigDecimal numberOfDaysBigDecimal = BigDecimal.valueOf(numberOfDays.longValue());
|
||||
|
||||
BigDecimal totalCumulativeBalanceForPeriod = BigDecimal.ZERO;
|
||||
for (SavingsAccountDailyBalance balance : dailyBalances) {
|
||||
if (balance.isGreaterThanZero()) {
|
||||
final BigDecimal cumulativeBalance = balance.cumulativeBalance();
|
||||
totalCumulativeBalanceForPeriod = totalCumulativeBalanceForPeriod.add(cumulativeBalance);
|
||||
}
|
||||
}
|
||||
|
||||
final BigDecimal averageDailyBalance = totalCumulativeBalanceForPeriod.divide(numberOfDaysBigDecimal, mc);
|
||||
|
||||
return averageDailyBalance.multiply(periodicInterestRate, mc);
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Calculates interest on a savings account using the <b>Daily Balance</b>
|
||||
* method.
|
||||
*/
|
||||
public class SavingsInterestCalculatorForDailyBalance implements SavingsInterestCalculator {
|
||||
|
||||
private final BigDecimal periodsInOneYearAsFraction;
|
||||
private final BigDecimal annualInterestRateAsFraction;
|
||||
|
||||
public SavingsInterestCalculatorForDailyBalance(final BigDecimal periodsInOneYearAsFraction,
|
||||
final BigDecimal annualInterestRateAsFraction) {
|
||||
this.periodsInOneYearAsFraction = periodsInOneYearAsFraction;
|
||||
this.annualInterestRateAsFraction = annualInterestRateAsFraction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal calculate(final List<SavingsAccountDailyBalance> dailyBalances, final Integer numberOfDays) {
|
||||
final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN);
|
||||
final BigDecimal dailyInterestRate = annualInterestRateAsFraction.multiply(periodsInOneYearAsFraction, mc);
|
||||
|
||||
BigDecimal interestCalculated = BigDecimal.ZERO;
|
||||
|
||||
for (SavingsAccountDailyBalance balance : dailyBalances) {
|
||||
if (balance.isGreaterThanZero()) {
|
||||
final BigDecimal interestForBalance = balance.interestUsingDailyBalanceMethod(dailyInterestRate, numberOfDays);
|
||||
interestCalculated = interestCalculated.add(interestForBalance, mc);
|
||||
}
|
||||
}
|
||||
|
||||
return interestCalculated;
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The interest period is the span of time at the end of which savings in a
|
||||
* client’s account earn interest.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Interest periods may differ for different types of savings products. In all
|
||||
* cases, savings accounts must have established and published interest periods.
|
||||
* </p>
|
||||
*/
|
||||
public enum SavingsInterestPeriodType {
|
||||
|
||||
INVALID(0, "savingsCalculationPeriodType.invalid"), //
|
||||
DAILY(1, "savingsCalculationPeriodType.daily"), //
|
||||
WEEKLY(2, "savingsCalculationPeriodType.weekly"), //
|
||||
BIWEEKLY(3, "savingsCalculationPeriodType.biweekly"), //
|
||||
MONTHLY(4, "savingsCalculationPeriodType.monthly"), //
|
||||
QUATERLY(5, "savingsCalculationPeriodType.quarterly"), //
|
||||
SEMIANNUAL(6, "savingsCalculationPeriodType.semiannual"), //
|
||||
ANNUAL(7, "savingsCalculationPeriodType.annual");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
|
||||
private SavingsInterestPeriodType(final Integer value, final String code) {
|
||||
this.value = value;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public static SavingsInterestPeriodType fromInt(final Integer type) {
|
||||
SavingsInterestPeriodType repaymentFrequencyType = SavingsInterestPeriodType.INVALID;
|
||||
if (type != null) {
|
||||
switch (type) {
|
||||
case 1:
|
||||
repaymentFrequencyType = SavingsInterestPeriodType.DAILY;
|
||||
break;
|
||||
case 2:
|
||||
repaymentFrequencyType = SavingsInterestPeriodType.WEEKLY;
|
||||
break;
|
||||
case 3:
|
||||
repaymentFrequencyType = SavingsInterestPeriodType.BIWEEKLY;
|
||||
break;
|
||||
case 4:
|
||||
repaymentFrequencyType = SavingsInterestPeriodType.MONTHLY;
|
||||
break;
|
||||
case 5:
|
||||
repaymentFrequencyType = SavingsInterestPeriodType.QUATERLY;
|
||||
break;
|
||||
case 6:
|
||||
repaymentFrequencyType = SavingsInterestPeriodType.SEMIANNUAL;
|
||||
break;
|
||||
case 7:
|
||||
repaymentFrequencyType = SavingsInterestPeriodType.ANNUAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return repaymentFrequencyType;
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.savings.domain;
|
||||
|
||||
/**
|
||||
* An enumeration of supported calendar periods used in savings.
|
||||
*/
|
||||
public enum SavingsPeriodFrequencyType {
|
||||
DAYS(0, "savingsPeriodFrequencyType.days"), //
|
||||
WEEKS(1, "savingsPeriodFrequencyType.weeks"), //
|
||||
MONTHS(2, "savingsPeriodFrequencyType.months"), //
|
||||
YEARS(3, "savingsPeriodFrequencyType.years"), //
|
||||
INVALID(4, "savingsPeriodFrequencyType.invalid");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
|
||||
private SavingsPeriodFrequencyType(final Integer value, final String code) {
|
||||
this.value = value;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public static SavingsPeriodFrequencyType fromInt(final Integer type) {
|
||||
SavingsPeriodFrequencyType repaymentFrequencyType = SavingsPeriodFrequencyType.INVALID;
|
||||
if (type != null) {
|
||||
switch (type) {
|
||||
case 0:
|
||||
repaymentFrequencyType = SavingsPeriodFrequencyType.DAYS;
|
||||
break;
|
||||
case 1:
|
||||
repaymentFrequencyType = SavingsPeriodFrequencyType.WEEKS;
|
||||
break;
|
||||
case 2:
|
||||
repaymentFrequencyType = SavingsPeriodFrequencyType.MONTHS;
|
||||
break;
|
||||
case 3:
|
||||
repaymentFrequencyType = SavingsPeriodFrequencyType.YEARS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return repaymentFrequencyType;
|
||||
}
|
||||
}
|
||||
+63
-10
@@ -8,6 +8,9 @@ package org.mifosplatform.portfolio.savings.domain;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.currencyCodeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.descriptionParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.digitsAfterDecimalParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestPeriodTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestRateParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestRatePeriodFrequencyTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.localeParamName;
|
||||
@@ -51,6 +54,28 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
@Column(name = "nominal_interest_rate_period_frequency_enum", nullable = false)
|
||||
private Integer interestRatePeriodFrequencyType;
|
||||
|
||||
/**
|
||||
* The interest period is the span of time at the end of which savings in a
|
||||
* client’s account earn interest.
|
||||
*
|
||||
* A value from the {@link SavingsInterestPeriodType} enumeration.
|
||||
*/
|
||||
@Column(name = "interest_period_enum", nullable = false)
|
||||
private Integer interestPeriodType;
|
||||
|
||||
/**
|
||||
* A value from the {@link SavingsInterestCalculationType} enumeration.
|
||||
*/
|
||||
@Column(name = "interest_calculation_type_enum", nullable = false)
|
||||
private Integer interestCalculationType;
|
||||
|
||||
/**
|
||||
* A value from the {@link SavingsInterestCalculationDaysInYearType}
|
||||
* enumeration.
|
||||
*/
|
||||
@Column(name = "interest_calculation_days_in_year_type_enum", nullable = false)
|
||||
private Integer interestCalculationDaysInYearType;
|
||||
|
||||
@Column(name = "min_required_opening_balance", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal minRequiredOpeningBalance;
|
||||
|
||||
@@ -61,12 +86,15 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
private Integer lockinPeriodFrequencyType;
|
||||
|
||||
public static SavingsProduct createNew(final String name, final String description, final MonetaryCurrency currency,
|
||||
final BigDecimal interestRate, final PeriodFrequencyType interestRatePeriodFrequencyType,
|
||||
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency,
|
||||
final BigDecimal interestRate, final SavingsPeriodFrequencyType interestRatePeriodFrequencyType,
|
||||
final SavingsInterestPeriodType interestPeriodType, final SavingsInterestCalculationType interestCalculationType,
|
||||
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
|
||||
final Integer lockinPeriodFrequency,
|
||||
final PeriodFrequencyType lockinPeriodFrequencyType) {
|
||||
|
||||
return new SavingsProduct(name, description, currency, interestRate, interestRatePeriodFrequencyType, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType);
|
||||
return new SavingsProduct(name, description, currency, interestRate, interestRatePeriodFrequencyType, interestPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType);
|
||||
}
|
||||
|
||||
protected SavingsProduct() {
|
||||
@@ -75,7 +103,9 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
private SavingsProduct(final String name, final String description, final MonetaryCurrency currency, final BigDecimal interestRate,
|
||||
final PeriodFrequencyType interestRatePeriodFrequencyType, final BigDecimal minRequiredOpeningBalance,
|
||||
final SavingsPeriodFrequencyType interestRatePeriodFrequencyType, final SavingsInterestPeriodType interestPeriodType,
|
||||
final SavingsInterestCalculationType interestCalculationType,
|
||||
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
|
||||
final Integer lockinPeriodFrequency, final PeriodFrequencyType lockinPeriodFrequencyType) {
|
||||
|
||||
this.name = name;
|
||||
@@ -84,6 +114,9 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
this.currency = currency;
|
||||
this.interestRate = interestRate;
|
||||
this.interestRatePeriodFrequencyType = interestRatePeriodFrequencyType.getValue();
|
||||
this.interestPeriodType = interestPeriodType.getValue();
|
||||
this.interestCalculationType = interestCalculationType.getValue();
|
||||
this.interestCalculationDaysInYearType = interestCalculationDaysInYearType.getValue();
|
||||
|
||||
if (minRequiredOpeningBalance != null) {
|
||||
this.minRequiredOpeningBalance = Money.of(currency, minRequiredOpeningBalance).getAmount();
|
||||
@@ -174,7 +207,25 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
if (command.isChangeInIntegerParameterNamed(interestRatePeriodFrequencyTypeParamName, this.interestRatePeriodFrequencyType)) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(interestRatePeriodFrequencyTypeParamName);
|
||||
actualChanges.put(interestRatePeriodFrequencyTypeParamName, newValue);
|
||||
this.interestRatePeriodFrequencyType = PeriodFrequencyType.fromInt(newValue).getValue();
|
||||
this.interestRatePeriodFrequencyType = SavingsPeriodFrequencyType.fromInt(newValue).getValue();
|
||||
}
|
||||
|
||||
if (command.isChangeInIntegerParameterNamed(interestPeriodTypeParamName, this.interestPeriodType)) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(interestPeriodTypeParamName);
|
||||
actualChanges.put(interestPeriodTypeParamName, newValue);
|
||||
this.interestPeriodType = SavingsInterestPeriodType.fromInt(newValue).getValue();
|
||||
}
|
||||
|
||||
if (command.isChangeInIntegerParameterNamed(interestCalculationTypeParamName, this.interestCalculationType)) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(interestCalculationTypeParamName);
|
||||
actualChanges.put(interestCalculationTypeParamName, newValue);
|
||||
this.interestCalculationType = SavingsInterestCalculationType.fromInt(newValue).getValue();
|
||||
}
|
||||
|
||||
if (command.isChangeInIntegerParameterNamed(interestCalculationDaysInYearTypeParamName, this.interestCalculationDaysInYearType)) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(interestCalculationDaysInYearTypeParamName);
|
||||
actualChanges.put(interestCalculationDaysInYearTypeParamName, newValue);
|
||||
this.interestCalculationDaysInYearType = SavingsInterestCalculationDaysInYearType.fromInt(newValue).getValue();
|
||||
}
|
||||
|
||||
if (command.isChangeInBigDecimalParameterNamed(minRequiredOpeningBalanceParamName, this.minRequiredOpeningBalance)) {
|
||||
@@ -194,7 +245,7 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
if (command.isChangeInIntegerParameterNamed(lockinPeriodFrequencyTypeParamName, this.lockinPeriodFrequencyType)) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(lockinPeriodFrequencyTypeParamName);
|
||||
actualChanges.put(lockinPeriodFrequencyTypeParamName, newValue);
|
||||
this.lockinPeriodFrequencyType = PeriodFrequencyType.fromInt(newValue).getValue();
|
||||
this.lockinPeriodFrequencyType = SavingsPeriodFrequencyType.fromInt(newValue).getValue();
|
||||
}
|
||||
|
||||
validateLockinDetails();
|
||||
@@ -203,9 +254,11 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
private void validateLockinDetails() {
|
||||
if (isInvalidConfigurationOfLockinSettings()) { throw new InvalidSavingsProductSettingsException(
|
||||
"error.msg.product.savings.invalid.lockin.settings", "Invalid configuration of lock in settings.",
|
||||
lockinPeriodFrequencyParamName); }
|
||||
if (isInvalidConfigurationOfLockinSettings()) {
|
||||
//
|
||||
throw new InvalidSavingsProductSettingsException("error.msg.product.savings.invalid.lockin.settings",
|
||||
"Invalid configuration of lock in settings.", lockinPeriodFrequencyParamName);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInvalidConfigurationOfLockinSettings() {
|
||||
|
||||
+27
-4
@@ -3,6 +3,9 @@ package org.mifosplatform.portfolio.savings.domain;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.currencyCodeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.descriptionParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.digitsAfterDecimalParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestPeriodTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestRateParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestRatePeriodFrequencyTypeParamName;
|
||||
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyParamName;
|
||||
@@ -30,10 +33,29 @@ public class SavingsProductAssembler {
|
||||
MonetaryCurrency currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal);
|
||||
|
||||
final BigDecimal interestRate = command.bigDecimalValueOfParameterNamed(interestRateParamName);
|
||||
PeriodFrequencyType interestRatePeriodFrequencyType = null;
|
||||
SavingsPeriodFrequencyType interestRatePeriodFrequencyType = null;
|
||||
final Integer interestRatePeriodFrequencyTypeValue = command.integerValueOfParameterNamed(interestRatePeriodFrequencyTypeParamName);
|
||||
if (interestRatePeriodFrequencyTypeValue != null) {
|
||||
interestRatePeriodFrequencyType = PeriodFrequencyType.fromInt(interestRatePeriodFrequencyTypeValue);
|
||||
interestRatePeriodFrequencyType = SavingsPeriodFrequencyType.fromInt(interestRatePeriodFrequencyTypeValue);
|
||||
}
|
||||
|
||||
SavingsInterestPeriodType interestPeriodType = null;
|
||||
final Integer interestPeriodTypeValue = command.integerValueOfParameterNamed(interestPeriodTypeParamName);
|
||||
if (interestPeriodTypeValue != null) {
|
||||
interestPeriodType = SavingsInterestPeriodType.fromInt(interestPeriodTypeValue);
|
||||
}
|
||||
|
||||
SavingsInterestCalculationType interestCalculationType = null;
|
||||
final Integer interestCalculationTypeValue = command.integerValueOfParameterNamed(interestCalculationTypeParamName);
|
||||
if (interestCalculationTypeValue != null) {
|
||||
interestCalculationType = SavingsInterestCalculationType.fromInt(interestCalculationTypeValue);
|
||||
}
|
||||
|
||||
SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType = null;
|
||||
final Integer interestCalculationDaysInYearTypeValue = command
|
||||
.integerValueOfParameterNamed(interestCalculationDaysInYearTypeParamName);
|
||||
if (interestCalculationDaysInYearTypeValue != null) {
|
||||
interestCalculationDaysInYearType = SavingsInterestCalculationDaysInYearType.fromInt(interestCalculationDaysInYearTypeValue);
|
||||
}
|
||||
|
||||
final BigDecimal minRequiredOpeningBalance = command.bigDecimalValueOfParameterNamed(minRequiredOpeningBalanceParamName);
|
||||
@@ -45,7 +67,8 @@ public class SavingsProductAssembler {
|
||||
lockinPeriodFrequencyType = PeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue);
|
||||
}
|
||||
|
||||
return SavingsProduct.createNew(name, description, currency, interestRate, interestRatePeriodFrequencyType,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType);
|
||||
return SavingsProduct.createNew(name, description, currency, interestRate, interestRatePeriodFrequencyType, interestPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType);
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package org.mifosplatform.portfolio.savings.handler;
|
||||
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class CalculateInterestSavingsAccountCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService savingAccountWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CalculateInterestSavingsAccountCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) {
|
||||
this.savingAccountWritePlatformService = savingAccountWritePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.savingAccountWritePlatformService.calculateInterest(command.getSavingsId(), command);
|
||||
}
|
||||
}
|
||||
+64
-12
@@ -21,13 +21,16 @@ import org.mifosplatform.portfolio.client.data.ClientData;
|
||||
import org.mifosplatform.portfolio.client.service.ClientReadPlatformService;
|
||||
import org.mifosplatform.portfolio.group.data.GroupData;
|
||||
import org.mifosplatform.portfolio.group.service.GroupReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountData;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountStatusEnumData;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountSummaryData;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionData;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionEnumData;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsProductData;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationDaysInYearType;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationType;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsInterestPeriodType;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsPeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.savings.exception.SavingsAccountNotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
@@ -103,11 +106,15 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
sqlBuilder.append("sa.nominal_interest_rate_per_period as interestRate, ");
|
||||
sqlBuilder.append("sa.nominal_interest_rate_period_frequency_enum as interestRatePeriodFrequencyType, ");
|
||||
sqlBuilder.append("sa.annual_nominal_interest_rate as annualInterestRate, ");
|
||||
sqlBuilder.append("sa.interest_period_enum as interestPeriodType, ");
|
||||
sqlBuilder.append("sa.interest_calculation_type_enum as interestCalculationType, ");
|
||||
sqlBuilder.append("sa.interest_calculation_days_in_year_type_enum as interestCalculationDaysInYearType, ");
|
||||
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.total_deposits_derived as totalDeposits, ");
|
||||
sqlBuilder.append("sa.total_withdrawals_derived as totalWithdrawals, ");
|
||||
sqlBuilder.append("sa.total_interest_earned_derived as totalInterestEarned, ");
|
||||
sqlBuilder.append("sa.total_interest_posted_derived as totalInterestPosted, ");
|
||||
sqlBuilder.append("sa.account_balance_derived as accountBalance ");
|
||||
sqlBuilder.append("from m_savings_account sa ");
|
||||
@@ -152,31 +159,45 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
currencyNameCode);
|
||||
|
||||
final BigDecimal interestRate = rs.getBigDecimal("interestRate");
|
||||
final EnumOptionData interestRatePeriodFrequencyType = SavingsEnumerations.interestRatePeriodFrequencyType(PeriodFrequencyType
|
||||
.fromInt(JdbcSupport.getInteger(rs, "interestRatePeriodFrequencyType")));
|
||||
final SavingsPeriodFrequencyType interestRatePeriodType = SavingsPeriodFrequencyType.fromInt(JdbcSupport.getInteger(rs,
|
||||
"interestRatePeriodFrequencyType"));
|
||||
final EnumOptionData interestRatePeriodFrequencyType = SavingsEnumerations
|
||||
.interestRatePeriodFrequencyType(interestRatePeriodType);
|
||||
final BigDecimal annualInterestRate = rs.getBigDecimal("annualInterestRate");
|
||||
|
||||
EnumOptionData interestPeriodType = SavingsEnumerations.interestPeriodType(SavingsInterestPeriodType.fromInt(JdbcSupport
|
||||
.getInteger(rs, "interestPeriodType")));
|
||||
|
||||
EnumOptionData interestCalculationType = SavingsEnumerations.interestCalculationType(SavingsInterestCalculationType
|
||||
.fromInt(JdbcSupport.getInteger(rs, "interestCalculationType")));
|
||||
|
||||
EnumOptionData interestCalculationDaysInYearType = SavingsEnumerations
|
||||
.interestCalculationDaysInYearType(SavingsInterestCalculationDaysInYearType.fromInt(JdbcSupport.getInteger(rs,
|
||||
"interestCalculationDaysInYearType")));
|
||||
|
||||
final BigDecimal minRequiredOpeningBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredOpeningBalance");
|
||||
|
||||
final Integer lockinPeriodFrequency = JdbcSupport.getInteger(rs, "lockinPeriodFrequency");
|
||||
EnumOptionData lockinPeriodFrequencyType = null;
|
||||
final Integer lockinPeriodFrequencyTypeValue = JdbcSupport.getInteger(rs, "lockinPeriodFrequencyType");
|
||||
if (lockinPeriodFrequencyTypeValue != null) {
|
||||
lockinPeriodFrequencyType = SavingsEnumerations.interestRatePeriodFrequencyType(PeriodFrequencyType
|
||||
.fromInt(lockinPeriodFrequencyTypeValue));
|
||||
final SavingsPeriodFrequencyType lockinPeriodType = SavingsPeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue);
|
||||
lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodType);
|
||||
}
|
||||
|
||||
final BigDecimal totalDeposits = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalDeposits");
|
||||
final BigDecimal totalWithdrawals = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalWithdrawals");
|
||||
final BigDecimal totalInterestEarned = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalInterestEarned");
|
||||
final BigDecimal totalInterestPosted = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalInterestPosted");
|
||||
final BigDecimal accountBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "accountBalance");
|
||||
|
||||
final SavingsAccountSummaryData summary = new SavingsAccountSummaryData(currency, totalDeposits, totalWithdrawals,
|
||||
totalInterestPosted, accountBalance);
|
||||
totalInterestEarned, totalInterestPosted, accountBalance);
|
||||
|
||||
return SavingsAccountData.instance(id, accountNo, externalId, status, activationDate, groupId, groupName, clientId, clientName,
|
||||
productId, productName, currency, interestRate, interestRatePeriodFrequencyType, annualInterestRate,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, summary);
|
||||
interestPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, summary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,11 +227,22 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
|
||||
final Collection<EnumOptionData> interestRatePeriodFrequencyTypeOptions = this.dropdownReadPlatformService
|
||||
.retrieveInterestRatePeriodFrequencyTypeOptions();
|
||||
|
||||
final Collection<EnumOptionData> interestPeriodTypeOptions = this.dropdownReadPlatformService
|
||||
.retrieveInterestPeriodTypeOptions();
|
||||
|
||||
final Collection<EnumOptionData> interestCalculationTypeOptions = this.dropdownReadPlatformService
|
||||
.retrieveInterestCalculationTypeOptions();
|
||||
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = this.dropdownReadPlatformService
|
||||
.retrieveInterestCalculationDaysInYearTypeOptions();
|
||||
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = this.dropdownReadPlatformService
|
||||
.retrieveLockinPeriodFrequencyTypeOptions();
|
||||
final Collection<SavingsAccountTransactionData> transactions = null;
|
||||
|
||||
template = SavingsAccountData.withTemplateOptions(template, productOptions, interestRatePeriodFrequencyTypeOptions,
|
||||
interestPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
|
||||
lockinPeriodFrequencyTypeOptions, transactions);
|
||||
} else {
|
||||
|
||||
@@ -227,10 +259,14 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
template = SavingsAccountData.withClientTemplate(clientId, clientName, groupId, groupName);
|
||||
|
||||
final Collection<EnumOptionData> interestRatePeriodFrequencyTypeOptions = null;
|
||||
final Collection<EnumOptionData> interestPeriodTypeOptions = null;
|
||||
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
|
||||
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
|
||||
final Collection<SavingsAccountTransactionData> transactions = null;
|
||||
|
||||
template = SavingsAccountData.withTemplateOptions(template, productOptions, interestRatePeriodFrequencyTypeOptions,
|
||||
interestPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
|
||||
lockinPeriodFrequencyTypeOptions, transactions);
|
||||
}
|
||||
|
||||
@@ -364,6 +400,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, ");
|
||||
sqlBuilder.append("sp.nominal_interest_rate_per_period as interestRate, ");
|
||||
sqlBuilder.append("sp.nominal_interest_rate_period_frequency_enum as interestRatePeriodFrequencyType, ");
|
||||
sqlBuilder.append("sp.interest_period_enum as interestPeriodType, ");
|
||||
sqlBuilder.append("sp.interest_calculation_type_enum as interestCalculationType, ");
|
||||
sqlBuilder.append("sp.interest_calculation_days_in_year_type_enum as interestCalculationDaysInYearType, ");
|
||||
sqlBuilder.append("sp.min_required_opening_balance as minRequiredOpeningBalance, ");
|
||||
sqlBuilder.append("sp.lockin_period_frequency as lockinPeriodFrequency, ");
|
||||
sqlBuilder.append("sp.lockin_period_frequency_enum as lockinPeriodFrequencyType ");
|
||||
@@ -392,18 +431,30 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
currencyNameCode);
|
||||
|
||||
final BigDecimal interestRate = rs.getBigDecimal("interestRate");
|
||||
final EnumOptionData interestRatePeriodFrequencyType = SavingsEnumerations.interestRatePeriodFrequencyType(PeriodFrequencyType
|
||||
.fromInt(JdbcSupport.getInteger(rs, "interestRatePeriodFrequencyType")));
|
||||
final SavingsPeriodFrequencyType interestRatePeriodType = SavingsPeriodFrequencyType.fromInt(JdbcSupport.getInteger(rs,
|
||||
"interestRatePeriodFrequencyType"));
|
||||
final EnumOptionData interestRatePeriodFrequencyType = SavingsEnumerations
|
||||
.interestRatePeriodFrequencyType(interestRatePeriodType);
|
||||
final BigDecimal annualInterestRate = null;
|
||||
|
||||
EnumOptionData interestPeriodType = SavingsEnumerations.interestPeriodType(SavingsInterestPeriodType.fromInt(JdbcSupport
|
||||
.getInteger(rs, "interestPeriodType")));
|
||||
|
||||
EnumOptionData interestCalculationType = SavingsEnumerations.interestCalculationType(SavingsInterestCalculationType
|
||||
.fromInt(JdbcSupport.getInteger(rs, "interestCalculationType")));
|
||||
|
||||
EnumOptionData interestCalculationDaysInYearType = SavingsEnumerations
|
||||
.interestCalculationDaysInYearType(SavingsInterestCalculationDaysInYearType.fromInt(JdbcSupport.getInteger(rs,
|
||||
"interestCalculationDaysInYearType")));
|
||||
|
||||
final BigDecimal minRequiredOpeningBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredOpeningBalance");
|
||||
|
||||
final Integer lockinPeriodFrequency = JdbcSupport.getInteger(rs, "lockinPeriodFrequency");
|
||||
EnumOptionData lockinPeriodFrequencyType = null;
|
||||
final Integer lockinPeriodFrequencyTypeValue = JdbcSupport.getInteger(rs, "lockinPeriodFrequencyType");
|
||||
if (lockinPeriodFrequencyTypeValue != null) {
|
||||
lockinPeriodFrequencyType = SavingsEnumerations.interestRatePeriodFrequencyType(PeriodFrequencyType
|
||||
.fromInt(lockinPeriodFrequencyTypeValue));
|
||||
final SavingsPeriodFrequencyType lockinPeriodType = SavingsPeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue);
|
||||
lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodType);
|
||||
}
|
||||
|
||||
Long clientId = null;
|
||||
@@ -425,7 +476,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
final SavingsAccountSummaryData summary = null;
|
||||
return SavingsAccountData.instance(null, null, null, status, activationDate, groupId, groupName, clientId, clientName,
|
||||
productId, productName, currency, interestRate, interestRatePeriodFrequencyType, annualInterestRate,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, summary);
|
||||
interestPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, summary);
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
@@ -21,4 +21,6 @@ public interface SavingsAccountWritePlatformService {
|
||||
CommandProcessingResult deposit(Long savingsId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult withdrawal(Long savingsId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult calculateInterest(Long savingsId, JsonCommand command);
|
||||
}
|
||||
+18
@@ -314,4 +314,22 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
.withSavingsId(savingsId) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult calculateInterest(final Long savingsId, final JsonCommand command) {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final SavingsAccount account = this.savingAccountRepository.findOneWithNotFoundDetection(savingsId);
|
||||
|
||||
account.calculateInterest();
|
||||
this.savingAccountRepository.save(account);
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(savingsId) //
|
||||
.withOfficeId(account.officeId()) //
|
||||
.withClientId(account.clientId()) //
|
||||
.withGroupId(account.groupId()) //
|
||||
.withSavingsId(savingsId) //
|
||||
.build();
|
||||
}
|
||||
}
|
||||
+6
@@ -9,4 +9,10 @@ public interface SavingsDropdownReadPlatformService {
|
||||
Collection<EnumOptionData> retrieveInterestRatePeriodFrequencyTypeOptions();
|
||||
|
||||
Collection<EnumOptionData> retrieveLockinPeriodFrequencyTypeOptions();
|
||||
|
||||
Collection<EnumOptionData> retrieveInterestPeriodTypeOptions();
|
||||
|
||||
Collection<EnumOptionData> retrieveInterestCalculationTypeOptions();
|
||||
|
||||
Collection<EnumOptionData> retrieveInterestCalculationDaysInYearTypeOptions();
|
||||
}
|
||||
+46
-7
@@ -1,10 +1,14 @@
|
||||
package org.mifosplatform.portfolio.savings.service;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationDaysInYearType;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationType;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsInterestPeriodType;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsPeriodFrequencyType;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@@ -13,8 +17,8 @@ public class SavingsDropdownReadPlatformServiceImpl implements SavingsDropdownRe
|
||||
@Override
|
||||
public List<EnumOptionData> retrieveInterestRatePeriodFrequencyTypeOptions() {
|
||||
List<EnumOptionData> allowedInterestRatePeriodFrequencyTypeOptions = Arrays.asList( //
|
||||
SavingsEnumerations.interestRatePeriodFrequencyType(PeriodFrequencyType.MONTHS), //
|
||||
SavingsEnumerations.interestRatePeriodFrequencyType(PeriodFrequencyType.YEARS) //
|
||||
SavingsEnumerations.interestRatePeriodFrequencyType(SavingsPeriodFrequencyType.MONTHS), //
|
||||
SavingsEnumerations.interestRatePeriodFrequencyType(SavingsPeriodFrequencyType.YEARS) //
|
||||
);
|
||||
|
||||
return allowedInterestRatePeriodFrequencyTypeOptions;
|
||||
@@ -23,12 +27,47 @@ public class SavingsDropdownReadPlatformServiceImpl implements SavingsDropdownRe
|
||||
@Override
|
||||
public List<EnumOptionData> retrieveLockinPeriodFrequencyTypeOptions() {
|
||||
List<EnumOptionData> allowedLockinPeriodFrequencyTypeOptions = Arrays.asList( //
|
||||
SavingsEnumerations.lockinPeriodFrequencyType(PeriodFrequencyType.DAYS), //
|
||||
SavingsEnumerations.lockinPeriodFrequencyType(PeriodFrequencyType.WEEKS), //
|
||||
SavingsEnumerations.lockinPeriodFrequencyType(PeriodFrequencyType.MONTHS), //
|
||||
SavingsEnumerations.lockinPeriodFrequencyType(PeriodFrequencyType.YEARS) //
|
||||
SavingsEnumerations.lockinPeriodFrequencyType(SavingsPeriodFrequencyType.DAYS), //
|
||||
SavingsEnumerations.lockinPeriodFrequencyType(SavingsPeriodFrequencyType.WEEKS), //
|
||||
SavingsEnumerations.lockinPeriodFrequencyType(SavingsPeriodFrequencyType.MONTHS), //
|
||||
SavingsEnumerations.lockinPeriodFrequencyType(SavingsPeriodFrequencyType.YEARS) //
|
||||
);
|
||||
|
||||
return allowedLockinPeriodFrequencyTypeOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<EnumOptionData> retrieveInterestPeriodTypeOptions() {
|
||||
List<EnumOptionData> allowedOptions = Arrays.asList( //
|
||||
SavingsEnumerations.interestPeriodType(SavingsInterestPeriodType.DAILY), //
|
||||
SavingsEnumerations.interestPeriodType(SavingsInterestPeriodType.WEEKLY), //
|
||||
SavingsEnumerations.interestPeriodType(SavingsInterestPeriodType.BIWEEKLY), //
|
||||
SavingsEnumerations.interestPeriodType(SavingsInterestPeriodType.MONTHLY), //
|
||||
SavingsEnumerations.interestPeriodType(SavingsInterestPeriodType.QUATERLY), //
|
||||
SavingsEnumerations.interestPeriodType(SavingsInterestPeriodType.SEMIANNUAL), //
|
||||
SavingsEnumerations.interestPeriodType(SavingsInterestPeriodType.ANNUAL) //
|
||||
);
|
||||
|
||||
return allowedOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<EnumOptionData> retrieveInterestCalculationTypeOptions() {
|
||||
List<EnumOptionData> allowedOptions = Arrays.asList( //
|
||||
SavingsEnumerations.interestCalculationType(SavingsInterestCalculationType.DAILY_BALANCE), //
|
||||
SavingsEnumerations.interestCalculationType(SavingsInterestCalculationType.AVERAGE_DAILY_BALANCE) //
|
||||
);
|
||||
|
||||
return allowedOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<EnumOptionData> retrieveInterestCalculationDaysInYearTypeOptions() {
|
||||
List<EnumOptionData> allowedOptions = Arrays.asList( //
|
||||
SavingsEnumerations.interestCalculationDaysInYearType(SavingsInterestCalculationDaysInYearType.DAYS_360), //
|
||||
SavingsEnumerations.interestCalculationDaysInYearType(SavingsInterestCalculationDaysInYearType.DAYS_365) //
|
||||
);
|
||||
|
||||
return allowedOptions;
|
||||
}
|
||||
}
|
||||
+123
-25
@@ -6,71 +6,74 @@
|
||||
package org.mifosplatform.portfolio.savings.service;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountStatusEnumData;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionEnumData;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccountStatusType;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransactionType;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationDaysInYearType;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationType;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsInterestPeriodType;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsPeriodFrequencyType;
|
||||
|
||||
public class SavingsEnumerations {
|
||||
|
||||
public static EnumOptionData interestRatePeriodFrequencyType(final int id) {
|
||||
return interestRatePeriodFrequencyType(PeriodFrequencyType.fromInt(id));
|
||||
return interestRatePeriodFrequencyType(SavingsPeriodFrequencyType.fromInt(id));
|
||||
}
|
||||
|
||||
public static EnumOptionData interestRatePeriodFrequencyType(final PeriodFrequencyType type) {
|
||||
public static EnumOptionData interestRatePeriodFrequencyType(final SavingsPeriodFrequencyType type) {
|
||||
final String codePrefix = "savings.interest.rate.";
|
||||
EnumOptionData optionData = new EnumOptionData(PeriodFrequencyType.INVALID.getValue().longValue(),
|
||||
PeriodFrequencyType.INVALID.getCode(), "Invalid");
|
||||
EnumOptionData optionData = new EnumOptionData(SavingsPeriodFrequencyType.INVALID.getValue().longValue(),
|
||||
SavingsPeriodFrequencyType.INVALID.getCode(), "Invalid");
|
||||
switch (type) {
|
||||
case INVALID:
|
||||
break;
|
||||
case DAYS:
|
||||
optionData = new EnumOptionData(PeriodFrequencyType.DAYS.getValue().longValue(), codePrefix
|
||||
+ PeriodFrequencyType.DAYS.getCode(), "Per day");
|
||||
optionData = new EnumOptionData(SavingsPeriodFrequencyType.DAYS.getValue().longValue(), codePrefix
|
||||
+ SavingsPeriodFrequencyType.DAYS.getCode(), "Per day");
|
||||
break;
|
||||
case WEEKS:
|
||||
optionData = new EnumOptionData(PeriodFrequencyType.WEEKS.getValue().longValue(), codePrefix
|
||||
+ PeriodFrequencyType.WEEKS.getCode(), "Per week");
|
||||
optionData = new EnumOptionData(SavingsPeriodFrequencyType.WEEKS.getValue().longValue(), codePrefix
|
||||
+ SavingsPeriodFrequencyType.WEEKS.getCode(), "Per week");
|
||||
break;
|
||||
case MONTHS:
|
||||
optionData = new EnumOptionData(PeriodFrequencyType.MONTHS.getValue().longValue(), codePrefix
|
||||
+ PeriodFrequencyType.MONTHS.getCode(), "Per month");
|
||||
optionData = new EnumOptionData(SavingsPeriodFrequencyType.MONTHS.getValue().longValue(), codePrefix
|
||||
+ SavingsPeriodFrequencyType.MONTHS.getCode(), "Per month");
|
||||
break;
|
||||
case YEARS:
|
||||
optionData = new EnumOptionData(PeriodFrequencyType.YEARS.getValue().longValue(), codePrefix
|
||||
+ PeriodFrequencyType.YEARS.getCode(), "Per year");
|
||||
optionData = new EnumOptionData(SavingsPeriodFrequencyType.YEARS.getValue().longValue(), codePrefix
|
||||
+ SavingsPeriodFrequencyType.YEARS.getCode(), "Per year");
|
||||
break;
|
||||
}
|
||||
return optionData;
|
||||
}
|
||||
|
||||
public static EnumOptionData lockinPeriodFrequencyType(final int id) {
|
||||
return lockinPeriodFrequencyType(PeriodFrequencyType.fromInt(id));
|
||||
return lockinPeriodFrequencyType(SavingsPeriodFrequencyType.fromInt(id));
|
||||
}
|
||||
|
||||
public static EnumOptionData lockinPeriodFrequencyType(final PeriodFrequencyType type) {
|
||||
public static EnumOptionData lockinPeriodFrequencyType(final SavingsPeriodFrequencyType type) {
|
||||
final String codePrefix = "savings.lockin.";
|
||||
EnumOptionData optionData = new EnumOptionData(PeriodFrequencyType.INVALID.getValue().longValue(),
|
||||
PeriodFrequencyType.INVALID.getCode(), "Invalid");
|
||||
EnumOptionData optionData = new EnumOptionData(SavingsPeriodFrequencyType.INVALID.getValue().longValue(),
|
||||
SavingsPeriodFrequencyType.INVALID.getCode(), "Invalid");
|
||||
switch (type) {
|
||||
case INVALID:
|
||||
break;
|
||||
case DAYS:
|
||||
optionData = new EnumOptionData(PeriodFrequencyType.DAYS.getValue().longValue(), codePrefix
|
||||
+ PeriodFrequencyType.DAYS.getCode(), "Days");
|
||||
optionData = new EnumOptionData(SavingsPeriodFrequencyType.DAYS.getValue().longValue(), codePrefix
|
||||
+ SavingsPeriodFrequencyType.DAYS.getCode(), "Days");
|
||||
break;
|
||||
case WEEKS:
|
||||
optionData = new EnumOptionData(PeriodFrequencyType.WEEKS.getValue().longValue(), codePrefix
|
||||
+ PeriodFrequencyType.WEEKS.getCode(), "Weeks");
|
||||
optionData = new EnumOptionData(SavingsPeriodFrequencyType.WEEKS.getValue().longValue(), codePrefix
|
||||
+ SavingsPeriodFrequencyType.WEEKS.getCode(), "Weeks");
|
||||
break;
|
||||
case MONTHS:
|
||||
optionData = new EnumOptionData(PeriodFrequencyType.MONTHS.getValue().longValue(), codePrefix
|
||||
+ PeriodFrequencyType.MONTHS.getCode(), "Months");
|
||||
optionData = new EnumOptionData(SavingsPeriodFrequencyType.MONTHS.getValue().longValue(), codePrefix
|
||||
+ SavingsPeriodFrequencyType.MONTHS.getCode(), "Months");
|
||||
break;
|
||||
case YEARS:
|
||||
optionData = new EnumOptionData(PeriodFrequencyType.YEARS.getValue().longValue(), codePrefix
|
||||
+ PeriodFrequencyType.YEARS.getCode(), "Years");
|
||||
optionData = new EnumOptionData(SavingsPeriodFrequencyType.YEARS.getValue().longValue(), codePrefix
|
||||
+ SavingsPeriodFrequencyType.YEARS.getCode(), "Years");
|
||||
break;
|
||||
}
|
||||
return optionData;
|
||||
@@ -136,4 +139,99 @@ public class SavingsEnumerations {
|
||||
}
|
||||
return optionData;
|
||||
}
|
||||
|
||||
public static EnumOptionData interestPeriodType(final Integer type) {
|
||||
return interestPeriodType(SavingsInterestPeriodType.fromInt(type));
|
||||
}
|
||||
|
||||
public static EnumOptionData interestPeriodType(final SavingsInterestPeriodType type) {
|
||||
|
||||
final String codePrefix = "savings.interest.period.";
|
||||
EnumOptionData optionData = new EnumOptionData(SavingsInterestPeriodType.INVALID.getValue().longValue(),
|
||||
SavingsInterestPeriodType.INVALID.getCode(), "Invalid");
|
||||
|
||||
switch (type) {
|
||||
case INVALID:
|
||||
break;
|
||||
case DAILY:
|
||||
optionData = new EnumOptionData(SavingsInterestPeriodType.DAILY.getValue().longValue(), codePrefix
|
||||
+ SavingsInterestPeriodType.DAILY.getCode(), "Daily");
|
||||
break;
|
||||
case WEEKLY:
|
||||
optionData = new EnumOptionData(SavingsInterestPeriodType.WEEKLY.getValue().longValue(), codePrefix
|
||||
+ SavingsInterestPeriodType.WEEKLY.getCode(), "Weekly");
|
||||
break;
|
||||
case BIWEEKLY:
|
||||
optionData = new EnumOptionData(SavingsInterestPeriodType.BIWEEKLY.getValue().longValue(), codePrefix
|
||||
+ SavingsInterestPeriodType.BIWEEKLY.getCode(), "Bi-Weekly");
|
||||
break;
|
||||
case MONTHLY:
|
||||
optionData = new EnumOptionData(SavingsInterestPeriodType.MONTHLY.getValue().longValue(), codePrefix
|
||||
+ SavingsInterestPeriodType.MONTHLY.getCode(), "Monthly");
|
||||
break;
|
||||
case QUATERLY:
|
||||
optionData = new EnumOptionData(SavingsInterestPeriodType.QUATERLY.getValue().longValue(), codePrefix
|
||||
+ SavingsInterestPeriodType.QUATERLY.getCode(), "Quarterly");
|
||||
break;
|
||||
case SEMIANNUAL:
|
||||
optionData = new EnumOptionData(SavingsInterestPeriodType.SEMIANNUAL.getValue().longValue(), codePrefix
|
||||
+ SavingsInterestPeriodType.SEMIANNUAL.getCode(), "Semi-Annual");
|
||||
break;
|
||||
case ANNUAL:
|
||||
optionData = new EnumOptionData(SavingsInterestPeriodType.ANNUAL.getValue().longValue(), codePrefix
|
||||
+ SavingsInterestPeriodType.ANNUAL.getCode(), "Annually");
|
||||
break;
|
||||
}
|
||||
|
||||
return optionData;
|
||||
}
|
||||
|
||||
public static EnumOptionData interestCalculationType(final Integer type) {
|
||||
return interestCalculationType(SavingsInterestCalculationType.fromInt(type));
|
||||
}
|
||||
|
||||
public static EnumOptionData interestCalculationType(final SavingsInterestCalculationType type) {
|
||||
|
||||
EnumOptionData optionData = new EnumOptionData(SavingsInterestCalculationType.INVALID.getValue().longValue(),
|
||||
SavingsInterestCalculationType.INVALID.getCode(), "Invalid");
|
||||
|
||||
switch (type) {
|
||||
case INVALID:
|
||||
break;
|
||||
case DAILY_BALANCE:
|
||||
optionData = new EnumOptionData(SavingsInterestCalculationType.DAILY_BALANCE.getValue().longValue(),
|
||||
SavingsInterestCalculationType.DAILY_BALANCE.getCode(), "Daily Balance");
|
||||
break;
|
||||
case AVERAGE_DAILY_BALANCE:
|
||||
optionData = new EnumOptionData(SavingsInterestCalculationType.AVERAGE_DAILY_BALANCE.getValue().longValue(),
|
||||
SavingsInterestCalculationType.AVERAGE_DAILY_BALANCE.getCode(), "Average Daily Balance");
|
||||
break;
|
||||
}
|
||||
|
||||
return optionData;
|
||||
}
|
||||
|
||||
public static EnumOptionData interestCalculationDaysInYearType(final Integer type) {
|
||||
return interestCalculationDaysInYearType(SavingsInterestCalculationDaysInYearType.fromInt(type));
|
||||
}
|
||||
|
||||
public static EnumOptionData interestCalculationDaysInYearType(final SavingsInterestCalculationDaysInYearType type) {
|
||||
EnumOptionData optionData = new EnumOptionData(SavingsInterestCalculationDaysInYearType.INVALID.getValue().longValue(),
|
||||
SavingsInterestCalculationDaysInYearType.INVALID.getCode(), "Invalid");
|
||||
|
||||
switch (type) {
|
||||
case INVALID:
|
||||
break;
|
||||
case DAYS_360:
|
||||
optionData = new EnumOptionData(SavingsInterestCalculationDaysInYearType.DAYS_360.getValue().longValue(),
|
||||
SavingsInterestCalculationDaysInYearType.DAYS_360.getCode(), "360 Days");
|
||||
break;
|
||||
case DAYS_365:
|
||||
optionData = new EnumOptionData(SavingsInterestCalculationDaysInYearType.DAYS_365.getValue().longValue(),
|
||||
SavingsInterestCalculationDaysInYearType.DAYS_365.getCode(), "365 Days");
|
||||
break;
|
||||
}
|
||||
|
||||
return optionData;
|
||||
}
|
||||
}
|
||||
+46
-12
@@ -15,8 +15,11 @@ import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
|
||||
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.savings.data.SavingsProductData;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationDaysInYearType;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationType;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsInterestPeriodType;
|
||||
import org.mifosplatform.portfolio.savings.domain.SavingsPeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.savings.exception.SavingsProductNotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
@@ -69,15 +72,30 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
|
||||
private static final class SavingProductMapper implements RowMapper<SavingsProductData> {
|
||||
|
||||
private final String schemaSql;
|
||||
|
||||
public SavingProductMapper() {
|
||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||
sqlBuilder.append("sp.id as id, sp.name as name, sp.description as description, ");
|
||||
sqlBuilder.append("sp.currency_code as currencyCode, sp.currency_digits as currencyDigits, ");
|
||||
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
|
||||
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, ");
|
||||
sqlBuilder.append("sp.nominal_interest_rate_per_period as interestRate, ");
|
||||
sqlBuilder.append("sp.nominal_interest_rate_period_frequency_enum as interestRatePeriodFrequencyType, ");
|
||||
sqlBuilder.append("sp.interest_period_enum as interestPeriodType, ");
|
||||
sqlBuilder.append("sp.interest_calculation_type_enum as interestCalculationType, ");
|
||||
sqlBuilder.append("sp.interest_calculation_days_in_year_type_enum as interestCalculationDaysInYearType, ");
|
||||
sqlBuilder.append("sp.min_required_opening_balance as minRequiredOpeningBalance, ");
|
||||
sqlBuilder.append("sp.lockin_period_frequency as lockinPeriodFrequency,");
|
||||
sqlBuilder.append("sp.lockin_period_frequency_enum as lockinPeriodFrequencyType ");
|
||||
sqlBuilder.append("from m_savings_product sp ");
|
||||
sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code ");
|
||||
|
||||
this.schemaSql = sqlBuilder.toString();
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
return "sp.id as id,sp.name as name, sp.description as description, "
|
||||
+ "sp.currency_code as currencyCode, sp.currency_digits as currencyDigits, "
|
||||
+ "curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol, "
|
||||
+ "sp.nominal_interest_rate_per_period as interestRate, sp.nominal_interest_rate_period_frequency_enum as interestRatePeriodFrequencyType, "
|
||||
+ "sp.min_required_opening_balance as minRequiredOpeningBalance, "
|
||||
+ "sp.lockin_period_frequency as lockinPeriodFrequency, sp.lockin_period_frequency_enum as lockinPeriodFrequencyType " //
|
||||
+ "from m_savings_product sp " //
|
||||
+ "join m_currency curr on curr.code = sp.currency_code";
|
||||
return this.schemaSql;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,21 +114,37 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, currencyDisplaySymbol,
|
||||
currencyNameCode);
|
||||
BigDecimal interestRate = rs.getBigDecimal("interestRate");
|
||||
EnumOptionData interestRatePeriodFrequencyType = SavingsEnumerations.interestRatePeriodFrequencyType(PeriodFrequencyType
|
||||
|
||||
EnumOptionData interestRatePeriodFrequencyType = SavingsEnumerations.interestRatePeriodFrequencyType(SavingsPeriodFrequencyType
|
||||
.fromInt(JdbcSupport.getInteger(rs, "interestRatePeriodFrequencyType")));
|
||||
|
||||
EnumOptionData interestPeriodType = SavingsEnumerations.interestPeriodType(SavingsInterestPeriodType.fromInt(JdbcSupport
|
||||
.getInteger(rs, "interestPeriodType")));
|
||||
|
||||
EnumOptionData interestCalculationType = SavingsEnumerations.interestCalculationType(SavingsInterestCalculationType
|
||||
.fromInt(JdbcSupport.getInteger(rs, "interestCalculationType")));
|
||||
|
||||
EnumOptionData interestCalculationDaysInYearType = null;
|
||||
final Integer interestCalculationDaysInYearTypeValue = JdbcSupport.getInteger(rs, "interestCalculationDaysInYearType");
|
||||
if (interestCalculationDaysInYearTypeValue != null) {
|
||||
interestCalculationDaysInYearType = SavingsEnumerations
|
||||
.interestCalculationDaysInYearType(SavingsInterestCalculationDaysInYearType
|
||||
.fromInt(interestCalculationDaysInYearTypeValue));
|
||||
}
|
||||
|
||||
final BigDecimal minRequiredOpeningBalance = rs.getBigDecimal("minRequiredOpeningBalance");
|
||||
|
||||
Integer lockinPeriodFrequency = JdbcSupport.getInteger(rs, "lockinPeriodFrequency");
|
||||
EnumOptionData lockinPeriodFrequencyType = null;
|
||||
final Integer lockinPeriodFrequencyTypeValue = JdbcSupport.getInteger(rs, "lockinPeriodFrequencyType");
|
||||
if (lockinPeriodFrequencyTypeValue != null) {
|
||||
lockinPeriodFrequencyType = SavingsEnumerations.interestRatePeriodFrequencyType(PeriodFrequencyType
|
||||
lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(SavingsPeriodFrequencyType
|
||||
.fromInt(lockinPeriodFrequencyTypeValue));
|
||||
}
|
||||
|
||||
return SavingsProductData.instance(id, name, description, currency, interestRate, interestRatePeriodFrequencyType,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType);
|
||||
interestPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user