MIFOSX-541: add application process around savings accounts.

This commit is contained in:
Keith Woodlock
2013-07-16 00:43:34 +01:00
parent ee223dab61
commit ad51a6b62f
91 changed files with 3670 additions and 1722 deletions
@@ -0,0 +1,69 @@
ALTER TABLE `m_note`
ADD COLUMN `savings_account_id` BIGINT(20) NULL DEFAULT NULL AFTER `loan_transaction_id`,
ADD COLUMN `savings_account_transaction_id` BIGINT(20) NULL DEFAULT NULL AFTER `savings_account_id`;
ALTER TABLE `m_note` ADD CONSTRAINT `FK_savings_account_id` FOREIGN KEY (`savings_account_id` ) REFERENCES `m_savings_account` (`id` ) ON DELETE NO ACTION ON UPDATE NO ACTION, ADD INDEX `FK_savings_account_id` (`savings_account_id` ASC) ;
ALTER TABLE `m_savings_account_transaction`
CHANGE COLUMN `is_reversed` `is_reversed` TINYINT(1) NOT NULL AFTER `transaction_type_enum`,
CHANGE COLUMN `balance_end_date_derived` `balance_end_date_derived` DATE NULL DEFAULT NULL AFTER `amount`,
CHANGE COLUMN `balance_number_of_days_derived` `balance_number_of_days_derived` INT(11) NULL DEFAULT NULL AFTER `balance_end_date_derived`;
ALTER TABLE `m_savings_account`
ADD COLUMN `field_officer_id` bigint(20) DEFAULT NULL AFTER `product_id`,
ADD COLUMN `submittedon_date` DATE NOT NULL AFTER `status_enum`,
ADD COLUMN `submittedon_userid` bigint(20) DEFAULT NULL AFTER `submittedon_date`,
ADD COLUMN `approvedon_date` DATE DEFAULT NULL AFTER `submittedon_userid`,
ADD COLUMN `approvedon_userid` bigint(20) DEFAULT NULL AFTER `approvedon_date`,
ADD COLUMN `rejectedon_date` DATE DEFAULT NULL AFTER `approvedon_userid`,
ADD COLUMN `rejectedon_userid` bigint(20) DEFAULT NULL AFTER `rejectedon_date`,
ADD COLUMN `withdrawnon_date` DATE DEFAULT NULL AFTER `rejectedon_userid`,
ADD COLUMN `withdrawnon_userid` bigint(20) DEFAULT NULL AFTER `withdrawnon_date`;
ALTER TABLE `m_savings_account`
CHANGE COLUMN `activation_date` `activatedon_date` DATE NULL DEFAULT NULL AFTER `withdrawnon_userid`;
ALTER TABLE `m_savings_account`
ADD COLUMN `activatedon_userid` bigint(20) DEFAULT NULL AFTER `activatedon_date`,
ADD COLUMN `closedon_date` DATE DEFAULT NULL AFTER `activatedon_userid`,
ADD COLUMN `closedon_userid` bigint(20) DEFAULT NULL AFTER `closedon_date`;
UPDATE `m_savings_account`
SET
`submittedon_date`=`activatedon_date`,
`approvedon_date`=`activatedon_date`;
INSERT INTO `mifostenant-default`.`m_permission`
(`grouping`,`code`,`entity_name`,`action_name`,`can_maker_checker`)
VALUES
('transaction_savings', 'APPROVE_SAVINGSACCOUNT', 'SAVINGSACCOUNT', 'APPROVE', 1),
('transaction_savings', 'REJECT_SAVINGSACCOUNT', 'SAVINGSACCOUNT', 'REJECT', 1),
('transaction_savings', 'WITHDRAW_SAVINGSACCOUNT', 'SAVINGSACCOUNT', 'WITHDRAW', 1),
('transaction_savings', 'APPROVALUNDO_SAVINGSACCOUNT', 'SAVINGSACCOUNT', 'APPROVALUNDO', 1),
('transaction_savings', 'CLOSE_SAVINGSACCOUNT', 'SAVINGSACCOUNT', 'CLOSE', 1);
INSERT INTO `mifostenant-default`.`m_permission`
(`grouping`,`code`,`entity_name`,`action_name`,`can_maker_checker`)
VALUES
('transaction_savings', 'APPROVE_SAVINGSACCOUNT_CHECKER', 'SAVINGSACCOUNT', 'APPROVE', 0),
('transaction_savings', 'REJECT_SAVINGSACCOUNT_CHECKER', 'SAVINGSACCOUNT', 'REJECT', 0),
('transaction_savings', 'WITHDRAW_SAVINGSACCOUNT_CHECKER', 'SAVINGSACCOUNT', 'WITHDRAW', 0),
('transaction_savings', 'APPROVALUNDO_SAVINGSACCOUNT_CHECKER', 'SAVINGSACCOUNT', 'APPROVALUNDO', 0),
('transaction_savings', 'CLOSE_SAVINGSACCOUNT_CHECKER', 'SAVINGSACCOUNT', 'CLOSE', 0);
-- Remove permissions with 'in the past' permissions
DELETE FROM `m_permission` WHERE `id`='210';
DELETE FROM `m_permission` WHERE `id`='212';
DELETE FROM `m_permission` WHERE `id`='214';
DELETE FROM `m_permission` WHERE `id`='217';
DELETE FROM `m_permission` WHERE `id`='220';
DELETE FROM `m_permission` WHERE `id`='233';
DELETE FROM `m_permission` WHERE `id`='235';
DELETE FROM `m_permission` WHERE `id`='237';
DELETE FROM `m_permission` WHERE `id`='240';
DELETE FROM `m_permission` WHERE `id`='243';
+5 -4
View File
@@ -53,7 +53,7 @@ project.ext.springVersion = '3.2.3.RELEASE'
project.ext.springSecurityVersion = '3.1.4.RELEASE'
project.ext.hibernateCoreVersion = '4.1.9.Final'
project.ext.jerseyVersion = '1.17'
project.ext.springDataJpaVersion = '1.3.0.RELEASE'
project.ext.springDataJpaVersion = '1.3.2.RELEASE'
project.ext.mysqlUser='root'
project.ext.mysqlPassword='mysql'
@@ -123,8 +123,8 @@ dependencies {
[group: 'joda-time', name: 'joda-time', version: '2.1'],
[group: 'net.sourceforge.javacsv', name: 'javacsv', version: '2.0'],
[group: 'org.apache.commons', name: 'commons-email', version: '1.2'],
[group: 'commons-lang', name: 'commons-lang', version: '2.4'],
[group: 'org.apache.commons', name: 'commons-email', version: '1.3.1'],
[group: 'org.apache.commons', name: 'commons-lang3', version: '3.1'],
[group: 'org.slf4j', name: 'slf4j-api', version: '1.7.5'],
[group: 'org.slf4j', name: 'jcl-over-slf4j', version: '1.7.5'],
@@ -164,7 +164,8 @@ dependencies {
[group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.2.1']
)
testCompile 'junit:junit:4.11',
testCompile 'junit:junit:4.10',
'junit:junit-dep:4.10',
'org.mockito:mockito-all:1.9.5',
'com.jayway.restassured:rest-assured:1.7.2'
@@ -23,7 +23,8 @@ import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.*;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.*;
import com.google.gson.JsonElement;
@@ -21,7 +21,8 @@ import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.*;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.*;
import com.google.gson.JsonElement;
@@ -415,42 +415,6 @@ public class CommandWrapper {
return isUserResource() && isUpdate() && loggedInUserId.equals(this.entityId);
}
public boolean isDepositProductResource() {
return this.entityName.equalsIgnoreCase("DEPOSITPRODUCT");
}
public boolean isDepositAccountResource() {
return this.entityName.equalsIgnoreCase("DEPOSITACCOUNT");
}
public boolean isApprovalOfDeposit() {
return this.actionName.equalsIgnoreCase("APPROVE") && this.entityName.equalsIgnoreCase("DEPOSITACCOUNT");
}
public boolean isWithdrawDepositAmount() {
return this.actionName.equalsIgnoreCase("WITHDRAWAL") && this.entityName.equalsIgnoreCase("DEPOSITACCOUNT");
}
public boolean isWithdrawInterest() {
return this.actionName.equalsIgnoreCase("INTEREST") && this.entityName.equalsIgnoreCase("DEPOSITACCOUNT");
}
public boolean isRenewOfDepositApplicaion() {
return this.actionName.equalsIgnoreCase("RENEW") && this.entityName.equalsIgnoreCase("DEPOSITACCOUNT");
}
public boolean isRejectionOfDepositApplicaion() {
return this.actionName.equalsIgnoreCase("REJECT") && this.entityName.equalsIgnoreCase("DEPOSITACCOUNT");
}
public boolean isWithdrawByApplicant() {
return this.actionName.equalsIgnoreCase("WITHDRAW") && this.entityName.equalsIgnoreCase("DEPOSITACCOUNT");
}
public boolean isUndoApprovalOfDepositApplication() {
return this.actionName.equalsIgnoreCase("APPROVALUNDO") && this.entityName.equalsIgnoreCase("DEPOSITACCOUNT");
}
public boolean isSavingsProductResource() {
return this.entityName.equalsIgnoreCase("SAVINGSPRODUCT");
}
@@ -459,6 +423,22 @@ public class CommandWrapper {
return this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
}
public boolean isRejectionOfSavingsAccountApplication() {
return this.actionName.equalsIgnoreCase("REJECT") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
}
public boolean isWithdrawFromSavingsAccountApplicationByApplicant() {
return this.actionName.equalsIgnoreCase("WITHDRAW") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
}
public boolean isApprovalOfSavingsAccountApplication() {
return this.actionName.equalsIgnoreCase("APPROVE") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
}
public boolean isUndoApprovalOfSavingsAccountApplication() {
return this.actionName.equalsIgnoreCase("APPROVALUNDO") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
}
public boolean isSavingsAccountActivation() {
return this.actionName.equalsIgnoreCase("ACTIVATE") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
}
@@ -757,6 +757,42 @@ public class CommandWrapperBuilder {
return this;
}
public CommandWrapperBuilder rejectSavingsAccountApplication(final Long accountId) {
this.actionName = "REJECT";
this.entityName = "SAVINGSACCOUNT";
this.entityId = accountId;
this.savingsId = accountId;
this.href = "/savingsaccounts/" + accountId + "?command=reject";
return this;
}
public CommandWrapperBuilder withdrawSavingsAccountApplication(final Long accountId) {
this.actionName = "WITHDRAW";
this.entityName = "SAVINGSACCOUNT";
this.entityId = accountId;
this.savingsId = accountId;
this.href = "/savingsaccounts/" + accountId + "?command=withdrawnByApplicant";
return this;
}
public CommandWrapperBuilder approveSavingsAccountApplication(final Long accountId) {
this.actionName = "APPROVE";
this.entityName = "SAVINGSACCOUNT";
this.entityId = accountId;
this.savingsId = accountId;
this.href = "/savingsaccounts/" + accountId + "?command=approve";
return this;
}
public CommandWrapperBuilder undoSavingsAccountApplication(final Long accountId) {
this.actionName = "APPROVALUNDO";
this.entityName = "SAVINGSACCOUNT";
this.entityId = accountId;
this.savingsId = accountId;
this.href = "/savingsaccounts/" + accountId + "?command=undoapproval";
return this;
}
public CommandWrapperBuilder savingsAccountActivation(final Long accountId) {
this.actionName = "ACTIVATE";
this.entityName = "SAVINGSACCOUNT";
@@ -1036,7 +1072,7 @@ public class CommandWrapperBuilder {
return this;
}
public CommandWrapperBuilder assignRole(Long groupId) {
public CommandWrapperBuilder assignRole(final Long groupId) {
this.actionName = "ASSIGNROLE";
this.entityName = "GROUP";
this.groupId = groupId;
@@ -1045,7 +1081,7 @@ public class CommandWrapperBuilder {
return this;
}
public CommandWrapperBuilder unassignRole(Long groupId, Long roleId) {
public CommandWrapperBuilder unassignRole(final Long groupId, final Long roleId) {
this.actionName = "UNASSIGNROLE";
this.entityName = "GROUP";
this.groupId = groupId;
@@ -1054,7 +1090,7 @@ public class CommandWrapperBuilder {
return this;
}
public CommandWrapperBuilder updateRole(Long groupId, Long roleId) {
public CommandWrapperBuilder updateRole(final Long groupId, final Long roleId) {
this.actionName = "UPDATEROLE";
this.entityName = "GROUP";
this.groupId = groupId;
@@ -1063,7 +1099,7 @@ public class CommandWrapperBuilder {
return this;
}
public CommandWrapperBuilder unassignClientStaff(Long clientId) {
public CommandWrapperBuilder unassignClientStaff(final Long clientId) {
this.actionName = "UNASSIGNSTAFF";
this.entityName = "CLIENT";
this.entityId = clientId;
@@ -1072,7 +1108,7 @@ public class CommandWrapperBuilder {
return this;
}
public CommandWrapperBuilder assignClientStaff(Long clientId) {
public CommandWrapperBuilder assignClientStaff(final Long clientId) {
this.actionName = "ASSIGNSTAFF";
this.entityName = "CLIENT";
this.entityId = clientId;
@@ -374,11 +374,23 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
}
} else if (wrapper.isSavingsAccountResource()) {
if (wrapper.isCreate()) {
handler = this.applicationContext.getBean("createSavingsAccountCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext
.getBean("savingsAccountApplicationSubmittalCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) {
handler = this.applicationContext.getBean("updateSavingsAccountCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("savingsAccountApplicationModificationCommandHandler",
NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) {
handler = this.applicationContext.getBean("deleteSavingsAccountCommandHandler", NewCommandSourceHandler.class);
handler = this.applicationContext.getBean("savingsAccountApplicationDeletionCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isRejectionOfSavingsAccountApplication()) {
handler = this.applicationContext.getBean("savingsAccountApplicationRejectedCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isWithdrawFromSavingsAccountApplicationByApplicant()) {
handler = this.applicationContext.getBean("savingsAccountApplicationWithdrawnByApplicantCommandHandler",
NewCommandSourceHandler.class);
} else if (wrapper.isApprovalOfSavingsAccountApplication()) {
handler = this.applicationContext.getBean("savingsAccountApplicationApprovalCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUndoApprovalOfSavingsAccountApplication()) {
handler = this.applicationContext.getBean("savingsAccountApplicationApprovalUndoCommandHandler",
NewCommandSourceHandler.class);
} else if (wrapper.isSavingsAccountDeposit()) {
handler = this.applicationContext.getBean("depositSavingsAccountCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isSavingsAccountWithdrawal()) {
@@ -46,6 +46,7 @@ public final class JsonCommand {
private final Long supportedEntityId;
private final String transactionId;
private final String url;
public static JsonCommand from(final String jsonCommand, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper,
final String entityName, final Long resourceId, final Long subresourceId, final Long groupId, final Long clientId,
final Long loanId, final Long savingsId, final Long codeId, final String supportedEntityType, final Long supportedEntityId,
@@ -64,8 +65,7 @@ public final class JsonCommand {
public JsonCommand(final Long commandId, final String jsonCommand, final JsonElement parsedCommand,
final FromJsonHelper fromApiJsonHelper, final String entityName, final Long resourceId, final Long subresourceId,
final Long groupId, final Long clientId, final Long loanId, final Long savingsId, final Long codeId,
final String supportedEntityType, final Long supportedEntityId, final String transactionId,
final String url) {
final String supportedEntityType, final Long supportedEntityId, final String transactionId, final String url) {
this.commandId = commandId;
this.jsonCommand = jsonCommand;
this.parsedCommand = parsedCommand;
@@ -148,9 +148,9 @@ public final class JsonCommand {
public String getTransactionId() {
return this.transactionId;
}
public String getUrl() {
return this.url;
return this.url;
}
private boolean differenceExists(final LocalDate baseValue, final LocalDate workingCopyValue) {
@@ -187,10 +187,10 @@ public final class JsonCommand {
boolean differenceExists = false;
if (baseValue != null) {
if(workingCopyValue != null) {
differenceExists = !baseValue.equals(workingCopyValue);
if (workingCopyValue != null) {
differenceExists = !baseValue.equals(workingCopyValue);
} else {
differenceExists = true;
differenceExists = true;
}
} else {
differenceExists = workingCopyValue != null;
@@ -203,11 +203,11 @@ public final class JsonCommand {
boolean differenceExists = false;
if (baseValue != null) {
if(workingCopyValue != null) {
differenceExists = baseValue.compareTo(workingCopyValue) != 0;
}else {
differenceExists = true;
}
if (workingCopyValue != null) {
differenceExists = baseValue.compareTo(workingCopyValue) != 0;
} else {
differenceExists = true;
}
} else {
differenceExists = workingCopyValue != null;
}
@@ -301,6 +301,15 @@ public final class JsonCommand {
return StringUtils.defaultIfEmpty(value, "");
}
public boolean isChangeInBigDecimalParameterNamedDefaultingZeroToNull(final String parameterName, final BigDecimal existingValue) {
boolean isChanged = false;
if (parameterExists(parameterName)) {
final BigDecimal workingValue = bigDecimalValueOfParameterNamedDefaultToNullIfZero(parameterName);
isChanged = differenceExists(existingValue, workingValue);
}
return isChanged;
}
public boolean isChangeInBigDecimalParameterNamed(final String parameterName, final BigDecimal existingValue) {
boolean isChanged = false;
if (parameterExists(parameterName)) {
@@ -309,24 +318,53 @@ public final class JsonCommand {
}
return isChanged;
}
public boolean isChangeInBigDecimalParameterNamedWithNullCheck(final String parameterName, final BigDecimal existingValue) {
boolean isChanged = false;
if (parameterExists(parameterName)) {
final BigDecimal workingValue = bigDecimalValueOfParameterNamed(parameterName);
if(workingValue == null && existingValue != null){
if (workingValue == null && existingValue != null) {
isChanged = true;
}else{
} else {
isChanged = differenceExists(existingValue, workingValue);
}
}
return isChanged;
}
private static BigDecimal defaultToNullIfZero(final BigDecimal value) {
BigDecimal result = value;
if (value != null && BigDecimal.ZERO.compareTo(value) == 0) {
result = null;
}
return result;
}
private static Integer defaultToNullIfZero(final Integer value) {
Integer result = value;
if (value != null && value == 0) {
result = null;
}
return result;
}
public BigDecimal bigDecimalValueOfParameterNamed(final String parameterName) {
return this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(parameterName, parsedCommand);
}
public BigDecimal bigDecimalValueOfParameterNamedDefaultToNullIfZero(final String parameterName) {
return defaultToNullIfZero(bigDecimalValueOfParameterNamed(parameterName));
}
public boolean isChangeInIntegerParameterNamedDefaultingZeroToNull(final String parameterName, final Integer existingValue) {
boolean isChanged = false;
if (parameterExists(parameterName)) {
final Integer workingValue = integerValueOfParameterNamedDefaultToNullIfZero(parameterName);
isChanged = differenceExists(existingValue, workingValue);
}
return isChanged;
}
public boolean isChangeInIntegerParameterNamed(final String parameterName, final Integer existingValue) {
boolean isChanged = false;
if (parameterExists(parameterName)) {
@@ -340,19 +378,23 @@ public final class JsonCommand {
boolean isChanged = false;
if (parameterExists(parameterName)) {
final Integer workingValue = integerValueOfParameterNamed(parameterName);
if(workingValue == null && existingValue != null){
if (workingValue == null && existingValue != null) {
isChanged = true;
}else{
} else {
isChanged = differenceExists(existingValue, workingValue);
}
}
return isChanged;
}
public Integer integerValueOfParameterNamed(final String parameterName) {
return this.fromApiJsonHelper.extractIntegerWithLocaleNamed(parameterName, parsedCommand);
}
public Integer integerValueOfParameterNamedDefaultToNullIfZero(final String parameterName) {
return defaultToNullIfZero(integerValueOfParameterNamed(parameterName));
}
public boolean isChangeInIntegerSansLocaleParameterNamed(final String parameterName, final Integer existingValue) {
boolean isChanged = false;
if (parameterExists(parameterName)) {
@@ -209,25 +209,24 @@ public class DataValidatorBuilder {
public DataValidatorBuilder isOneOfTheseValues(final Object... values) {
if (value == null && ignoreNullValue) { return this; }
if (value != null) {
List<Object> valuesList = Arrays.asList(values);
String valuesListStr = StringUtils.join(valuesList, ", ");
List<Object> valuesList = Arrays.asList(values);
String valuesListStr = StringUtils.join(valuesList, ", ");
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(valuesListStr).append(" ] ").append(".");
if (this.value == null || !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(valuesListStr).append(" ] ").append(".");
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), parameter, this.value, values);
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(),
parameter, this.value, values);
dataValidationErrors.add(error);
}
dataValidationErrors.add(error);
}
return this;
}
public DataValidatorBuilder isNotOneOfTheseValues(final Object... values) {
if (value == null && ignoreNullValue) { return this; }
@@ -238,8 +237,8 @@ public class DataValidatorBuilder {
if (valuesList.contains(this.value)) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".is.one.of.unwanted.enumerations");
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(" must not be any of [ ")
.append(valuesListStr).append(" ] ").append(".");
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter)
.append(" must not be any of [ ").append(valuesListStr).append(" ] ").append(".");
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), parameter, this.value, values);
@@ -268,6 +267,9 @@ public class DataValidatorBuilder {
return this;
}
/*
* should be used with .notNull() before it
*/
public DataValidatorBuilder zeroOrPositiveAmount() {
if (value == null && ignoreNullValue) { return this; }
@@ -286,6 +288,30 @@ public class DataValidatorBuilder {
return this;
}
/*
* should be used with .notNull() before it
*/
public DataValidatorBuilder integerZeroOrGreater() {
if (value == null && ignoreNullValue) { return this; }
if (value != null) {
Integer number = Integer.valueOf(value.toString());
if (number < 0) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".not.zero.or.greater");
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(
" must be zero or greater.");
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), parameter, number, 0);
dataValidationErrors.add(error);
}
}
return this;
}
/*
* should be used with .notNull() before it
*/
public DataValidatorBuilder integerGreaterThanZero() {
if (value == null && ignoreNullValue) { return this; }
@@ -304,6 +330,9 @@ public class DataValidatorBuilder {
return this;
}
/*
* should be used with .notNull() before it
*/
public DataValidatorBuilder longGreaterThanZero() {
if (value == null && ignoreNullValue) { return this; }
@@ -337,11 +366,11 @@ public class DataValidatorBuilder {
}
return this;
}
public DataValidatorBuilder jsonArrayNotEmpty() {
if (value == null && ignoreNullValue) { return this; }
JsonArray array = (JsonArray)value;
JsonArray array = (JsonArray) value;
if (value != null && !array.iterator().hasNext()) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".cannot.be.empty");
@@ -427,14 +456,14 @@ public class DataValidatorBuilder {
dataValidationErrors.add(error);
return this;
}
public DataValidatorBuilder cantBeBlankWhenParameterProvidedIs(final String parameterName, final Object parameterValue) {
if (value != null && StringUtils.isNotBlank(value.toString())) { return this; }
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".must.be.provided.when.").append(parameterName).append(".is.").append(parameterValue);
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter)
.append(" must be provided when ").append(parameterName).append(" is ").append(parameterValue);
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(" must be provided when ")
.append(parameterName).append(" is ").append(parameterValue);
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(),
parameter, value, parameterName, parameterValue);
dataValidationErrors.add(error);
@@ -457,12 +486,12 @@ public class DataValidatorBuilder {
}
public DataValidatorBuilder inMinAndMaxAmountRange(final BigDecimal minimumAmount, final BigDecimal maximumAmount) {
if (minimumAmount != null && maximumAmount != null && value != null){
if (minimumAmount != null && maximumAmount != null && value != null) {
BigDecimal amount = BigDecimal.valueOf(Double.valueOf(value.toString()));
if (amount.compareTo(minimumAmount) == -1 || amount.compareTo(maximumAmount) == 1) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".amount.is.not.within.min.max.range");
StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(parameter) .append(" amount ").append(amount)
StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(parameter).append(" amount ").append(amount)
.append(" must be between ").append(minimumAmount).append(" and ").append(maximumAmount).append(" .");
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), parameter, amount, minimumAmount, maximumAmount);
@@ -472,14 +501,14 @@ public class DataValidatorBuilder {
}
return this;
}
public DataValidatorBuilder notLessThanMin(final BigDecimal min) {
if (min != null && value != null){
if (min != null && value != null) {
BigDecimal amount = BigDecimal.valueOf(Double.valueOf(value.toString()));
if (amount.compareTo(min) == -1) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".is.less.than.min");
StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(parameter) .append(" value ").append(amount)
StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(parameter).append(" value ").append(amount)
.append(" must not be less than minimum value ").append(min);
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), parameter, amount, min);
@@ -489,14 +518,14 @@ public class DataValidatorBuilder {
}
return this;
}
public DataValidatorBuilder notGreaterThanMax(final BigDecimal max) {
if (max != null && value != null){
if (max != null && value != null) {
BigDecimal amount = BigDecimal.valueOf(Double.valueOf(value.toString()));
if (amount.compareTo(max) == 1) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".is.greater.than.max");
StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(parameter) .append(" value ").append(amount)
StringBuilder defaultEnglishMessage = new StringBuilder("The ").append(parameter).append(" value ").append(amount)
.append(" must not be more than maximum value ").append(max);
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), parameter, amount, max);
@@ -541,7 +570,7 @@ public class DataValidatorBuilder {
}
return this;
}
public DataValidatorBuilder notLessThanMin(final Integer min) {
if (value == null && ignoreNullValue) { return this; }
@@ -559,7 +588,7 @@ public class DataValidatorBuilder {
}
return this;
}
public DataValidatorBuilder notGreaterThanMax(final Integer max) {
if (value == null && ignoreNullValue) { return this; }
@@ -577,26 +606,25 @@ public class DataValidatorBuilder {
}
return this;
}
public DataValidatorBuilder matchesRegularExpression(final String expression) {
if (value == null && ignoreNullValue) { return this; }
if (value != null && !value.toString().matches(expression)) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
public DataValidatorBuilder matchesRegularExpression(final String expression) {
if (value == null && ignoreNullValue) { return this; }
if (value != null && !value.toString().matches(expression)) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".does.not.match.regexp");
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter)
.append(" must match the provided regular expression [ ")
.append(expression).append(" ] ").append(".");
.append(" must match the provided regular expression [ ").append(expression).append(" ] ").append(".");
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), parameter, this.value, expression);
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(),
parameter, this.value, expression);
dataValidationErrors.add(error);
}
return this;
return this;
}
private DataValidatorBuilder validateStringFor(final String validInputs) {
if (this.value == null && this.ignoreNullValue) { return this; }
final String[] inputs = validInputs.split(VALID_INPUT_SEPERATOR);
@@ -610,15 +638,15 @@ public class DataValidatorBuilder {
if (validationErr) {
final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".")
.append(this.parameter).append(".value.should.true.or.false");
final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter)
.append(" value should true or false ");
final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter).append(
" value should true or false ");
final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), this.parameter, this.value);
this.dataValidationErrors.add(error);
}
return this;
}
public DataValidatorBuilder validateForBooleanValue() {
return validateStringFor("TRUE" + VALID_INPUT_SEPERATOR + "FALSE");
}
@@ -5,6 +5,8 @@
*/
package org.mifosplatform.infrastructure.core.domain;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.joda.time.Days;
import org.joda.time.LocalDate;
@@ -38,6 +40,14 @@ public class LocalDateInterval {
return Days.daysBetween(this.startDate, this.endDate).getDays();
}
public boolean containsPortionOf(final LocalDateInterval interval) {
return contains(interval.startDate) || contains(interval.endDate);
}
public boolean contains(final LocalDateInterval interval) {
return contains(interval.startDate) && contains(interval.endDate);
}
public boolean contains(final LocalDate target) {
return isBetweenInclusive(this.startDate, this.endDate, target);
}
@@ -49,4 +59,9 @@ public class LocalDateInterval {
public boolean fallsBefore(final LocalDate dateToCheck) {
return this.endDate.isBefore(dateToCheck);
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
}
@@ -5,7 +5,7 @@
*/
package org.mifosplatform.portfolio.client.data;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activationDateParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.activationDateParamName;
import java.lang.reflect.Type;
import java.util.ArrayList;
@@ -452,7 +452,7 @@ public final class Client extends AbstractPersistable<Long> {
this.staff = null;
}
public void assignStaff(Staff staff) {
public void assignStaff(final Staff staff) {
this.staff = staff;
}
@@ -466,4 +466,8 @@ public final class Client extends AbstractPersistable<Long> {
public Integer getStatus() {
return status;
}
public boolean isActivatedAfter(final LocalDate submittedOn) {
return getActivationLocalDate().isAfter(submittedOn);
}
}
@@ -179,6 +179,10 @@ public final class Group extends AbstractPersistable<Long> {
this.status = GroupingTypeStatus.ACTIVE.getValue();
}
public boolean isActivatedAfter(final LocalDate submittedOn) {
return getActivationLocalDate().isAfter(submittedOn);
}
public boolean isNotActive() {
return !isActive();
}
@@ -137,9 +137,9 @@ public class Loan extends AbstractPersistable<Long> {
@Column(name = "loan_status_id", nullable = false)
private Integer loanStatus;
@Column(name = "sync_disbursement_with_meeting", nullable = true)
private Boolean syncDisbursementWithMeeting;
@Column(name = "sync_disbursement_with_meeting", nullable = true)
private Boolean syncDisbursementWithMeeting;
// loan application states
@Temporal(TemporalType.DATE)
@Column(name = "submittedon_date")
@@ -231,10 +231,10 @@ public class Loan extends AbstractPersistable<Long> {
@SuppressWarnings("unused")
@Column(name = "total_overpaid_derived", scale = 6, precision = 19)
private BigDecimal totalOverpaid;
@Column(name = "loan_counter")
private Integer loanCounter;
@Column(name = "loan_product_counter")
private Integer loanProductCounter;
@@ -294,7 +294,7 @@ public class Loan extends AbstractPersistable<Long> {
public static Loan newGroupLoanApplication(final String accountNo, final Group group, final Integer loanType,
final LoanProduct loanProduct, final Fund fund, final Staff officer,
final LoanTransactionProcessingStrategy transactionProcessingStrategy,
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges,
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges,
final Boolean syncDisbursementWithMeeting) {
final LoanStatus status = null;
final CodeValue loanPurpose = null;
@@ -307,7 +307,7 @@ public class Loan extends AbstractPersistable<Long> {
public static Loan newIndividualLoanApplicationFromGroup(final String accountNo, final Client client, final Group group,
final Integer loanType, final LoanProduct loanProduct, final Fund fund, final Staff officer,
final LoanTransactionProcessingStrategy transactionProcessingStrategy,
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges,
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges,
final Boolean syncDisbursementWithMeeting) {
final LoanStatus status = null;
final CodeValue loanPurpose = null;
@@ -361,7 +361,7 @@ public class Loan extends AbstractPersistable<Long> {
this.collateral = null;
}
this.loanOfficerHistory = null;
this.syncDisbursementWithMeeting = syncDisbursementWithMeeting;
}
@@ -830,7 +830,7 @@ public class Loan extends AbstractPersistable<Long> {
}
// add clientId, groupId and loanType changes to actual changes
final String clientIdParamName = "clientId";
final Long clientId = this.client == null ? null : this.client.getId();
if (command.isChangeInLongParameterNamed(clientIdParamName, clientId)) {
@@ -932,11 +932,11 @@ public class Loan extends AbstractPersistable<Long> {
final String syncDisbursementParameterName = "syncDisbursementWithMeeting";
if (command.isChangeInBooleanParameterNamed(syncDisbursementParameterName, isSyncDisbursementWithMeeting())) {
final Boolean valueAsInput = command.booleanObjectValueOfParameterNamed(syncDisbursementParameterName);
actualChanges.put(syncDisbursementParameterName, valueAsInput);
this.syncDisbursementWithMeeting = valueAsInput;
final Boolean valueAsInput = command.booleanObjectValueOfParameterNamed(syncDisbursementParameterName);
actualChanges.put(syncDisbursementParameterName, valueAsInput);
this.syncDisbursementWithMeeting = valueAsInput;
}
final String interestChargedFromDateParamName = "interestChargedFromDate";
if (command.isChangeInLocalDateParameterNamed(interestChargedFromDateParamName, getInterestChargedFromDate())) {
final String valueAsInput = command.stringValueOfParameterNamed(interestChargedFromDateParamName);
@@ -1003,14 +1003,14 @@ public class Loan extends AbstractPersistable<Long> {
actualChanges.put(collateralParamName, listOfLoanCollateralData(possiblyModifedLoanCollateralItems));
}
}
final String loanTermFrequencyParamName = "loanTermFrequency";
if (command.isChangeInIntegerParameterNamed(loanTermFrequencyParamName, this.termFrequency)) {
final Integer newValue = command.integerValueOfParameterNamed(loanTermFrequencyParamName);
actualChanges.put(externalIdParamName, newValue);
this.termFrequency = newValue;
}
final String loanTermFrequencyTypeParamName = "loanTermFrequencyType";
if (command.isChangeInIntegerParameterNamed(loanTermFrequencyTypeParamName, this.termPeriodFrequencyType)) {
final Integer newValue = command.integerValueOfParameterNamed(loanTermFrequencyTypeParamName);
@@ -1100,18 +1100,12 @@ public class Loan extends AbstractPersistable<Long> {
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.a.future.date", errorMessage, submittedOn);
}
if (!(client == null)) {
if (submittedOn.isBefore(client.getActivationLocalDate())) {
final String errorMessage = "The date on which a loan is submitted cannot be earlier than client's activation date.";
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.client.activation.date", errorMessage,
submittedOn);
}
} else if (!(group == null)) {
if (submittedOn.isBefore(group.getActivationLocalDate())) {
final String errorMessage = "The date on which a loan is submitted cannot be earlier than groups's activation date.";
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.group.activation.date", errorMessage,
submittedOn);
}
if (client != null && client.isActivatedAfter(submittedOn)) {
final String errorMessage = "The date on which a loan is submitted cannot be earlier than client's activation date.";
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.client.activation.date", errorMessage, submittedOn);
} else if (group != null && group.isActivatedAfter(submittedOn)) {
final String errorMessage = "The date on which a loan is submitted cannot be earlier than groups's activation date.";
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.group.activation.date", errorMessage, submittedOn);
}
if (submittedOn.isAfter(getExpectedDisbursedOnLocalDate())) {
@@ -1379,14 +1373,17 @@ public class Loan extends AbstractPersistable<Long> {
final Integer loanTermFrequency = this.termFrequency;
final PeriodFrequencyType loanTermPeriodFrequencyType = PeriodFrequencyType.fromInt(this.termPeriodFrequencyType);
//If ExpectedFirstRepaymentOnDate is null and repayment is synced with meeting then use firstRepaymentMeetingDate
//to generate repayment schedule dates
final LocalDate firstRepaymentOnDate = (getExpectedFirstRepaymentOnDate() == null) ? firstRepaymentMeetingDate : getExpectedFirstRepaymentOnDate();
// If ExpectedFirstRepaymentOnDate is null and repayment is synced with
// meeting then use firstRepaymentMeetingDate
// to generate repayment schedule dates
final LocalDate firstRepaymentOnDate = (getExpectedFirstRepaymentOnDate() == null) ? firstRepaymentMeetingDate
: getExpectedFirstRepaymentOnDate();
LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency,
loanTermPeriodFrequencyType, getDisbursementDate(), firstRepaymentOnDate, getInArrearsTolerance(),
this.loanRepaymentScheduleDetail);
LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, applicationCurrency, loanApplicationTerms, this.charges, isHolidayEnabled, holidays);
LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, applicationCurrency, loanApplicationTerms, this.charges,
isHolidayEnabled, holidays);
updateLoanSchedule(loanSchedule);
}
@@ -2348,34 +2345,37 @@ public class Loan extends AbstractPersistable<Long> {
return this.closedOnDate;
}
public void updateLoanRepaymentScheduleDates(final LocalDate meetingStartDate, final String recuringRule, final boolean isHolidayEnabled, final List<Holiday> holidays) {
//update repayment dates of repayment schedule installaments
LocalDate tmpFromDate = this.getDisbursementDate();//first repayment's from date is same as disbursement date.
public void updateLoanRepaymentScheduleDates(final LocalDate meetingStartDate, final String recuringRule,
final boolean isHolidayEnabled, final List<Holiday> holidays) {
// update repayment dates of repayment schedule installaments
LocalDate tmpFromDate = this.getDisbursementDate();// first repayment's
// from date is same
// as disbursement
// date.
LocalDate newRepaymentDate = null;
for (LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : this.repaymentScheduleInstallments) {
final LocalDate oldDueDate = loanRepaymentScheduleInstallment.getDueDate();
//FIXME: AA this won't update repayment dates before current date.
// FIXME: AA this won't update repayment dates before current date.
if (oldDueDate.isAfter(meetingStartDate) && oldDueDate.isAfter(DateUtils.getLocalDateOfTenant())) {
final PeriodFrequencyType repaymentPeriodFrequencyType = this.loanRepaymentScheduleDetail.getRepaymentPeriodFrequencyType();
final String frequency = CalendarHelper.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType);
final Integer loanRepaymentInterval = this.loanRepaymentScheduleDetail.getRepayEvery();
// reset repayment date with new meeting date
newRepaymentDate = CalendarHelper.getNewRepaymentMeetingDate(recuringRule, meetingStartDate, oldDueDate, loanRepaymentInterval, frequency);
if(isHolidayEnabled){
newRepaymentDate = CalendarHelper.getNewRepaymentMeetingDate(recuringRule, meetingStartDate, oldDueDate,
loanRepaymentInterval, frequency);
if (isHolidayEnabled) {
newRepaymentDate = HolidayUtil.getRepaymentRescheduleDateToIfHoliday(newRepaymentDate, holidays);
}
loanRepaymentScheduleInstallment.updateDueDate(newRepaymentDate);
//reset from date to get actual daysInPeriod
// reset from date to get actual daysInPeriod
loanRepaymentScheduleInstallment.updateFromDate(tmpFromDate);
tmpFromDate = newRepaymentDate;//update with new repayment date
}else{
tmpFromDate = newRepaymentDate;// update with new repayment date
} else {
tmpFromDate = oldDueDate;
}
}
}
public void applyHolidayToRepaymentScheduleDates(final Holiday holiday) {
// first repayment's from date is same as disbursement date.
@@ -2383,9 +2383,10 @@ public class Loan extends AbstractPersistable<Long> {
// Loop through all loanRepayments
for (LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : this.repaymentScheduleInstallments) {
final LocalDate oldDueDate = loanRepaymentScheduleInstallment.getDueDate();
//update from date if it's not same as previous installament's due date.
if(!loanRepaymentScheduleInstallment.getFromDate().isEqual(tmpFromDate)){
// update from date if it's not same as previous installament's due
// date.
if (!loanRepaymentScheduleInstallment.getFromDate().isEqual(tmpFromDate)) {
loanRepaymentScheduleInstallment.updateFromDate(tmpFromDate);
}
if (oldDueDate.isAfter(holiday.getToDateLocalDate())) break;
@@ -2412,11 +2413,11 @@ public class Loan extends AbstractPersistable<Long> {
return this.loanProductCounter;
}
public void updateClientLoanCounter(Integer newLoanCounter) {
public void updateClientLoanCounter(final Integer newLoanCounter) {
this.loanCounter = newLoanCounter;
}
public void updateLoanProductLoanCounter(Integer newLoanProductLoanCounter) {
public void updateLoanProductLoanCounter(final Integer newLoanProductLoanCounter) {
this.loanProductCounter = newLoanProductLoanCounter;
}
@@ -2432,7 +2433,6 @@ public class Loan extends AbstractPersistable<Long> {
return this.termFrequency;
}
public Integer getTermPeriodFrequencyType() {
return this.termPeriodFrequencyType;
}
@@ -27,6 +27,6 @@ public class LoanApplicationApprovalCommandHandler implements NewCommandSourceHa
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.approveLoanApplication(command.entityId(), command);
return this.writePlatformService.approveApplication(command.entityId(), command);
}
}
@@ -27,6 +27,6 @@ public class LoanApplicationApprovalUndoCommandHandler implements NewCommandSour
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.undoLoanApplicationApproval(command.entityId(), command);
return this.writePlatformService.undoApplicationApproval(command.entityId(), command);
}
}
@@ -27,6 +27,6 @@ public class LoanApplicationDeletionCommandHandler implements NewCommandSourceHa
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.deleteLoanApplication(command.entityId());
return this.writePlatformService.deleteApplication(command.entityId());
}
}
@@ -27,6 +27,6 @@ public class LoanApplicationModificationCommandHandler implements NewCommandSour
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.modifyLoanApplication(command.getLoanId(), command);
return this.writePlatformService.modifyApplication(command.getLoanId(), command);
}
}
@@ -27,6 +27,6 @@ public class LoanApplicationRejectedCommandHandler implements NewCommandSourceHa
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.rejectLoanApplication(command.entityId(), command);
return this.writePlatformService.rejectApplication(command.entityId(), command);
}
}
@@ -27,6 +27,6 @@ public class LoanApplicationSubmittalCommandHandler implements NewCommandSourceH
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.submitLoanApplication(command);
return this.writePlatformService.submitApplication(command);
}
}
@@ -27,6 +27,6 @@ public class LoanApplicationWithdrawnByApplicantCommandHandler implements NewCom
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.applicantWithdrawsFromLoanApplication(command.entityId(), command);
return this.writePlatformService.applicantWithdrawsFromApplication(command.entityId(), command);
}
}
@@ -647,8 +647,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
baseDataValidator.reset().parameter(note).value(noteText).notExceedingLengthOf(1000);
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
public void validateMinMaxConstraintValues(final JsonElement element, final LoanProduct loanProduct) {
@@ -10,17 +10,17 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
public interface LoanApplicationWritePlatformService {
CommandProcessingResult submitLoanApplication(JsonCommand command);
CommandProcessingResult submitApplication(JsonCommand command);
CommandProcessingResult modifyLoanApplication(Long loanId, JsonCommand command);
CommandProcessingResult modifyApplication(Long loanId, JsonCommand command);
CommandProcessingResult deleteLoanApplication(Long loanId);
CommandProcessingResult deleteApplication(Long loanId);
CommandProcessingResult approveLoanApplication(Long loanId, JsonCommand command);
CommandProcessingResult approveApplication(Long loanId, JsonCommand command);
CommandProcessingResult undoLoanApplicationApproval(Long loanId, JsonCommand command);
CommandProcessingResult undoApplicationApproval(Long loanId, JsonCommand command);
CommandProcessingResult rejectLoanApplication(Long loanId, JsonCommand command);
CommandProcessingResult rejectApplication(Long loanId, JsonCommand command);
CommandProcessingResult applicantWithdrawsFromLoanApplication(Long loanId, JsonCommand command);
CommandProcessingResult applicantWithdrawsFromApplication(Long loanId, JsonCommand command);
}
@@ -138,7 +138,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
@Transactional
@Override
public CommandProcessingResult submitLoanApplication(final JsonCommand command) {
public CommandProcessingResult submitApplication(final JsonCommand command) {
final AppUser currentUser = context.authenticatedUser();
@@ -205,7 +205,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
@Transactional
@Override
public CommandProcessingResult modifyLoanApplication(final Long loanId, final JsonCommand command) {
public CommandProcessingResult modifyApplication(final Long loanId, final JsonCommand command) {
try {
context.authenticatedUser();
@@ -384,7 +384,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
@Transactional
@Override
public CommandProcessingResult deleteLoanApplication(final Long loanId) {
public CommandProcessingResult deleteApplication(final Long loanId) {
context.authenticatedUser();
@@ -408,7 +408,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
@Transactional
@Override
public CommandProcessingResult approveLoanApplication(final Long loanId, final JsonCommand command) {
public CommandProcessingResult approveApplication(final Long loanId, final JsonCommand command) {
final AppUser currentUser = context.authenticatedUser();
@@ -441,7 +441,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
@Transactional
@Override
public CommandProcessingResult undoLoanApplicationApproval(final Long loanId, final JsonCommand command) {
public CommandProcessingResult undoApplicationApproval(final Long loanId, final JsonCommand command) {
context.authenticatedUser();
@@ -473,7 +473,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
@Transactional
@Override
public CommandProcessingResult rejectLoanApplication(final Long loanId, final JsonCommand command) {
public CommandProcessingResult rejectApplication(final Long loanId, final JsonCommand command) {
final AppUser currentUser = context.authenticatedUser();
@@ -505,7 +505,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
@Transactional
@Override
public CommandProcessingResult applicantWithdrawsFromLoanApplication(final Long loanId, final JsonCommand command) {
public CommandProcessingResult applicantWithdrawsFromApplication(final Long loanId, final JsonCommand command) {
final AppUser currentUser = context.authenticatedUser();
@@ -21,6 +21,7 @@ import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.group.domain.Group;
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
import org.mifosplatform.useradministration.domain.AppUser;
@Entity
@@ -53,10 +54,10 @@ public class Note extends AbstractAuditableCustom<AppUser, Long> {
@Column(name = "note_type_enum")
private final Integer noteTypeId;
// @SuppressWarnings("unused")
// @ManyToOne
// @JoinColumn(name = "saving_account_id", nullable = true)
// private SavingAccount savingAccount;
@SuppressWarnings("unused")
@ManyToOne
@JoinColumn(name = "savings_account_id", nullable = true)
private SavingsAccount savingsAccount;
public static Note clientNoteFromJson(final Client client, final JsonCommand command) {
final String note = command.stringValueOfParameterNamed("note");
@@ -76,10 +77,9 @@ public class Note extends AbstractAuditableCustom<AppUser, Long> {
return new Note(loan, loanTransaction, note);
}
// public static Note savingNote(final SavingAccount account, final String
// note) {
// return new Note(account, note);
// }
public static Note savingNote(final SavingsAccount account, final String note) {
return new Note(account, note);
}
private Note(final Client client, final String note) {
this.client = client;
@@ -118,12 +118,12 @@ public class Note extends AbstractAuditableCustom<AppUser, Long> {
this.noteTypeId = null;
}
// public Note(final SavingAccount account, final String note) {
// this.savingAccount = account;
// this.client = account.getClient();
// this.note = note;
// this.noteTypeId = NoteType.SAVING_ACCOUNT.getValue();
// }
public Note(final SavingsAccount account, final String note) {
this.savingsAccount = account;
this.client = account.getClient();
this.note = note;
this.noteTypeId = NoteType.SAVING_ACCOUNT.getValue();
}
public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(7);
@@ -26,7 +26,7 @@ public interface NoteRepository extends JpaRepository<Note, Long>, JpaSpecificat
Note findByLoanTransactionIdAndId(Long loanTransactionId, Long id);
// Note findByDepositAccountIdAndId(Long depositAccountId, Long id);
List<Note> findBySavingsAccountId(Long savingAccountId);
// Note findBySavingAccountIdAndId(Long savingAccountId, Long id);
// Note findBySavingsAccountIdAndId(Long savingAccountId, Long id);
}
@@ -3,7 +3,9 @@
* 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;
package org.mifosplatform.portfolio.savings;
import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
/**
* An enumeration of different transactions that can occur on a
@@ -3,7 +3,7 @@
* 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.api;
package org.mifosplatform.portfolio.savings;
import java.util.Arrays;
import java.util.HashSet;
@@ -32,8 +32,10 @@ public class SavingsApiConstants {
public static final String clientIdParamName = "clientId";
public static final String groupIdParamName = "groupId";
public static final String productIdParamName = "productId";
public static final String fieldOfficerIdParamName = "fieldOfficerId";
public static final String submittedOnDateParamName = "submittedOnDate";
public static final String activeParamName = "active";
public static final String activationDateParamName = "activationDate";
public static final String activationDateParamName = "activatedOnDate";
public static final String nameParamName = "name";
public static final String descriptionParamName = "description";
public static final String currencyCodeParamName = "currencyCode";
@@ -52,7 +54,7 @@ public class SavingsApiConstants {
public static final String annualFeeOnMonthDayParamName = "annualFeeOnMonthDay";
public static final String accountingRuleParamName = "accountingRule";
public static final String paymentTypeIdParamName = "paymentTypeId";
public static final String accountNumberParamName = "accountNumber";
public static final String transactionAccountNumberParamName = "accountNumber";
public static final String checkNumberParamName = "checkNumber";
public static final String routingCodeParamName = "routingCode";
public static final String receiptNumberParamName = "receiptNumber";
@@ -89,7 +91,7 @@ public class SavingsApiConstants {
public static final Set<String> SAVINGS_ACCOUNT_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
dateFormatParamName, monthDayFormatParamName, accountNoParamName, externalIdParamName, clientIdParamName, groupIdParamName,
productIdParamName, activeParamName, activationDateParamName, nominalAnnualInterestRateParamName,
productIdParamName, fieldOfficerIdParamName, submittedOnDateParamName, nominalAnnualInterestRateParamName,
interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName,
interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName,
lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, annualFeeAmountParamName,
@@ -112,7 +114,7 @@ public class SavingsApiConstants {
public static final Set<String> SAVINGS_ACCOUNT_TRANSACTION_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(
localeParamName, dateFormatParamName, transactionDateParamName, transactionAmountParamName, paymentTypeIdParamName,
accountNumberParamName, checkNumberParamName, routingCodeParamName, receiptNumberParamName, bankNumberParamName));
transactionAccountNumberParamName, checkNumberParamName, routingCodeParamName, receiptNumberParamName, bankNumberParamName));
public static final Set<String> SAVINGS_TRANSACTION_RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(idParamName,
"accountId", accountNoParamName, "currency", "amount"));
@@ -3,7 +3,11 @@
* 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;
package org.mifosplatform.portfolio.savings;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
@@ -15,13 +19,13 @@ public enum SavingsCompoundingInterestPeriodType {
INVALID(0, "savingsCompoundingInterestPeriodType.invalid"), //
DAILY(1, "savingsCompoundingInterestPeriodType.daily"), //
WEEKLY(2, "savingsCompoundingInterestPeriodType.weekly"), //
BIWEEKLY(3, "savingsCompoundingInterestPeriodType.biweekly"), //
MONTHLY(4, "savingsCompoundingInterestPeriodType.monthly"), //
QUATERLY(5, "savingsCompoundingInterestPeriodType.quarterly"), //
BI_ANNUAL(6, "savingsCompoundingInterestPeriodType.biannual"), //
ANNUAL(7, "savingsCompoundingInterestPeriodType.annual"), //
NO_COMPOUNDING_SIMPLE_INTEREST(8, "savingsCompoundingInterestPeriodType.nocompounding");
// WEEKLY(2, "savingsCompoundingInterestPeriodType.weekly"), //
// BIWEEKLY(3, "savingsCompoundingInterestPeriodType.biweekly"), //
MONTHLY(4, "savingsCompoundingInterestPeriodType.monthly");
// QUATERLY(5, "savingsCompoundingInterestPeriodType.quarterly"), //
// BI_ANNUAL(6, "savingsCompoundingInterestPeriodType.biannual"), //
// ANNUAL(7, "savingsCompoundingInterestPeriodType.annual"), //
// NO_COMPOUNDING_SIMPLE_INTEREST(8, "savingsCompoundingInterestPeriodType.nocompounding");
private final Integer value;
private final String code;
@@ -39,6 +43,15 @@ public enum SavingsCompoundingInterestPeriodType {
return code;
}
public static Object[] integerValues() {
List<Integer> values = new ArrayList<Integer>();
for (SavingsCompoundingInterestPeriodType enumType : values()) {
values.add(enumType.getValue());
}
return values.toArray();
}
public static SavingsCompoundingInterestPeriodType fromInt(final Integer type) {
SavingsCompoundingInterestPeriodType repaymentFrequencyType = SavingsCompoundingInterestPeriodType.INVALID;
if (type != null) {
@@ -47,28 +60,34 @@ public enum SavingsCompoundingInterestPeriodType {
repaymentFrequencyType = SavingsCompoundingInterestPeriodType.DAILY;
break;
case 2:
repaymentFrequencyType = SavingsCompoundingInterestPeriodType.WEEKLY;
// repaymentFrequencyType =
// SavingsCompoundingInterestPeriodType.WEEKLY;
break;
case 3:
repaymentFrequencyType = SavingsCompoundingInterestPeriodType.BIWEEKLY;
// repaymentFrequencyType =
// SavingsCompoundingInterestPeriodType.BIWEEKLY;
break;
case 4:
repaymentFrequencyType = SavingsCompoundingInterestPeriodType.MONTHLY;
break;
case 5:
repaymentFrequencyType = SavingsCompoundingInterestPeriodType.QUATERLY;
// repaymentFrequencyType =
// SavingsCompoundingInterestPeriodType.QUATERLY;
break;
case 6:
repaymentFrequencyType = SavingsCompoundingInterestPeriodType.BI_ANNUAL;
// repaymentFrequencyType =
// SavingsCompoundingInterestPeriodType.BI_ANNUAL;
break;
case 7:
repaymentFrequencyType = SavingsCompoundingInterestPeriodType.ANNUAL;
// repaymentFrequencyType =
// SavingsCompoundingInterestPeriodType.ANNUAL;
break;
case 8:
repaymentFrequencyType = SavingsCompoundingInterestPeriodType.NO_COMPOUNDING_SIMPLE_INTEREST;
// repaymentFrequencyType =
// SavingsCompoundingInterestPeriodType.NO_COMPOUNDING_SIMPLE_INTEREST;
break;
}
}
return repaymentFrequencyType;
}
}
}
@@ -3,7 +3,7 @@
* 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;
package org.mifosplatform.portfolio.savings;
/**
* <ul>
@@ -3,7 +3,7 @@
* 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;
package org.mifosplatform.portfolio.savings;
/**
* <ul>
@@ -3,7 +3,7 @@
* 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;
package org.mifosplatform.portfolio.savings;
/**
* An enumeration of supported calendar periods used in savings.
@@ -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;
/**
* The interest posting period is the span of time at the end of which savings
* earned but not yet credited/posted in a client's account is credited/posted.
*/
public enum SavingsPostingInterestPeriodType {
INVALID(0, "savingsPostingInterestPeriodType.invalid"), //
MONTHLY(4, "savingsPostingInterestPeriodType.monthly"), //
QUATERLY(5, "savingsPostingInterestPeriodType.quarterly"), //
ANNUAL(7, "savingsPostingInterestPeriodType.annual");
private final Integer value;
private final String code;
private SavingsPostingInterestPeriodType(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 SavingsPostingInterestPeriodType fromInt(final Integer type) {
SavingsPostingInterestPeriodType repaymentFrequencyType = SavingsPostingInterestPeriodType.INVALID;
if (type != null) {
switch (type) {
case 4:
repaymentFrequencyType = SavingsPostingInterestPeriodType.MONTHLY;
break;
case 5:
repaymentFrequencyType = SavingsPostingInterestPeriodType.QUATERLY;
break;
case 7:
repaymentFrequencyType = SavingsPostingInterestPeriodType.ANNUAL;
break;
}
}
return repaymentFrequencyType;
}
}
@@ -3,7 +3,7 @@
* 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;
package org.mifosplatform.portfolio.savings;
public enum SavingsWithdrawalFeesType {
@@ -31,6 +31,7 @@ import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSeriali
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants;
import org.mifosplatform.portfolio.savings.SavingsApiConstants;
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionData;
import org.mifosplatform.portfolio.savings.service.SavingsAccountReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -72,7 +73,8 @@ public class SavingsAccountTransactionsApiResource {
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveTemplate(@PathParam("savingsId") final Long savingsId, @QueryParam("command") final String commandParam,
public String retrieveTemplate(@PathParam("savingsId") final Long savingsId,
// @QueryParam("command") final String commandParam,
@Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
@@ -12,6 +12,7 @@ import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
@@ -36,7 +37,9 @@ import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSeriali
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.mifosplatform.infrastructure.core.service.Page;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.staff.data.StaffData;
import org.mifosplatform.portfolio.group.service.SearchParameters;
import org.mifosplatform.portfolio.savings.SavingsApiConstants;
import org.mifosplatform.portfolio.savings.data.SavingsAccountData;
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionData;
import org.mifosplatform.portfolio.savings.data.SavingsProductData;
@@ -81,12 +84,15 @@ public class SavingsAccountsApiResource {
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveTemplate(@QueryParam("clientId") final Long clientId, @QueryParam("groupId") final Long groupId,
@QueryParam("productId") final Long productId, @Context final UriInfo uriInfo) {
public String template(@QueryParam("clientId") final Long clientId, @QueryParam("groupId") final Long groupId,
@QueryParam("productId") final Long productId,
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly,
@Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
final SavingsAccountData savingsAccount = this.savingsAccountReadPlatformService.retrieveTemplate(clientId, groupId, productId);
final SavingsAccountData savingsAccount = this.savingsAccountReadPlatformService.retrieveTemplate(clientId, groupId, productId,
staffInSelectedOfficeOnly);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, savingsAccount, SavingsApiConstants.SAVINGS_ACCOUNT_RESPONSE_DATA_PARAMETERS);
@@ -113,7 +119,7 @@ public class SavingsAccountsApiResource {
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String create(final String apiRequestBodyAsJson) {
public String submitApplication(final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().createSavingsAccount().withJson(apiRequestBodyAsJson).build();
@@ -147,6 +153,7 @@ public class SavingsAccountsApiResource {
Collection<SavingsAccountTransactionData> transactions = null;
Collection<SavingsProductData> productOptions = null;
Collection<StaffData> fieldOfficerOptions = null;
Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
Collection<EnumOptionData> interestCompoundingPostingTypeOptions = null;
Collection<EnumOptionData> interestCalculationTypeOptions = null;
@@ -174,16 +181,49 @@ public class SavingsAccountsApiResource {
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
if (settings.isTemplate()) {
productOptions = this.savingsProductReadPlatformService.retrieveAllForLookup();
// FIXME - FOR CHANGE SAVINGS ACCOUNT APPLICATION need to determine
// office id of savings account
// if (savingsAccount.officeId() != null) {
//
// if (staffInSelectedOfficeOnly) {
// // only bring back loan officers in selected branch/office
// Collection<StaffData> fieldOfficersInBranch =
// this.staffReadPlatformService
// .retrieveAllLoanOfficersInOfficeById(officeId);
//
// if (!CollectionUtils.isEmpty(fieldOfficersInBranch)) {
// fieldOfficerOptions = new
// ArrayList<StaffData>(fieldOfficersInBranch);
// }
// } else {
// // by default bring back all officers in selected
// // branch/office as well as officers in office above
// // this office
// final boolean restrictToLoanOfficersOnly = true;
// Collection<StaffData> loanOfficersInHierarchy =
// this.staffReadPlatformService
// .retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(officeId,
// restrictToLoanOfficersOnly);
//
// if (!CollectionUtils.isEmpty(loanOfficersInHierarchy)) {
// fieldOfficerOptions = new
// ArrayList<StaffData>(loanOfficersInHierarchy);
// }
// }
// }
interestCompoundingPeriodTypeOptions = this.dropdownReadPlatformService.retrieveCompoundingInterestPeriodTypeOptions();
interestCompoundingPostingTypeOptions = this.dropdownReadPlatformService.retrieveInterestPostingPeriodTypeOptions();
interestCalculationTypeOptions = this.dropdownReadPlatformService.retrieveInterestCalculationTypeOptions();
interestCalculationDaysInYearTypeOptions = this.dropdownReadPlatformService.retrieveInterestCalculationDaysInYearTypeOptions();
lockinPeriodFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveLockinPeriodFrequencyTypeOptions();
withdrawalFeeTypeOptions = this.dropdownReadPlatformService.retrievewithdrawalFeeTypeOptions();
}
return SavingsAccountData.withTemplateOptions(savingsAccount, productOptions, interestCompoundingPeriodTypeOptions,
interestCompoundingPostingTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions);
return SavingsAccountData.withTemplateOptions(savingsAccount, productOptions, fieldOfficerOptions,
interestCompoundingPeriodTypeOptions, interestCompoundingPostingTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions);
}
@PUT
@@ -210,7 +250,19 @@ public class SavingsAccountsApiResource {
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
CommandProcessingResult result = null;
if (is(commandParam, "activate")) {
if (is(commandParam, "reject")) {
final CommandWrapper commandRequest = builder.rejectSavingsAccountApplication(accountId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (is(commandParam, "withdrawnByApplicant")) {
final CommandWrapper commandRequest = builder.withdrawSavingsAccountApplication(accountId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (is(commandParam, "approve")) {
final CommandWrapper commandRequest = builder.approveSavingsAccountApplication(accountId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (is(commandParam, "undoapproval")) {
final CommandWrapper commandRequest = builder.undoSavingsAccountApplication(accountId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (is(commandParam, "activate")) {
final CommandWrapper commandRequest = builder.savingsAccountActivation(accountId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (is(commandParam, "calculateInterest")) {
@@ -223,8 +275,8 @@ public class SavingsAccountsApiResource {
if (result == null) {
//
throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate", "calculateInterest",
"postInterest" });
throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "reject", "withdrawnByApplicant", "approve",
"undoapproval", "activate", "calculateInterest", "postInterest" });
}
return this.toApiJsonSerializer.serialize(result);
@@ -42,11 +42,12 @@ 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.paymentdetail.PaymentDetailConstants;
import org.mifosplatform.portfolio.savings.SavingsApiConstants;
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType;
import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType;
import org.mifosplatform.portfolio.savings.data.SavingsProductData;
import org.mifosplatform.portfolio.savings.domain.SavingsCompoundingInterestPeriodType;
import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationDaysInYearType;
import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationType;
import org.mifosplatform.portfolio.savings.domain.SavingsInterestPostingPeriodType;
import org.mifosplatform.portfolio.savings.service.SavingsDropdownReadPlatformService;
import org.mifosplatform.portfolio.savings.service.SavingsEnumerations;
import org.mifosplatform.portfolio.savings.service.SavingsProductReadPlatformService;
@@ -180,7 +181,7 @@ public class SavingsProductsApiResource {
.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.DAILY);
final EnumOptionData interestPostingPeriodType = SavingsEnumerations
.interestPostingPeriodType(SavingsInterestPostingPeriodType.MONTHLY);
.interestPostingPeriodType(SavingsPostingInterestPeriodType.MONTHLY);
final EnumOptionData interestCalculationType = SavingsEnumerations
.interestCalculationType(SavingsInterestCalculationType.DAILY_BALANCE);
@@ -0,0 +1,109 @@
/**
* 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.data;
import org.joda.time.LocalDate;
/**
* Immutable data object represent the important time-line events of a savings
* account application.
*/
@SuppressWarnings("unused")
public class SavingsAccountApplicationTimelineData {
private final LocalDate submittedOnDate;
private final String submittedByUsername;
private final String submittedByFirstname;
private final String submittedByLastname;
private final LocalDate rejectedOnDate;
private final String rejectedByUsername;
private final String rejectedByFirstname;
private final String rejectedByLastname;
private final LocalDate withdrawnOnDate;
private final String withdrawnByUsername;
private final String withdrawnByFirstname;
private final String withdrawnByLastname;
private final LocalDate approvedOnDate;
private final String approvedByUsername;
private final String approvedByFirstname;
private final String approvedByLastname;
private final LocalDate activatedOnDate;
private final String activatedByUsername;
private final String activatedByFirstname;
private final String activatedByLastname;
private final LocalDate closedOnDate;
private final String closedByUsername;
private final String closedByFirstname;
private final String closedByLastname;
public static SavingsAccountApplicationTimelineData templateDefault() {
final LocalDate submittedOnDate = null;
final String submittedByUsername = null;
final String submittedByFirstname = null;
final String submittedByLastname = null;
final LocalDate rejectedOnDate = null;
final String rejectedByUsername = null;
final String rejectedByFirstname = null;
final String rejectedByLastname = null;
final LocalDate withdrawnOnDate = null;
final String withdrawnByUsername = null;
final String withdrawnByFirstname = null;
final String withdrawnByLastname = null;
final LocalDate approvedOnDate = null;
final String approvedByUsername = null;
final String approvedByFirstname = null;
final String approvedByLastname = null;
final LocalDate activatedOnDate = null;
final String activatedByUsername = null;
final String activatedByFirstname = null;
final String activatedByLastname = null;
final LocalDate closedOnDate = null;
final String closedByUsername = null;
final String closedByFirstname = null;
final String closedByLastname = null;
return new SavingsAccountApplicationTimelineData(submittedOnDate, submittedByUsername, submittedByFirstname, submittedByLastname,
rejectedOnDate, rejectedByUsername, rejectedByFirstname, rejectedByLastname, withdrawnOnDate, withdrawnByUsername,
withdrawnByFirstname, withdrawnByLastname, approvedOnDate, approvedByUsername, approvedByFirstname, approvedByLastname,
activatedOnDate, activatedByUsername, activatedByFirstname, activatedByLastname, closedOnDate, closedByUsername,
closedByFirstname, closedByLastname);
}
public SavingsAccountApplicationTimelineData(final LocalDate submittedOnDate, final String submittedByUsername,
final String submittedByFirstname, final String submittedByLastname, final LocalDate rejectedOnDate,
final String rejectedByUsername, final String rejectedByFirstname, final String rejectedByLastname,
final LocalDate withdrawnOnDate, final String withdrawnByUsername, final String withdrawnByFirstname,
final String withdrawnByLastname, final LocalDate approvedOnDate, final String approvedByUsername,
final String approvedByFirstname, final String approvedByLastname, final LocalDate activatedOnDate,
final String activatedByUsername, final String activatedByFirstname, final String activatedByLastname,
final LocalDate closedOnDate, final String closedByUsername, final String closedByFirstname, final String closedByLastname) {
this.submittedOnDate = submittedOnDate;
this.submittedByUsername = submittedByUsername;
this.submittedByFirstname = submittedByFirstname;
this.submittedByLastname = submittedByLastname;
this.rejectedOnDate = rejectedOnDate;
this.rejectedByUsername = rejectedByUsername;
this.rejectedByFirstname = rejectedByFirstname;
this.rejectedByLastname = rejectedByLastname;
this.withdrawnOnDate = withdrawnOnDate;
this.withdrawnByUsername = withdrawnByUsername;
this.withdrawnByFirstname = withdrawnByFirstname;
this.withdrawnByLastname = withdrawnByLastname;
this.approvedOnDate = approvedOnDate;
this.approvedByUsername = approvedByUsername;
this.approvedByFirstname = approvedByFirstname;
this.approvedByLastname = approvedByLastname;
this.activatedOnDate = activatedOnDate;
this.activatedByUsername = activatedByUsername;
this.activatedByFirstname = activatedByFirstname;
this.activatedByLastname = activatedByLastname;
this.closedOnDate = closedOnDate;
this.closedByUsername = closedByUsername;
this.closedByFirstname = closedByFirstname;
this.closedByLastname = closedByLastname;
}
}
@@ -12,6 +12,7 @@ import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.organisation.staff.data.StaffData;
/**
* Immutable data object representing a savings account.
@@ -21,14 +22,16 @@ public class SavingsAccountData {
private final Long id;
private final String accountNo;
private final String externalId;
private final SavingsAccountStatusEnumData status;
private final LocalDate activationDate;
private final Long groupId;
private final String groupName;
private final Long clientId;
private final String clientName;
private final Long savingsProductId;
private final String savingsProductName;
private final Long fieldofficerId;
private final String fieldofficerName;
private final SavingsAccountStatusEnumData status;
private final SavingsAccountApplicationTimelineData timeline;
private final CurrencyData currency;
private final BigDecimal nominalAnnualInterestRate;
private final EnumOptionData interestCompoundingPeriodType;
@@ -53,6 +56,8 @@ public class SavingsAccountData {
@SuppressWarnings("unused")
private final Collection<SavingsProductData> productOptions;
@SuppressWarnings("unused")
private final Collection<StaffData> fieldOfficerOptions;
@SuppressWarnings("unused")
private final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions;
@SuppressWarnings("unused")
private final Collection<EnumOptionData> interestPostingPeriodTypeOptions;
@@ -65,17 +70,19 @@ public class SavingsAccountData {
@SuppressWarnings("unused")
private final Collection<EnumOptionData> withdrawalFeeTypeOptions;
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 interestCompoundingPeriodType,
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount,
final EnumOptionData withdrawalFeeType, final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay,
final LocalDate annualFeeNextDueDate, final SavingsAccountSummaryData summary) {
public static SavingsAccountData instance(final Long id, final String accountNo, final String externalId, final Long groupId,
final String groupName, final Long clientId, final String clientName, final Long productId, final String productName,
final Long fieldOfficerId, final String fieldOfficerName, final SavingsAccountStatusEnumData status,
final SavingsAccountApplicationTimelineData timeline, final CurrencyData currency, final BigDecimal interestRate,
final EnumOptionData interestCompoundingPeriodType, final EnumOptionData interestPostingPeriodType,
final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType,
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency,
final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount, final EnumOptionData withdrawalFeeType,
final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay, final LocalDate annualFeeNextDueDate,
final SavingsAccountSummaryData summary) {
final Collection<SavingsProductData> productOptions = null;
final Collection<StaffData> fieldOfficerOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
@@ -85,32 +92,34 @@ public class SavingsAccountData {
final Collection<SavingsAccountTransactionData> transactions = null;
return new SavingsAccountData(id, accountNo, externalId, status, activationDate, groupId, groupName, clientId, clientName,
productId, productName, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, annualFeeOnMonthDay,
annualFeeNextDueDate, summary, transactions, productOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
return new SavingsAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
fieldOfficerId, fieldOfficerName, status, timeline, currency, interestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount,
annualFeeOnMonthDay, annualFeeNextDueDate, summary, transactions, productOptions, fieldOfficerOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
}
public static SavingsAccountData withTemplateOptions(final SavingsAccountData account,
final Collection<SavingsProductData> productOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<SavingsProductData> productOptions, final Collection<StaffData> fieldOfficerOptions,
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
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.nominalAnnualInterestRate, account.interestCompoundingPeriodType,
account.interestPostingPeriodType, account.interestCalculationType, account.interestCalculationDaysInYearType,
account.minRequiredOpeningBalance, account.lockinPeriodFrequency, account.lockinPeriodFrequencyType,
account.withdrawalFeeAmount, account.withdrawalFeeType, account.annualFeeAmount, account.annualFeeOnMonthDay,
account.annualFeeNextDueDate, account.summary, transactions, productOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
return new SavingsAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName,
account.clientId, account.clientName, account.savingsProductId, account.savingsProductName, account.fieldofficerId,
account.fieldofficerName, account.status, account.timeline, account.currency, account.nominalAnnualInterestRate,
account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType,
account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency,
account.lockinPeriodFrequencyType, account.withdrawalFeeAmount, account.withdrawalFeeType, account.annualFeeAmount,
account.annualFeeOnMonthDay, account.annualFeeNextDueDate, account.summary, transactions, productOptions,
fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions);
}
public static SavingsAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId,
@@ -119,10 +128,12 @@ public class SavingsAccountData {
final Long id = null;
final String accountNo = null;
final String externalId = null;
final SavingsAccountStatusEnumData status = null;
final LocalDate activationDate = null;
final Long productId = null;
final String productName = null;
final Long fieldOfficerId = null;
final String fieldOfficerName = null;
final SavingsAccountStatusEnumData status = null;
final SavingsAccountApplicationTimelineData timeline = null;
final CurrencyData currency = null;
final BigDecimal nominalAnnualInterestRate = null;
final EnumOptionData interestPeriodType = null;
@@ -142,6 +153,7 @@ public class SavingsAccountData {
final Collection<SavingsAccountTransactionData> transactions = null;
final Collection<SavingsProductData> productOptions = null;
final Collection<StaffData> fieldOfficerOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
@@ -149,25 +161,26 @@ public class SavingsAccountData {
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
return new SavingsAccountData(id, accountNo, externalId, status, activationDate, groupId, groupName, clientId, clientName,
productId, productName, currency, nominalAnnualInterestRate, interestPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, annualFeeOnMonthDay,
annualFeeNextDueDate, summary, transactions, productOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
return new SavingsAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount,
annualFeeOnMonthDay, annualFeeNextDueDate, summary, transactions, productOptions, fieldOfficerOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
}
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 nominalAnnualInterestRate,
final EnumOptionData interestPeriodType, final EnumOptionData interestPostingPeriodType,
final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType,
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency,
final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount, final EnumOptionData withdrawalFeeType,
final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay, final LocalDate annualFeeNextDueDate,
final SavingsAccountSummaryData summary, final Collection<SavingsAccountTransactionData> transactions,
final Collection<SavingsProductData> productOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
private SavingsAccountData(final Long id, final String accountNo, final String externalId, final Long groupId, final String groupName,
final Long clientId, final String clientName, final Long productId, final String productName, final Long fieldofficerId,
final String fieldofficerName, final SavingsAccountStatusEnumData status, final SavingsAccountApplicationTimelineData timeline,
final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestPeriodType,
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount,
final EnumOptionData withdrawalFeeType, final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay,
final LocalDate annualFeeNextDueDate, final SavingsAccountSummaryData summary,
final Collection<SavingsAccountTransactionData> transactions, final Collection<SavingsProductData> productOptions,
final Collection<StaffData> fieldOfficerOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
@@ -175,14 +188,16 @@ public class SavingsAccountData {
this.id = id;
this.accountNo = accountNo;
this.externalId = externalId;
this.status = status;
this.activationDate = activationDate;
this.groupId = groupId;
this.groupName = groupName;
this.clientId = clientId;
this.clientName = clientName;
this.savingsProductId = productId;
this.savingsProductName = productName;
this.fieldofficerId = fieldofficerId;
this.fieldofficerName = fieldofficerName;
this.status = status;
this.timeline = timeline;
this.currency = currency;
this.nominalAnnualInterestRate = nominalAnnualInterestRate;
this.interestCompoundingPeriodType = interestPeriodType;
@@ -202,6 +217,7 @@ public class SavingsAccountData {
this.transactions = transactions;
this.productOptions = productOptions;
this.fieldOfficerOptions = fieldOfficerOptions;
this.interestCompoundingPeriodTypeOptions = interestCompoundingPeriodTypeOptions;
this.interestPostingPeriodTypeOptions = interestPostingPeriodTypeOptions;
this.interestCalculationTypeOptions = interestCalculationTypeOptions;
@@ -5,25 +5,24 @@
*/
package org.mifosplatform.portfolio.savings.data;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.accountNoParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activationDateParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.clientIdParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.externalIdParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.groupIdParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.productIdParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountNoParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.clientIdParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.externalIdParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.groupIdParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.productIdParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.submittedOnDateParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeTypeParamName;
import java.lang.reflect.Type;
import java.math.BigDecimal;
@@ -39,7 +38,7 @@ import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.savings.api.SavingsApiConstants;
import org.mifosplatform.portfolio.savings.SavingsApiConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -56,7 +55,7 @@ public class SavingsAccountDataValidator {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateForCreate(final String json) {
public void validateForSubmit(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
@@ -85,13 +84,9 @@ public class SavingsAccountDataValidator {
final Long productId = fromApiJsonHelper.extractLongNamed(productIdParamName, element);
baseDataValidator.reset().parameter(productIdParamName).value(productId).notNull().integerGreaterThanZero();
final Boolean active = fromApiJsonHelper.extractBooleanNamed(activeParamName, element);
baseDataValidator.reset().parameter(activeParamName).value(active).notNull();
LocalDate activationDate = null;
if (active != null && active.booleanValue()) {
activationDate = fromApiJsonHelper.extractLocalDateNamed(activationDateParamName, element);
baseDataValidator.reset().parameter(activationDateParamName).value(activationDate).notNull();
final LocalDate submittedOnDate = fromApiJsonHelper.extractLocalDateNamed(submittedOnDateParamName, element);
if (submittedOnDate == null) {
baseDataValidator.reset().parameter(submittedOnDateParamName).value(submittedOnDate).notNull();
}
if (fromApiJsonHelper.parameterExists(accountNoParamName, element)) {
@@ -140,32 +135,32 @@ public class SavingsAccountDataValidator {
if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) {
final BigDecimal minOpeningBalance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minRequiredOpeningBalanceParamName,
element);
baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).positiveAmount();
baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) {
final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).ignoreIfNull()
.positiveAmount();
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).integerZeroOrGreater();
if (lockinPeriodFrequency != null) {
final Integer lockinPeriodFrequencyType = fromApiJsonHelper.extractIntegerSansLocaleNamed(
lockinPeriodFrequencyTypeParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).notNull()
.inMinMaxRange(1, 3);
.inMinMaxRange(0, 3);
}
}
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyTypeParamName, element)) {
final Integer lockinPeriodFrequencyType = fromApiJsonHelper.extractIntegerSansLocaleNamed(lockinPeriodFrequencyTypeParamName,
element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).inMinMaxRange(1, 3);
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).inMinMaxRange(0, 3);
if (lockinPeriodFrequencyType != null) {
final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName,
element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).notNull().positiveAmount();
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).notNull()
.integerZeroOrGreater();
}
}
@@ -173,17 +168,30 @@ public class SavingsAccountDataValidator {
final BigDecimal withdrawalFeeAmount = fromApiJsonHelper
.extractBigDecimalWithLocaleNamed(withdrawalFeeAmountParamName, element);
baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(withdrawalFeeAmount).positiveAmount();
baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(withdrawalFeeAmount).zeroOrPositiveAmount();
if (withdrawalFeeAmount != null) {
final Integer withdrawalFeeType = fromApiJsonHelper.extractIntegerSansLocaleNamed(withdrawalFeeTypeParamName, element);
baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType).isOneOfTheseValues(1, 2);
}
}
if (this.fromApiJsonHelper.parameterExists(withdrawalFeeTypeParamName, element)) {
final Integer withdrawalFeeType = fromApiJsonHelper.extractIntegerSansLocaleNamed(withdrawalFeeTypeParamName, element);
baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType).isOneOfTheseValues(1, 2);
baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType).ignoreIfNull()
.isOneOfTheseValues(1, 2);
if (withdrawalFeeType != null) {
final BigDecimal withdrawalFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(withdrawalFeeAmountParamName,
element);
baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(withdrawalFeeAmount).notNull()
.zeroOrPositiveAmount();
}
}
if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) {
final BigDecimal annualFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(annualFeeAmountParamName, element);
baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).positiveAmount();
baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).zeroOrPositiveAmount();
if (annualFeeAmount != null) {
MonthDay monthDayOfAnnualFee = fromApiJsonHelper.extractMonthDayNamed(annualFeeOnMonthDayParamName, element);
@@ -191,6 +199,15 @@ public class SavingsAccountDataValidator {
}
}
if (this.fromApiJsonHelper.parameterExists(annualFeeOnMonthDayParamName, element)) {
MonthDay monthDayOfAnnualFee = fromApiJsonHelper.extractMonthDayNamed(annualFeeOnMonthDayParamName, element);
if (monthDayOfAnnualFee != null) {
final BigDecimal annualFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(annualFeeAmountParamName, element);
baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).notNull().zeroOrPositiveAmount();
}
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
@@ -293,50 +310,38 @@ public class SavingsAccountDataValidator {
if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) {
final BigDecimal minOpeningBalance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minRequiredOpeningBalanceParamName,
element);
baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).positiveAmount();
baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).ignoreIfNull()
.zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) {
final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).ignoreIfNull()
.positiveAmount();
if (lockinPeriodFrequency != null) {
final Integer lockinPeriodFrequencyType = fromApiJsonHelper.extractIntegerSansLocaleNamed(
lockinPeriodFrequencyTypeParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).notNull()
.inMinMaxRange(1, 3);
}
.integerZeroOrGreater();
}
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyTypeParamName, element)) {
final Integer lockinPeriodFrequencyType = fromApiJsonHelper.extractIntegerSansLocaleNamed(lockinPeriodFrequencyTypeParamName,
element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).inMinMaxRange(1, 3);
if (lockinPeriodFrequencyType != null) {
final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName,
element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).notNull().positiveAmount();
}
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).inMinMaxRange(0, 3);
}
if (this.fromApiJsonHelper.parameterExists(withdrawalFeeAmountParamName, element)) {
final BigDecimal withdrawalFeeAmount = fromApiJsonHelper
.extractBigDecimalWithLocaleNamed(withdrawalFeeAmountParamName, element);
baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(withdrawalFeeAmount).notNull().positiveAmount();
baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(withdrawalFeeAmount).ignoreIfNull()
.zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(withdrawalFeeTypeParamName, element)) {
final Integer withdrawalFeeType = fromApiJsonHelper.extractIntegerSansLocaleNamed(withdrawalFeeTypeParamName, element);
baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType).isOneOfTheseValues(1, 2);
baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType).ignoreIfNull()
.isOneOfTheseValues(1, 2);
}
if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) {
final BigDecimal annualFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(annualFeeAmountParamName, element);
baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).ignoreIfNull().positiveAmount();
baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).ignoreIfNull().zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(annualFeeOnMonthDayParamName, element)) {
@@ -16,22 +16,31 @@ public class SavingsAccountStatusEnumData {
@SuppressWarnings("unused")
private final String value;
@SuppressWarnings("unused")
private final boolean unactivated;
private final boolean submittedAndPendingApproval;
@SuppressWarnings("unused")
private final boolean approved;
@SuppressWarnings("unused")
private final boolean rejected;
@SuppressWarnings("unused")
private final boolean withdrawnByApplicant;
@SuppressWarnings("unused")
private final boolean active;
@SuppressWarnings("unused")
private final boolean closed;
public SavingsAccountStatusEnumData(final Long id, final String code, final String value, final boolean unactivated,
final boolean active, final boolean closed) {
public SavingsAccountStatusEnumData(final Long id, final String code, final String value, final boolean submittedAndPendingApproval,
final boolean approved, final boolean rejected, final boolean withdrawnByApplicant, final boolean active, final boolean closed) {
this.id = id;
this.code = code;
this.value = value;
this.unactivated = unactivated;
this.submittedAndPendingApproval = submittedAndPendingApproval;
this.approved = approved;
this.rejected = rejected;
this.withdrawnByApplicant = withdrawnByApplicant;
this.active = active;
this.closed = closed;
}
public Long id() {
return this.id;
}
@@ -5,15 +5,15 @@
*/
package org.mifosplatform.portfolio.savings.data;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.accountNumberParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activationDateParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.bankNumberParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.checkNumberParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.paymentTypeIdParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.receiptNumberParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.routingCodeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.transactionAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.transactionDateParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.activationDateParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.bankNumberParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.checkNumberParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.paymentTypeIdParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.receiptNumberParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.routingCodeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.transactionAccountNumberParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.transactionAmountParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.transactionDateParamName;
import java.lang.reflect.Type;
import java.math.BigDecimal;
@@ -32,7 +32,7 @@ import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.savings.api.SavingsApiConstants;
import org.mifosplatform.portfolio.savings.SavingsApiConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -74,7 +74,7 @@ public class SavingsAccountTransactionDataValidator {
// Validate all string payment detail fields for max length
final Integer paymentTypeId = fromApiJsonHelper.extractIntegerWithLocaleNamed(paymentTypeIdParamName, element);
baseDataValidator.reset().parameter(paymentTypeIdParamName).value(paymentTypeId).ignoreIfNull().integerGreaterThanZero();
final Set<String> paymentDetailParameters = new HashSet<String>(Arrays.asList(accountNumberParamName, checkNumberParamName,
final Set<String> paymentDetailParameters = new HashSet<String>(Arrays.asList(transactionAccountNumberParamName, checkNumberParamName,
routingCodeParamName, receiptNumberParamName, bankNumberParamName));
for (String paymentDetailParameterName : paymentDetailParameters) {
final String paymentDetailParameterValue = fromApiJsonHelper.extractStringNamed(paymentDetailParameterName, element);
@@ -5,7 +5,7 @@
*/
package org.mifosplatform.portfolio.savings.data;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransactionType;
import org.mifosplatform.portfolio.savings.SavingsAccountTransactionType;
/**
* Immutable data object represent savings account transaction type
@@ -5,24 +5,24 @@
*/
package org.mifosplatform.portfolio.savings.data;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.currencyCodeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.descriptionParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.digitsAfterDecimalParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nameParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nameParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeTypeParamName;
import java.lang.reflect.Type;
import java.math.BigDecimal;
@@ -40,6 +40,7 @@ import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -90,7 +91,7 @@ public class SavingsProductDataValidator {
final Integer interestCompoundingPeriodType = fromApiJsonHelper.extractIntegerSansLocaleNamed(
interestCompoundingPeriodTypeParamName, element);
baseDataValidator.reset().parameter(interestCompoundingPeriodTypeParamName).value(interestCompoundingPeriodType).notNull()
.isOneOfTheseValues(new Object[] { 1, 2, 3, 4, 5, 6, 7, 8 });
.isOneOfTheseValues(SavingsCompoundingInterestPeriodType.integerValues());
final Integer interestPostingPeriodType = fromApiJsonHelper.extractIntegerSansLocaleNamed(interestPostingPeriodTypeParamName,
element);
@@ -109,14 +110,13 @@ public class SavingsProductDataValidator {
if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) {
final BigDecimal minOpeningBalance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minRequiredOpeningBalanceParamName,
element);
baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).positiveAmount();
baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) {
final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).ignoreIfNull()
.positiveAmount();
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).integerZeroOrGreater();
if (lockinPeriodFrequency != null) {
final Integer lockinPeriodFrequencyType = fromApiJsonHelper.extractIntegerSansLocaleNamed(
@@ -134,24 +134,39 @@ public class SavingsProductDataValidator {
if (lockinPeriodFrequencyType != null) {
final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName,
element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).notNull().positiveAmount();
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).notNull()
.integerZeroOrGreater();
}
}
if (this.fromApiJsonHelper.parameterExists(withdrawalFeeAmountParamName, element)) {
final BigDecimal withdrawalFeeAmount = fromApiJsonHelper
.extractBigDecimalWithLocaleNamed(withdrawalFeeAmountParamName, element);
baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(withdrawalFeeAmount).zeroOrPositiveAmount();
if (withdrawalFeeAmount != null) {
final Integer withdrawalFeeType = fromApiJsonHelper.extractIntegerSansLocaleNamed(withdrawalFeeTypeParamName, element);
baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType).isOneOfTheseValues(1, 2);
}
}
if (this.fromApiJsonHelper.parameterExists(withdrawalFeeTypeParamName, element)) {
final Integer withdrawalFeeType = fromApiJsonHelper.extractIntegerSansLocaleNamed(withdrawalFeeTypeParamName, element);
baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType).isOneOfTheseValues(1, 2);
baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType).ignoreIfNull()
.isOneOfTheseValues(1, 2);
if (withdrawalFeeType != null) {
final BigDecimal withdrawalFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(withdrawalFeeAmountParamName,
element);
baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(withdrawalFeeAmount).notNull().positiveAmount();
baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(withdrawalFeeAmount).notNull()
.zeroOrPositiveAmount();
}
}
if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) {
final BigDecimal annualFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(annualFeeAmountParamName, element);
baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).ignoreIfNull().positiveAmount();
baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).zeroOrPositiveAmount();
if (annualFeeAmount != null) {
MonthDay monthDayOfAnnualFee = fromApiJsonHelper.extractMonthDayNamed(annualFeeOnMonthDayParamName, element);
@@ -159,6 +174,15 @@ public class SavingsProductDataValidator {
}
}
if (this.fromApiJsonHelper.parameterExists(annualFeeOnMonthDayParamName, element)) {
MonthDay monthDayOfAnnualFee = fromApiJsonHelper.extractMonthDayNamed(annualFeeOnMonthDayParamName, element);
if (monthDayOfAnnualFee != null) {
final BigDecimal annualFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(annualFeeAmountParamName, element);
baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).notNull().zeroOrPositiveAmount();
}
}
// accounting related data validation
final Integer accountingRuleType = fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 3);
@@ -232,7 +256,7 @@ public class SavingsProductDataValidator {
final Integer interestCompoundingPeriodType = fromApiJsonHelper.extractIntegerSansLocaleNamed(
interestCompoundingPeriodTypeParamName, element);
baseDataValidator.reset().parameter(interestCompoundingPeriodTypeParamName).value(interestCompoundingPeriodType).notNull()
.inMinMaxRange(1, 8);
.isOneOfTheseValues(SavingsCompoundingInterestPeriodType.integerValues());
}
if (this.fromApiJsonHelper.parameterExists(interestCalculationTypeParamName, element)) {
@@ -249,11 +273,17 @@ public class SavingsProductDataValidator {
.notNull().isOneOfTheseValues(360, 365);
}
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) {
if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) {
final BigDecimal minOpeningBalance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minRequiredOpeningBalanceParamName,
element);
baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).ignoreIfNull()
.zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) {
final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).ignoreIfNull()
.positiveAmount();
.integerZeroOrGreater();
}
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyTypeParamName, element)) {
@@ -265,7 +295,8 @@ public class SavingsProductDataValidator {
if (this.fromApiJsonHelper.parameterExists(withdrawalFeeAmountParamName, element)) {
final BigDecimal withdrawalFeeAmount = fromApiJsonHelper
.extractBigDecimalWithLocaleNamed(withdrawalFeeAmountParamName, element);
baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(withdrawalFeeAmount).ignoreIfNull().positiveAmount();
baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(withdrawalFeeAmount).ignoreIfNull()
.zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(withdrawalFeeTypeParamName, element)) {
@@ -276,7 +307,7 @@ public class SavingsProductDataValidator {
if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) {
final BigDecimal annualFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(annualFeeAmountParamName, element);
baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).ignoreIfNull().positiveAmount();
baseDataValidator.reset().parameter(annualFeeAmountParamName).value(annualFeeAmount).ignoreIfNull().zeroOrPositiveAmount();
}
if (this.fromApiJsonHelper.parameterExists(annualFeeOnMonthDayParamName, element)) {
@@ -319,9 +350,6 @@ public class SavingsProductDataValidator {
/**
* Validation for advanced accounting options
*
* @param baseDataValidator
* @param element
*/
private void validatePaymentChannelFundSourceMappings(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
if (fromApiJsonHelper.parameterExists(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), element)) {
@@ -1,63 +0,0 @@
/**
* 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;
import java.math.BigDecimal;
import java.math.MathContext;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
public class InterestCompoundingPeriodSummary {
private final LocalDateInterval periodInterval;
@SuppressWarnings("unused")
private final BigDecimal openingBalance;
private final BigDecimal closingBalance;
private final BigDecimal interestEarnedUnrounded;
private final BigDecimal cumulativeCompoundedInterestToDate;
public static InterestCompoundingPeriodSummary create(final LocalDateInterval periodInterval, final BigDecimal openingBalance,
final BigDecimal closingBalance, final BigDecimal interestEarnedUnrounded, final BigDecimal cumulativeCompoundedInterestToDate) {
return new InterestCompoundingPeriodSummary(periodInterval, openingBalance, closingBalance, interestEarnedUnrounded,
cumulativeCompoundedInterestToDate);
}
public InterestCompoundingPeriodSummary(final LocalDateInterval periodInterval, final BigDecimal openingBalance,
final BigDecimal closingBalance, final BigDecimal interestEarnedUnrounded, final BigDecimal cumulativeCompoundedInterestToDate) {
this.periodInterval = periodInterval;
this.openingBalance = openingBalance;
this.closingBalance = closingBalance;
this.interestEarnedUnrounded = interestEarnedUnrounded;
this.cumulativeCompoundedInterestToDate = cumulativeCompoundedInterestToDate;
}
public Money closingBalance(final MonetaryCurrency currency) {
return Money.of(currency, this.closingBalance);
}
public BigDecimal closingBalance() {
return this.closingBalance;
}
public BigDecimal closingBalanceWithUnroundedInterest(final MathContext mc) {
return this.closingBalance.add(interestEarnedUnrounded, mc);
}
public BigDecimal compoundedInterest() {
return cumulativeCompoundedInterestToDate;
}
public BigDecimal interestUnrounded() {
return this.interestEarnedUnrounded;
}
public boolean fallsBefore(final LocalDate dateToCheck) {
return this.periodInterval.fallsBefore(dateToCheck);
}
}
@@ -5,36 +5,45 @@
*/
package org.mifosplatform.portfolio.savings.domain;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activationDateParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountNoParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.clientIdParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.externalIdParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.fieldOfficerIdParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.groupIdParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.productIdParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.submittedOnDateParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeTypeParamName;
import java.math.BigDecimal;
import java.util.List;
import java.util.Locale;
import org.joda.time.LocalDate;
import org.joda.time.MonthDay;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.organisation.staff.domain.Staff;
import org.mifosplatform.organisation.staff.domain.StaffRepositoryWrapper;
import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper;
import org.mifosplatform.portfolio.group.domain.Group;
import org.mifosplatform.portfolio.group.domain.GroupRepository;
import org.mifosplatform.portfolio.group.exception.GroupNotFoundException;
import org.mifosplatform.portfolio.group.domain.GroupRepositoryWrapper;
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType;
import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType;
import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType;
import org.mifosplatform.portfolio.savings.SavingsWithdrawalFeesType;
import org.mifosplatform.portfolio.savings.exception.SavingsProductNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -46,18 +55,24 @@ public class SavingsAccountAssembler {
private final SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper;
private final ClientRepositoryWrapper clientRepository;
private final GroupRepository groupRepository;
private final GroupRepositoryWrapper groupRepository;
private final StaffRepositoryWrapper staffRepository;
private final SavingsProductRepository savingProductRepository;
private final SavingsAccountRepositoryWrapper savingsAccountRepository;
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public SavingsAccountAssembler(final SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper,
final ClientRepositoryWrapper clientRepository, final GroupRepository groupRepository,
final SavingsProductRepository savingProductRepository, final FromJsonHelper fromApiJsonHelper) {
final ClientRepositoryWrapper clientRepository, final GroupRepositoryWrapper groupRepository,
final StaffRepositoryWrapper staffRepository, final SavingsProductRepository savingProductRepository,
final SavingsAccountRepositoryWrapper savingsAccountRepository,
final FromJsonHelper fromApiJsonHelper) {
this.savingsAccountTransactionSummaryWrapper = savingsAccountTransactionSummaryWrapper;
this.clientRepository = clientRepository;
this.groupRepository = groupRepository;
this.staffRepository = staffRepository;
this.savingProductRepository = savingProductRepository;
this.savingsAccountRepository = savingsAccountRepository;
this.fromApiJsonHelper = fromApiJsonHelper;
}
@@ -65,36 +80,39 @@ public class SavingsAccountAssembler {
* Assembles a new {@link SavingsAccount} from JSON details passed in
* request inheriting details where relevant from chosen
* {@link SavingsProduct}.
*
* @param existingReversedTransactionIds
* @param existingTransactionIds
*/
public SavingsAccount assembleFrom(final JsonCommand command, List<Long> existingTransactionIds,
List<Long> existingReversedTransactionIds) {
public SavingsAccount assembleFrom(final JsonCommand command) {
final JsonElement element = command.parsedJson();
final String accountNo = fromApiJsonHelper.extractStringNamed("accountNo", element);
final String externalId = fromApiJsonHelper.extractStringNamed("externalId", element);
final Long productId = fromApiJsonHelper.extractLongNamed("productId", element);
final String accountNo = fromApiJsonHelper.extractStringNamed(accountNoParamName, element);
final String externalId = fromApiJsonHelper.extractStringNamed(externalIdParamName, element);
final Long productId = fromApiJsonHelper.extractLongNamed(productIdParamName, element);
final SavingsProduct product = this.savingProductRepository.findOne(productId);
if (product == null) { throw new SavingsProductNotFoundException(productId); }
final Long clientId = fromApiJsonHelper.extractLongNamed("clientId", element);
final Long groupId = fromApiJsonHelper.extractLongNamed("groupId", element);
Client client = null;
Group group = null;
Staff fieldOfficer = null;
final Long clientId = fromApiJsonHelper.extractLongNamed(clientIdParamName, element);
if (clientId != null) {
client = this.clientRepository.findOneWithNotFoundDetection(clientId);
}
final Long groupId = fromApiJsonHelper.extractLongNamed(groupIdParamName, element);
if (groupId != null) {
group = this.groupRepository.findOne(groupId);
if (group == null) { throw new GroupNotFoundException(groupId); }
group = this.groupRepository.findOneWithNotFoundDetection(groupId);
}
final Long fieldOfficerId = fromApiJsonHelper.extractLongNamed(fieldOfficerIdParamName, element);
if (fieldOfficerId != null) {
fieldOfficer = this.staffRepository.findOneWithNotFoundDetection(fieldOfficerId);
}
final LocalDate submittedOnDate = fromApiJsonHelper.extractLocalDateNamed(submittedOnDateParamName, element);
BigDecimal interestRate = null;
if (command.parameterExists(nominalAnnualInterestRateParamName)) {
interestRate = command.bigDecimalValueOfParameterNamed(nominalAnnualInterestRateParamName);
@@ -102,9 +120,6 @@ public class SavingsAccountAssembler {
interestRate = product.nominalAnnualInterestRate();
}
final boolean active = fromApiJsonHelper.extractBooleanNamed(activeParamName, element);
final LocalDate activationDate = fromApiJsonHelper.extractLocalDateNamed(activationDateParamName, element);
SavingsCompoundingInterestPeriodType interestCompoundingPeriodType = null;
final Integer interestPeriodTypeValue = command.integerValueOfParameterNamed(interestCompoundingPeriodTypeParamName);
if (interestPeriodTypeValue != null) {
@@ -113,10 +128,10 @@ public class SavingsAccountAssembler {
interestCompoundingPeriodType = product.interestCompoundingPeriodType();
}
SavingsInterestPostingPeriodType interestPostingPeriodType = null;
SavingsPostingInterestPeriodType interestPostingPeriodType = null;
final Integer interestPostingPeriodTypeValue = command.integerValueOfParameterNamed(interestPostingPeriodTypeParamName);
if (interestPostingPeriodTypeValue != null) {
interestPostingPeriodType = SavingsInterestPostingPeriodType.fromInt(interestPostingPeriodTypeValue);
interestPostingPeriodType = SavingsPostingInterestPeriodType.fromInt(interestPostingPeriodTypeValue);
} else {
interestPostingPeriodType = product.interestPostingPeriodType();
}
@@ -194,17 +209,20 @@ public class SavingsAccountAssembler {
monthDayOfAnnualFee = product.monthDayOfAnnualFee();
}
final SavingsAccount account = SavingsAccount.createNewAccount(client, group, product, accountNo, externalId, interestRate,
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType,
annualFeeAmount, monthDayOfAnnualFee);
final SavingsAccount account = SavingsAccount.createNewApplicationForSubmittal(client, group, product, fieldOfficer, accountNo, externalId,
submittedOnDate, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, monthDayOfAnnualFee);
account.setHelpers(this.savingsAccountTransactionSummaryWrapper);
if (active) {
final Locale locale = command.extractLocale();
final DateTimeFormatter formatter = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
account.activate(formatter, activationDate, existingTransactionIds, existingReversedTransactionIds);
}
account.validateNewApplicationState(DateUtils.getLocalDateOfTenant());
return account;
}
public SavingsAccount assembleFrom(final Long savingsId) {
SavingsAccount account = this.savingsAccountRepository.findOneWithNotFoundDetection(savingsId);
account.setHelpers(this.savingsAccountTransactionSummaryWrapper);
return account;
}
@@ -1,65 +0,0 @@
/**
* 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;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
public class SavingsAccountDailyBalance {
private final LocalDateInterval interestPeriodInterval;
private final BigDecimal endOfDayBalance;
private final BigDecimal cumulativeBalance;
private final BigDecimal compoundedInterestToDate;
public static SavingsAccountDailyBalance createFrom(final LocalDateInterval interestPeriodInterval, final BigDecimal endOfDayBalance,
final BigDecimal compoundedInterestToDate) {
return new SavingsAccountDailyBalance(interestPeriodInterval, endOfDayBalance, compoundedInterestToDate);
}
private SavingsAccountDailyBalance(final LocalDateInterval interestPeriodInterval, final BigDecimal endOfDayBalance,
final BigDecimal compoundedInterestToDate) {
this.interestPeriodInterval = interestPeriodInterval;
this.endOfDayBalance = endOfDayBalance;
this.compoundedInterestToDate = compoundedInterestToDate;
final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN);
final BigDecimal numberOfDaysBigDecimal = BigDecimal.valueOf(interestPeriodInterval.daysInPeriodInclusiveOfEndDate());
this.cumulativeBalance = endOfDayBalance.multiply(numberOfDaysBigDecimal, mc);
}
public Money dailyRunningBalance(final MonetaryCurrency currency) {
return Money.of(currency, endOfDayBalance);
}
public BigDecimal endOfDayBalance() {
return this.endOfDayBalance;
}
public BigDecimal cumulativeBalance() {
return this.cumulativeBalance;
}
public Integer numberOfDays() {
return interestPeriodInterval.daysInPeriodInclusiveOfEndDate();
}
public boolean isGreaterThanZero() {
return this.endOfDayBalance.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());
final BigDecimal compoundedEndOfDayBalance = this.endOfDayBalance.add(this.compoundedInterestToDate, mc);
return compoundedEndOfDayBalance.multiply(dailyInterestRate, mc).multiply(numberOfDaysBigDecimal, mc);
}
}
@@ -11,8 +11,11 @@ package org.mifosplatform.portfolio.savings.domain;
public enum SavingsAccountStatusType {
INVALID(0, "savingsAccountStatusType.invalid"), //
UNACTIVATED(100, "savingsAccountStatusType.unactivated"), //
SUBMITTED_AND_PENDING_APPROVAL(100, "savingsAccountStatusType.submitted.and.pending.approval"), //
APPROVED(200, "savingsAccountStatusType.approved"), //
ACTIVE(300, "savingsAccountStatusType.active"), //
WITHDRAWN_BY_APPLICANT(400, "savingsAccountStatusType.withdrawn.by.applicant"), //
REJECTED(500, "savingsAccountStatusType.rejected"), //
CLOSED(600, "savingsAccountStatusType.closed");
private final Integer value;
@@ -23,11 +26,20 @@ public enum SavingsAccountStatusType {
SavingsAccountStatusType enumeration = SavingsAccountStatusType.INVALID;
switch (type) {
case 100:
enumeration = SavingsAccountStatusType.UNACTIVATED;
enumeration = SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL;
break;
case 200:
enumeration = SavingsAccountStatusType.APPROVED;
break;
case 300:
enumeration = SavingsAccountStatusType.ACTIVE;
break;
case 400:
enumeration = SavingsAccountStatusType.WITHDRAWN_BY_APPLICANT;
break;
case 500:
enumeration = SavingsAccountStatusType.REJECTED;
break;
case 600:
enumeration = SavingsAccountStatusType.CLOSED;
break;
@@ -52,8 +64,20 @@ public enum SavingsAccountStatusType {
return code;
}
public boolean isUnactivated() {
return this.value.equals(SavingsAccountStatusType.UNACTIVATED.getValue());
public boolean isSubmittedAndPendingApproval() {
return this.value.equals(SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.getValue());
}
public boolean isApproved() {
return this.value.equals(SavingsAccountStatusType.APPROVED.getValue());
}
public boolean isRejected() {
return this.value.equals(SavingsAccountStatusType.REJECTED.getValue());
}
public boolean isApplicationWithdrawnByApplicant() {
return this.value.equals(SavingsAccountStatusType.WITHDRAWN_BY_APPLICANT.getValue());
}
public boolean isActive() {
@@ -13,6 +13,7 @@ import javax.persistence.Embeddable;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.portfolio.savings.domain.interest.PostingPeriod;
/**
* {@link SavingsAccountSummary} encapsulates all the summary details of a
@@ -60,19 +61,15 @@ public final class SavingsAccountSummary {
.minus(this.totalWithdrawalFees).minus(this.totalAnnualFees).getAmount();
}
public void updateFromInterestPeriodSummaries(final MonetaryCurrency currency,
final List<InterestCompoundingPeriodSummary> compoundingPeriods) {
public void updateFromInterestPeriodSummaries(final MonetaryCurrency currency, final List<PostingPeriod> allPostingPeriods) {
// get the max compounded interest
final BigDecimal compoundedInterestToDate = compoundingPeriods.get(compoundingPeriods.size() - 1).compoundedInterest();
final Money maxIntrestCompoundedToDate = Money.of(currency, compoundedInterestToDate);
Money totalEarned = Money.zero(currency);
BigDecimal totalInterestUnrounded = BigDecimal.ZERO;
for (InterestCompoundingPeriodSummary interestPeriodSummary : compoundingPeriods) {
totalInterestUnrounded = totalInterestUnrounded.add(interestPeriodSummary.interestUnrounded());
for (PostingPeriod period : allPostingPeriods) {
totalEarned = totalEarned.plus(period.interest());
}
this.totalInterestEarned = maxIntrestCompoundedToDate.getAmountDefaultedToNullIfZero();
this.totalInterestEarned = totalEarned.getAmount();
}
public boolean isLessThanOrEqualToAccountBalance(final Money amount) {
@@ -24,7 +24,9 @@ import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
import org.mifosplatform.portfolio.savings.SavingsAccountTransactionType;
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionEnumData;
import org.mifosplatform.portfolio.savings.domain.interest.EndOfDayBalance;
import org.mifosplatform.portfolio.savings.service.SavingsEnumerations;
import org.springframework.data.jpa.domain.AbstractPersistable;
@@ -205,27 +207,6 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
return this.balanceNumberOfDays != null && this.balanceNumberOfDays > 0;
}
public SavingsAccountDailyBalance toDailyBalance(final LocalDate periodEnd) {
LocalDate balanceValidTo = getEndOfBalanceLocalDate();
if (periodEnd.isBefore(getEndOfBalanceLocalDate())) {
balanceValidTo = periodEnd;
}
final LocalDateInterval interestPeriodInterval = LocalDateInterval.create(getTransactionLocalDate(), balanceValidTo);
return SavingsAccountDailyBalance.createFrom(interestPeriodInterval, this.runningBalance, BigDecimal.ZERO);
}
public SavingsAccountDailyBalance toCompoundedDailyBalance(final LocalDate periodEnd, final BigDecimal compoundedInterestToDate) {
LocalDate balanceValidTo = getEndOfBalanceLocalDate();
if (periodEnd.isBefore(getEndOfBalanceLocalDate())) {
balanceValidTo = periodEnd;
}
final LocalDateInterval interestPeriodInterval = LocalDateInterval.create(getTransactionLocalDate(), balanceValidTo);
return SavingsAccountDailyBalance.createFrom(interestPeriodInterval, this.runningBalance, compoundedInterestToDate);
}
public boolean hasNotAmount(final Money amountToCheck) {
final Money transactionAmount = getAmount(amountToCheck.getCurrency());
return transactionAmount.isNotEqualTo(amountToCheck);
@@ -249,4 +230,99 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
return thisTransactionData;
}
public boolean isAfter(final LocalDate transactionDate) {
return this.getTransactionLocalDate().isAfter(transactionDate);
}
public EndOfDayBalance toEndOfDayBalance(final LocalDateInterval periodInterval, final MonetaryCurrency currency) {
Money endOfDayBalance = Money.of(currency, this.runningBalance);
Money openingBalance = endOfDayBalance;
LocalDate balanceDate = periodInterval.startDate();
int numberOfDays = periodInterval.daysInPeriodInclusiveOfEndDate();
if (periodInterval.contains(getTransactionLocalDate())) {
balanceDate = getTransactionLocalDate();
LocalDateInterval newInterval = LocalDateInterval.create(getTransactionLocalDate(), periodInterval.endDate());
numberOfDays = newInterval.daysInPeriodInclusiveOfEndDate();
}
return EndOfDayBalance.from(balanceDate, openingBalance, endOfDayBalance, numberOfDays);
}
public EndOfDayBalance toEndOfDayBalance(final Money openingBalance, final LocalDate nextTransactionDate) {
MonetaryCurrency currency = openingBalance.getCurrency();
Money endOfDayBalance = openingBalance.copy();
if (isDeposit()) {
endOfDayBalance = openingBalance.plus(getAmount(currency));
} else if (isWithdrawal() || isWithdrawalFee()) {
endOfDayBalance = openingBalance.minus(getAmount(currency));
}
int numberOfDays = LocalDateInterval.create(getTransactionLocalDate(), nextTransactionDate).daysInPeriodInclusiveOfEndDate();
if (!openingBalance.isEqualTo(endOfDayBalance) && numberOfDays > 1) {
numberOfDays = numberOfDays - 1;
}
return EndOfDayBalance.from(getTransactionLocalDate(), openingBalance, endOfDayBalance, numberOfDays);
}
public EndOfDayBalance toEndOfDayBalance(final Money openingBalance) {
MonetaryCurrency currency = openingBalance.getCurrency();
Money endOfDayBalance = openingBalance.copy();
if (isDeposit()) {
endOfDayBalance = openingBalance.plus(getAmount(currency));
} else if (isWithdrawal() || isWithdrawalFee()) {
endOfDayBalance = openingBalance.minus(getAmount(currency));
}
return EndOfDayBalance.from(getTransactionLocalDate(), openingBalance, endOfDayBalance, this.balanceNumberOfDays);
}
public EndOfDayBalance toEndOfDayBalanceBoundedBy(final Money openingBalance, final LocalDateInterval boundedBy) {
MonetaryCurrency currency = openingBalance.getCurrency();
Money endOfDayBalance = openingBalance.copy();
int numberOfDaysOfBalance = this.balanceNumberOfDays;
LocalDate balanceStartDate = getTransactionLocalDate();
LocalDate balanceEndDate = getEndOfBalanceLocalDate();
if (boundedBy.startDate().isAfter(balanceStartDate)) {
balanceStartDate = boundedBy.startDate();
LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
} else {
if (isDeposit()) {
endOfDayBalance = openingBalance.plus(getAmount(currency));
} else if (isWithdrawal() || isWithdrawalFee()) {
endOfDayBalance = openingBalance.minus(getAmount(currency));
}
}
if (balanceEndDate.isAfter(boundedBy.endDate())) {
balanceEndDate = boundedBy.endDate();
LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
}
return EndOfDayBalance.from(balanceStartDate, openingBalance, endOfDayBalance, numberOfDaysOfBalance);
}
public boolean isBalanceInExistencesForOneDayOrMore() {
return this.balanceNumberOfDays != null && this.balanceNumberOfDays.intValue() >= 1;
}
public boolean fallsWithin(final LocalDateInterval periodInterval) {
LocalDateInterval balanceInterval = LocalDateInterval.create(getTransactionLocalDate(), getEndOfBalanceLocalDate());
return periodInterval.contains(balanceInterval);
}
public boolean spansAnyPortionOf(final LocalDateInterval periodInterval) {
LocalDateInterval balanceInterval = LocalDateInterval.create(getTransactionLocalDate(), getEndOfBalanceLocalDate());
return balanceInterval.containsPortionOf(periodInterval);
}
}
@@ -1,15 +0,0 @@
/**
* 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;
import java.math.BigDecimal;
import java.util.List;
public interface SavingsCompoundInterestCalculator {
BigDecimal calculate(List<SavingsAccountDailyBalance> dailyBalances, Integer numberOfDays);
}
@@ -1,49 +0,0 @@
/**
* 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;
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 SavingsCompoundInterestCalculatorForAverageBalance implements SavingsCompoundInterestCalculator {
private final BigDecimal periodsInOneYearAsFraction;
private final BigDecimal annualInterestRateAsFraction;
public SavingsCompoundInterestCalculatorForAverageBalance(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);
}
}
@@ -1,44 +0,0 @@
/**
* 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;
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 SavingsCompoundInterestCalculatorForDailyBalance implements SavingsCompoundInterestCalculator {
private final BigDecimal periodsInOneYearAsFraction;
private final BigDecimal annualInterestRateAsFraction;
public SavingsCompoundInterestCalculatorForDailyBalance(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, balance.numberOfDays());
interestCalculated = interestCalculated.add(interestForBalance, mc);
}
}
return interestCalculated;
}
}
@@ -0,0 +1,88 @@
package org.mifosplatform.portfolio.savings.domain;
import java.util.ArrayList;
import java.util.List;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType;
import org.mifosplatform.portfolio.savings.domain.interest.CompoundInterestHelper;
import org.mifosplatform.portfolio.savings.domain.interest.PostingPeriod;
public final class SavingsHelper {
private final CompoundInterestHelper compoundInterestHelper = new CompoundInterestHelper();
public List<LocalDateInterval> determineInterestPostingPeriods(final LocalDate activationLocalDate,
final LocalDate interestPostingUpToDate, final SavingsPostingInterestPeriodType postingPeriodType) {
List<LocalDateInterval> postingPeriods = new ArrayList<LocalDateInterval>();
LocalDate periodStartDate = activationLocalDate;
LocalDate periodEndDate = periodStartDate;
while (!periodStartDate.isAfter(interestPostingUpToDate) && !periodEndDate.isAfter(interestPostingUpToDate)) {
final LocalDate interestPostingLocalDate = determineInterestPostingPeriodEndDateFrom(periodStartDate, postingPeriodType);
periodEndDate = interestPostingLocalDate.minusDays(1);
if (!interestPostingLocalDate.isAfter(DateUtils.getLocalDateOfTenant())) {
postingPeriods.add(LocalDateInterval.create(periodStartDate, periodEndDate));
} else {
postingPeriods.add(LocalDateInterval.create(periodStartDate, periodEndDate));
}
periodEndDate = interestPostingLocalDate;
periodStartDate = interestPostingLocalDate;
}
return postingPeriods;
}
private LocalDate determineInterestPostingPeriodEndDateFrom(final LocalDate periodStartDate,
final SavingsPostingInterestPeriodType interestPostingPeriodType) {
LocalDate periodEndDate = DateUtils.getLocalDateOfTenant();
switch (interestPostingPeriodType) {
case INVALID:
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 ANNUAL:
periodEndDate = periodStartDate.monthOfYear().withMaximumValue();
periodEndDate = periodEndDate.dayOfMonth().withMaximumValue();
break;
}
// interest posting always occurs on next day after the period end date.
periodEndDate = periodEndDate.plusDays(1);
return periodEndDate;
}
public Money calculateInterestForAllPostingPeriods(final MonetaryCurrency currency, final List<PostingPeriod> allPeriods) {
return this.compoundInterestHelper.calculateInterestForAllPostingPeriods(currency, allPeriods);
}
}
@@ -1,32 +0,0 @@
/**
* 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;
import java.math.BigDecimal;
public class SavingsInterestCalculatorFactory {
public SavingsCompoundInterestCalculator createFrom(final SavingsInterestCalculationType interestCalculationType,
final BigDecimal periodsInOneYearAsFraction, final BigDecimal annualInterestRateAsFraction) {
SavingsCompoundInterestCalculator savingsInterestCalculator = new SavingsCompoundInterestCalculatorForDailyBalance(periodsInOneYearAsFraction,
annualInterestRateAsFraction);
switch (interestCalculationType) {
case AVERAGE_DAILY_BALANCE:
savingsInterestCalculator = new SavingsCompoundInterestCalculatorForAverageBalance(periodsInOneYearAsFraction,
annualInterestRateAsFraction);
break;
case DAILY_BALANCE:
savingsInterestCalculator = new SavingsCompoundInterestCalculatorForDailyBalance(periodsInOneYearAsFraction,
annualInterestRateAsFraction);
break;
case INVALID:
break;
}
return savingsInterestCalculator;
}
}
@@ -1,56 +0,0 @@
/**
* 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;
/**
* The interest posting period is the span of time at the end of which savings
* earned but not yet credited/posted in a client's account is credited/posted.
*/
public enum SavingsInterestPostingPeriodType {
INVALID(0, "savingsInterestPostingPeriodType.invalid"), //
MONTHLY(4, "savingsInterestPostingPeriodType.monthly"), //
QUATERLY(5, "savingsInterestPostingPeriodType.quarterly"), //
BI_ANNUAL(6, "savingsInterestPostingPeriodType.biannual"), //
ANNUAL(7, "savingsInterestPostingPeriodType.annual");
private final Integer value;
private final String code;
private SavingsInterestPostingPeriodType(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 SavingsInterestPostingPeriodType fromInt(final Integer type) {
SavingsInterestPostingPeriodType repaymentFrequencyType = SavingsInterestPostingPeriodType.INVALID;
if (type != null) {
switch (type) {
case 4:
repaymentFrequencyType = SavingsInterestPostingPeriodType.MONTHLY;
break;
case 5:
repaymentFrequencyType = SavingsInterestPostingPeriodType.QUATERLY;
break;
case 6:
repaymentFrequencyType = SavingsInterestPostingPeriodType.BI_ANNUAL;
break;
case 7:
repaymentFrequencyType = SavingsInterestPostingPeriodType.ANNUAL;
break;
}
}
return repaymentFrequencyType;
}
}
@@ -5,27 +5,30 @@
*/
package org.mifosplatform.portfolio.savings.domain;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.currencyCodeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.descriptionParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.digitsAfterDecimalParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.localeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nameParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.accountingRuleParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.localeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nameParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeTypeParamName;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.Column;
@@ -36,9 +39,17 @@ import javax.persistence.Table;
import org.joda.time.MonthDay;
import org.mifosplatform.accounting.common.AccountingRuleType;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.exception.GeneralPlatformDomainRuleException;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType;
import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType;
import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType;
import org.mifosplatform.portfolio.savings.SavingsWithdrawalFeesType;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@@ -68,7 +79,7 @@ public class SavingsProduct extends AbstractPersistable<Long> {
private Integer interestCompoundingPeriodType;
/**
* A value from the {@link SavingsInterestPostingPeriodType} enumeration.
* A value from the {@link SavingsPostingInterestPeriodType} enumeration.
*/
@Column(name = "interest_posting_period_enum", nullable = false)
private Integer interestPostingPeriodType;
@@ -118,7 +129,7 @@ public class SavingsProduct extends AbstractPersistable<Long> {
public static SavingsProduct createNew(final String name, final String description, final MonetaryCurrency currency,
final BigDecimal interestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType, final BigDecimal annualFeeAmount,
@@ -136,7 +147,7 @@ public class SavingsProduct extends AbstractPersistable<Long> {
private SavingsProduct(final String name, final String description, final MonetaryCurrency currency, final BigDecimal interestRate,
final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType, final BigDecimal annualFeeAmount,
@@ -157,16 +168,16 @@ public class SavingsProduct extends AbstractPersistable<Long> {
}
this.lockinPeriodFrequency = lockinPeriodFrequency;
if (lockinPeriodFrequencyType != null) {
if (lockinPeriodFrequency != null && lockinPeriodFrequencyType != null) {
this.lockinPeriodFrequencyType = lockinPeriodFrequencyType.getValue();
}
this.withdrawalFeeAmount = withdrawalFeeAmount;
if (withdrawalFeeType != null) {
if (withdrawalFeeAmount != null && withdrawalFeeType != null) {
this.withdrawalFeeType = withdrawalFeeType.getValue();
}
this.annualFeeAmount = annualFeeAmount;
if (annualFeeOnMonthDay != null) {
if (annualFeeAmount != null && annualFeeOnMonthDay != null) {
this.annualFeeOnMonth = annualFeeOnMonthDay.getMonthOfYear();
this.annualFeeOnDay = annualFeeOnMonthDay.getDayOfMonth();
}
@@ -192,8 +203,8 @@ public class SavingsProduct extends AbstractPersistable<Long> {
return SavingsCompoundingInterestPeriodType.fromInt(this.interestCompoundingPeriodType);
}
public SavingsInterestPostingPeriodType interestPostingPeriodType() {
return SavingsInterestPostingPeriodType.fromInt(this.interestPostingPeriodType);
public SavingsPostingInterestPeriodType interestPostingPeriodType() {
return SavingsPostingInterestPeriodType.fromInt(this.interestPostingPeriodType);
}
public SavingsInterestCalculationType interestCalculationType() {
@@ -295,7 +306,7 @@ public class SavingsProduct extends AbstractPersistable<Long> {
if (command.isChangeInIntegerParameterNamed(interestPostingPeriodTypeParamName, this.interestPostingPeriodType)) {
final Integer newValue = command.integerValueOfParameterNamed(interestPostingPeriodTypeParamName);
actualChanges.put(interestPostingPeriodTypeParamName, newValue);
this.interestPostingPeriodType = SavingsInterestPostingPeriodType.fromInt(newValue).getValue();
this.interestPostingPeriodType = SavingsPostingInterestPeriodType.fromInt(newValue).getValue();
}
if (command.isChangeInIntegerParameterNamed(interestCalculationTypeParamName, this.interestCalculationType)) {
@@ -310,15 +321,16 @@ public class SavingsProduct extends AbstractPersistable<Long> {
this.interestCalculationDaysInYearType = SavingsInterestCalculationDaysInYearType.fromInt(newValue).getValue();
}
if (command.isChangeInBigDecimalParameterNamed(minRequiredOpeningBalanceParamName, this.minRequiredOpeningBalance)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(minRequiredOpeningBalanceParamName);
if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(minRequiredOpeningBalanceParamName,
this.minRequiredOpeningBalance)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minRequiredOpeningBalanceParamName);
actualChanges.put(minRequiredOpeningBalanceParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
this.minRequiredOpeningBalance = newValue;
}
if (command.isChangeInIntegerParameterNamed(lockinPeriodFrequencyParamName, this.lockinPeriodFrequency)) {
final Integer newValue = command.integerValueOfParameterNamed(lockinPeriodFrequencyParamName);
if (command.isChangeInIntegerParameterNamedDefaultingZeroToNull(lockinPeriodFrequencyParamName, this.lockinPeriodFrequency)) {
final Integer newValue = command.integerValueOfParameterNamedDefaultToNullIfZero(lockinPeriodFrequencyParamName);
actualChanges.put(lockinPeriodFrequencyParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
this.lockinPeriodFrequency = newValue;
@@ -327,47 +339,63 @@ public class SavingsProduct extends AbstractPersistable<Long> {
if (command.isChangeInIntegerParameterNamed(lockinPeriodFrequencyTypeParamName, this.lockinPeriodFrequencyType)) {
final Integer newValue = command.integerValueOfParameterNamed(lockinPeriodFrequencyTypeParamName);
actualChanges.put(lockinPeriodFrequencyTypeParamName, newValue);
this.lockinPeriodFrequencyType = SavingsPeriodFrequencyType.fromInt(newValue).getValue();
this.lockinPeriodFrequencyType = newValue != null ? SavingsPeriodFrequencyType.fromInt(newValue).getValue() : newValue;
}
if (command.isChangeInBigDecimalParameterNamed(withdrawalFeeAmountParamName, this.withdrawalFeeAmount)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(withdrawalFeeAmountParamName);
// set period type to null if frequency is null
if (this.lockinPeriodFrequency == null) {
this.lockinPeriodFrequencyType = null;
}
if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(withdrawalFeeAmountParamName, this.withdrawalFeeAmount)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(withdrawalFeeAmountParamName);
actualChanges.put(withdrawalFeeAmountParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
this.withdrawalFeeAmount = newValue;
}
if (command.isChangeInIntegerParameterNamed(withdrawalFeeTypeParamName, this.withdrawalFeeType)) {
final Integer newValue = command.integerValueOfParameterNamed(withdrawalFeeTypeParamName);
if (command.isChangeInIntegerParameterNamedDefaultingZeroToNull(withdrawalFeeTypeParamName, this.withdrawalFeeType)) {
final Integer newValue = command.integerValueOfParameterNamedDefaultToNullIfZero(withdrawalFeeTypeParamName);
actualChanges.put(withdrawalFeeTypeParamName, newValue);
this.withdrawalFeeType = newValue != null? SavingsWithdrawalFeesType.fromInt(newValue).getValue(): newValue;
this.withdrawalFeeType = newValue != null ? SavingsWithdrawalFeesType.fromInt(newValue).getValue() : newValue;
}
if (command.isChangeInBigDecimalParameterNamed(annualFeeAmountParamName, this.annualFeeAmount)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(annualFeeAmountParamName);
// set period type to null if frequency is null
if (this.withdrawalFeeAmount == null) {
this.withdrawalFeeType = null;
}
if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(annualFeeAmountParamName, this.annualFeeAmount)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(annualFeeAmountParamName);
actualChanges.put(annualFeeAmountParamName, newValue);
actualChanges.put(localeParamName, localeAsInput);
this.annualFeeAmount = newValue;
}
if (command.isChangeInIntegerParameterNamed(annualFeeOnMonthDayParamName, this.annualFeeOnDay)) {
if (command.isChangeInIntegerParameterNamedDefaultingZeroToNull(annualFeeOnMonthDayParamName, this.annualFeeOnDay)) {
final MonthDay monthDay = command.extractMonthDayNamed(annualFeeOnMonthDayParamName);
final String actualValueEntered = command.stringValueOfParameterNamed(annualFeeOnMonthDayParamName);
final Integer newValue = monthDay != null? monthDay.getDayOfMonth(): null;
final Integer newValue = monthDay != null ? monthDay.getDayOfMonth() : null;
actualChanges.put(annualFeeOnMonthDayParamName, actualValueEntered);
actualChanges.put(localeParamName, localeAsInput);
this.annualFeeOnDay = newValue;
}
if (command.isChangeInIntegerParameterNamed(annualFeeOnMonthDayParamName, this.annualFeeOnMonth)) {
if (command.isChangeInIntegerParameterNamedDefaultingZeroToNull(annualFeeOnMonthDayParamName, this.annualFeeOnMonth)) {
final MonthDay monthDay = command.extractMonthDayNamed(annualFeeOnMonthDayParamName);
final String actualValueEntered = command.stringValueOfParameterNamed(annualFeeOnMonthDayParamName);
final Integer newValue = monthDay != null? monthDay.getMonthOfYear(): null;
final Integer newValue = monthDay != null ? monthDay.getMonthOfYear() : null;
actualChanges.put(annualFeeOnMonthDayParamName, actualValueEntered);
actualChanges.put(localeParamName, localeAsInput);
this.annualFeeOnMonth = newValue;
}
// set period type to null if frequency is null
if (this.annualFeeAmount == null) {
this.annualFeeOnDay = null;
this.annualFeeOnMonth = null;
}
if (command.isChangeInIntegerParameterNamed(accountingRuleParamName, this.accountingRule)) {
final Integer newValue = command.integerValueOfParameterNamed(accountingRuleParamName);
actualChanges.put(accountingRuleParamName, newValue);
@@ -382,42 +410,72 @@ public class SavingsProduct extends AbstractPersistable<Long> {
}
private void validateAnnualFeeDetails() {
if (isInvalidConfigurationOfAnnualFeeSettings()) {
Object[] defaultUserMessageArgs = new Object[] { annualFeeAmountParamName };
throw new GeneralPlatformDomainRuleException("error.msg.product.savings.invalid.annualfee.settings",
"Invalid configuration of annual fee settings.", defaultUserMessageArgs);
}
}
private boolean isInvalidConfigurationOfAnnualFeeSettings() {
return (this.annualFeeAmount == null && (this.annualFeeOnDay != null || this.annualFeeOnMonth != null))
|| ((this.annualFeeOnDay == null || this.annualFeeOnMonth == null) && this.annualFeeAmount != null);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SAVINGS_PRODUCT_RESOURCE_NAME);
if (this.annualFeeAmount == null) {
if (this.annualFeeOnMonth != null || this.annualFeeOnDay != null) {
baseDataValidator.reset().parameter(annualFeeAmountParamName).value(this.annualFeeAmount).notNull();
}
} else {
if (this.annualFeeOnMonth == null || this.annualFeeOnDay == null) {
baseDataValidator.reset().parameter(annualFeeOnMonthDayParamName).value(this.annualFeeOnMonth).notNull();
}
baseDataValidator.reset().parameter(annualFeeAmountParamName).value(this.annualFeeAmount).zeroOrPositiveAmount();
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
private void validateWithdrawalFeeDetails() {
if (isInvalidConfigurationOfWithdrawalFeeSettings()) {
Object[] defaultUserMessageArgs = new Object[] { withdrawalFeeAmountParamName };
throw new GeneralPlatformDomainRuleException("error.msg.product.savings.invalid.withdrawalfee.settings",
"Invalid configuration of withdrawal fee settings.", defaultUserMessageArgs);
}
}
private boolean isInvalidConfigurationOfWithdrawalFeeSettings() {
return (this.withdrawalFeeAmount == null && this.withdrawalFeeType != null)
|| (this.withdrawalFeeType == null && this.withdrawalFeeAmount != null);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SAVINGS_PRODUCT_RESOURCE_NAME);
if (this.withdrawalFeeAmount == null) {
baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(this.withdrawalFeeType).ignoreIfNull()
.isOneOfTheseValues(1, 2);
if (this.withdrawalFeeType != null) {
baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(this.withdrawalFeeAmount).notNull();
}
} else {
baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(this.withdrawalFeeAmount).zeroOrPositiveAmount();
baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(this.withdrawalFeeType).notNull()
.isOneOfTheseValues(1, 2);
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
private void validateLockinDetails() {
if (isInvalidConfigurationOfLockinSettings()) {
Object[] defaultUserMessageArgs = new Object[] { lockinPeriodFrequencyParamName };
throw new GeneralPlatformDomainRuleException("error.msg.product.savings.invalid.lockin.settings",
"Invalid configuration of lock in settings.", defaultUserMessageArgs);
}
}
private boolean isInvalidConfigurationOfLockinSettings() {
return (this.lockinPeriodFrequency == null && this.lockinPeriodFrequencyType != null)
|| (this.lockinPeriodFrequencyType == null && this.lockinPeriodFrequency != null);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SAVINGS_PRODUCT_RESOURCE_NAME);
if (this.lockinPeriodFrequency == null) {
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).ignoreIfNull()
.inMinMaxRange(0, 3);
if (this.lockinPeriodFrequencyType != null) {
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).notNull()
.integerZeroOrGreater();
}
} else {
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequencyType).integerZeroOrGreater();
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).notNull()
.inMinMaxRange(0, 3);
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
public boolean isCashBasedAccountingEnabled() {
@@ -5,22 +5,22 @@
*/
package org.mifosplatform.portfolio.savings.domain;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.currencyCodeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.descriptionParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.digitsAfterDecimalParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nameParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nameParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeTypeParamName;
import java.math.BigDecimal;
@@ -28,6 +28,12 @@ import org.joda.time.MonthDay;
import org.mifosplatform.accounting.common.AccountingRuleType;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType;
import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType;
import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType;
import org.mifosplatform.portfolio.savings.SavingsWithdrawalFeesType;
import org.springframework.stereotype.Component;
@Component
@@ -50,10 +56,10 @@ public class SavingsProductAssembler {
interestCompoundingPeriodType = SavingsCompoundingInterestPeriodType.fromInt(interestPeriodTypeValue);
}
SavingsInterestPostingPeriodType interestPostingPeriodType = null;
SavingsPostingInterestPeriodType interestPostingPeriodType = null;
final Integer interestPostingPeriodTypeValue = command.integerValueOfParameterNamed(interestPostingPeriodTypeParamName);
if (interestPostingPeriodTypeValue != null) {
interestPostingPeriodType = SavingsInterestPostingPeriodType.fromInt(interestPostingPeriodTypeValue);
interestPostingPeriodType = SavingsPostingInterestPeriodType.fromInt(interestPostingPeriodTypeValue);
}
SavingsInterestCalculationType interestCalculationType = null;
@@ -69,23 +75,24 @@ public class SavingsProductAssembler {
interestCalculationDaysInYearType = SavingsInterestCalculationDaysInYearType.fromInt(interestCalculationDaysInYearTypeValue);
}
final BigDecimal minRequiredOpeningBalance = command.bigDecimalValueOfParameterNamed(minRequiredOpeningBalanceParamName);
final BigDecimal minRequiredOpeningBalance = command
.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minRequiredOpeningBalanceParamName);
final Integer lockinPeriodFrequency = command.integerValueOfParameterNamed(lockinPeriodFrequencyParamName);
final Integer lockinPeriodFrequency = command.integerValueOfParameterNamedDefaultToNullIfZero(lockinPeriodFrequencyParamName);
SavingsPeriodFrequencyType lockinPeriodFrequencyType = null;
final Integer lockinPeriodFrequencyTypeValue = command.integerValueOfParameterNamed(lockinPeriodFrequencyTypeParamName);
if (lockinPeriodFrequencyTypeValue != null) {
lockinPeriodFrequencyType = SavingsPeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue);
}
final BigDecimal withdrawalFeeAmount = command.bigDecimalValueOfParameterNamed(withdrawalFeeAmountParamName);
final BigDecimal withdrawalFeeAmount = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(withdrawalFeeAmountParamName);
SavingsWithdrawalFeesType withdrawalFeeType = null;
final Integer withdrawalFeeTypeValue = command.integerValueOfParameterNamed(withdrawalFeeTypeParamName);
if (withdrawalFeeTypeValue != null) {
withdrawalFeeType = SavingsWithdrawalFeesType.fromInt(withdrawalFeeTypeValue);
}
final BigDecimal annualFeeAmount = command.bigDecimalValueOfParameterNamed(annualFeeAmountParamName);
final BigDecimal annualFeeAmount = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(annualFeeAmountParamName);
final MonthDay monthDayOfAnnualFee = command.extractMonthDayNamed(annualFeeOnMonthDayParamName);
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(command.integerValueOfParameterNamed("accountingRule"));
@@ -0,0 +1,31 @@
package org.mifosplatform.portfolio.savings.domain.interest;
import java.math.BigDecimal;
import java.util.List;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
public class CompoundInterestHelper {
public Money calculateInterestForAllPostingPeriods(final MonetaryCurrency currency, final List<PostingPeriod> allPeriods) {
// sum up the 'rounded' values that are posted each posting period
Money interestEarned = Money.zero(currency);
// total interest earned in previous periods but not yet recognised
BigDecimal interestEarnedButNotPosted = BigDecimal.ZERO;
for (PostingPeriod postingPeriod : allPeriods) {
final BigDecimal interestEarnedThisPeriod = postingPeriod.calculateInterest(interestEarnedButNotPosted);
Money moneyToBePostedForPeriod = Money.of(currency, interestEarnedThisPeriod);
interestEarned = interestEarned.plus(moneyToBePostedForPeriod);
interestEarnedButNotPosted = interestEarnedButNotPosted.add(moneyToBePostedForPeriod.getAmount());
}
return interestEarned;
}
}
@@ -0,0 +1,15 @@
package org.mifosplatform.portfolio.savings.domain.interest;
import java.math.BigDecimal;
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType;
public interface CompoundingPeriod {
BigDecimal calculateInterest(BigDecimal interestRateAsFraction, long daysInYear);
BigDecimal calculateInterest(SavingsCompoundingInterestPeriodType compoundingInterestPeriodType,
SavingsInterestCalculationType interestCalculationType, BigDecimal interestFromPreviousPostingPeriod,
BigDecimal interestRateAsFraction, long daysInYear);
}
@@ -0,0 +1,90 @@
package org.mifosplatform.portfolio.savings.domain.interest;
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.ArrayList;
import java.util.List;
import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType;
public class DailyCompoundingPeriod implements CompoundingPeriod {
@SuppressWarnings("unused")
private final LocalDateInterval periodInterval;
private final List<EndOfDayBalance> endOfDayBalances;
public static DailyCompoundingPeriod create(final LocalDateInterval periodInterval, final List<EndOfDayBalance> allEndOfDayBalances) {
final List<EndOfDayBalance> endOfDayBalancesWithinPeriod = endOfDayBalancesWithinPeriodInterval(periodInterval, allEndOfDayBalances);
return new DailyCompoundingPeriod(periodInterval, endOfDayBalancesWithinPeriod);
}
private static List<EndOfDayBalance> endOfDayBalancesWithinPeriodInterval(final LocalDateInterval compoundingPeriodInterval,
final List<EndOfDayBalance> allEndOfDayBalances) {
List<EndOfDayBalance> endOfDayBalancesForPeriodInterval = new ArrayList<EndOfDayBalance>();
EndOfDayBalance cappedToPeriodEndDate = null;
for (EndOfDayBalance endOfDayBalance : allEndOfDayBalances) {
if (compoundingPeriodInterval.contains(endOfDayBalance.date())) {
cappedToPeriodEndDate = endOfDayBalance.upTo(compoundingPeriodInterval);
} else if (endOfDayBalance.contains(compoundingPeriodInterval)) {
cappedToPeriodEndDate = endOfDayBalance.upTo(compoundingPeriodInterval);
} else {
LocalDateInterval latestPeriod = LocalDateInterval.create(compoundingPeriodInterval.startDate(),
DateUtils.getLocalDateOfTenant());
cappedToPeriodEndDate = endOfDayBalance.upTo(latestPeriod);
}
if (cappedToPeriodEndDate != null) {
endOfDayBalancesForPeriodInterval.add(cappedToPeriodEndDate);
}
}
return endOfDayBalancesForPeriodInterval;
}
private DailyCompoundingPeriod(final LocalDateInterval periodInterval, final List<EndOfDayBalance> endOfDayBalances) {
this.periodInterval = periodInterval;
this.endOfDayBalances = endOfDayBalances;
}
@Override
public BigDecimal calculateInterest(final BigDecimal interestRateAsFraction, final long daysInYear) {
BigDecimal interestEarned = BigDecimal.ZERO;
for (EndOfDayBalance balance : this.endOfDayBalances) {
BigDecimal interestOnBalanceUnrounded = balance.calculateInterestOnBalance(BigDecimal.ZERO, interestRateAsFraction, daysInYear);
interestEarned = interestEarned.add(interestOnBalanceUnrounded);
}
return interestEarned;
}
@Override
public BigDecimal calculateInterest(
@SuppressWarnings("unused") final SavingsCompoundingInterestPeriodType compoundingInterestPeriodType,
@SuppressWarnings("unused") final SavingsInterestCalculationType interestCalculationType,
final BigDecimal interestFromPreviousPostingPeriod, final BigDecimal interestRateAsFraction, final long daysInYear) {
BigDecimal interestEarned = BigDecimal.ZERO;
// for daily compounding - each interest calculated from previous daily
// calculations is 'compounded'
BigDecimal interestToCompound = interestFromPreviousPostingPeriod;
for (EndOfDayBalance balance : this.endOfDayBalances) {
BigDecimal interestOnBalanceUnrounded = balance.calculateInterestOnBalanceAndInterest(interestToCompound,
interestRateAsFraction, daysInYear);
interestToCompound = interestToCompound.add(interestOnBalanceUnrounded, MathContext.DECIMAL64).setScale(9);
interestEarned = interestEarned.add(interestOnBalanceUnrounded);
}
return interestEarned;
}
}
@@ -0,0 +1,118 @@
package org.mifosplatform.portfolio.savings.domain.interest;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.organisation.monetary.domain.Money;
public class EndOfDayBalance {
private final LocalDate date;
private final Money openingBalance;
private final Money endOfDayBalance;
private final int numberOfDays;
public static EndOfDayBalance from(final LocalDate date, final Money openingBalance, final Money endOfDayBalance, final int numberOfDays) {
return new EndOfDayBalance(date, openingBalance, endOfDayBalance, numberOfDays);
}
public EndOfDayBalance(final LocalDate date, final Money openingBalance, final Money endOfDayBalance, final int numberOfDays) {
this.date = date;
this.openingBalance = openingBalance;
this.endOfDayBalance = endOfDayBalance;
this.numberOfDays = numberOfDays;
}
public LocalDate date() {
return this.date;
}
public Money closingBalance() {
return this.endOfDayBalance;
}
public BigDecimal cumulativeBalance(final BigDecimal interestToCompound) {
final BigDecimal daysAsBigDecimal = BigDecimal.valueOf(this.numberOfDays);
final BigDecimal realBalanceForInterestCalculation = this.endOfDayBalance.getAmount().add(interestToCompound);
return realBalanceForInterestCalculation.multiply(daysAsBigDecimal, MathContext.DECIMAL64).setScale(9, RoundingMode.HALF_EVEN);
}
public BigDecimal calculateInterestOnBalance(final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction,
final long daysInYear) {
BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64);
BigDecimal dailyInterestRate = interestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64);
BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(this.numberOfDays), MathContext.DECIMAL64);
BigDecimal realBalanceForInterestCalculation = this.endOfDayBalance.getAmount().add(interestToCompound);
return realBalanceForInterestCalculation.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, RoundingMode.HALF_EVEN);
}
/**
* Future Value (FV) = PV x (1+r)^n
*
* Interest = FV - PV PV = Principal or the Account Balance r = rate per
* compounding period (so for daily, r = nominalInterestRateAsFraction x
* 1/365 n = number of periods rate is compounded
*/
public BigDecimal calculateInterestOnBalanceAndInterest(final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction,
final long daysInYear) {
BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64);
BigDecimal presentValue = endOfDayBalance.getAmount().add(interestToCompound);
BigDecimal r = interestRateAsFraction.multiply(multiplicand);
BigDecimal interestRateForCompoundingPeriodPlusOne = BigDecimal.ONE.add(r);
double interestRateForCompoundingPeriodPowered = Math.pow(interestRateForCompoundingPeriodPlusOne.doubleValue(),
Integer.valueOf(this.numberOfDays).doubleValue());
BigDecimal futureValue = presentValue.multiply(BigDecimal.valueOf(interestRateForCompoundingPeriodPowered), MathContext.DECIMAL64)
.setScale(9, RoundingMode.HALF_EVEN);
return futureValue.subtract(presentValue);
}
public EndOfDayBalance upTo(final LocalDateInterval compoundingPeriodInterval) {
Money startingBalance = openingBalance;
LocalDate balanceStartDate = this.date;
if (this.date.isBefore(compoundingPeriodInterval.startDate())) {
balanceStartDate = compoundingPeriodInterval.startDate();
startingBalance = endOfDayBalance;
}
int daysOfBalance = this.numberOfDays;
LocalDate balanceEndDate = balanceStartDate.plusDays(this.numberOfDays);
if (balanceEndDate.isAfter(compoundingPeriodInterval.endDate())) {
balanceEndDate = compoundingPeriodInterval.endDate();
LocalDateInterval balancePeriodInterval = LocalDateInterval.create(balanceStartDate, balanceEndDate);
daysOfBalance = balancePeriodInterval.daysInPeriodInclusiveOfEndDate();
}
if (balanceEndDate.isAfter(DateUtils.getLocalDateOfTenant())) {
balanceEndDate = DateUtils.getLocalDateOfTenant();
LocalDateInterval balancePeriodInterval = LocalDateInterval.create(balanceStartDate, balanceEndDate);
daysOfBalance = balancePeriodInterval.daysInPeriodInclusiveOfEndDate();
}
return new EndOfDayBalance(balanceStartDate, startingBalance, this.endOfDayBalance, daysOfBalance);
}
public boolean contains(final LocalDateInterval compoundingPeriodInterval) {
final LocalDate balanceUpToDate = this.date.plusDays(this.numberOfDays);
final LocalDateInterval balanceInterval = LocalDateInterval.create(this.date, balanceUpToDate);
return balanceInterval.containsPortionOf(compoundingPeriodInterval);
}
public Integer getNumberOfDays() {
return Integer.valueOf(this.numberOfDays);
}
}
@@ -0,0 +1,149 @@
package org.mifosplatform.portfolio.savings.domain.interest;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType;
public class MonthlyCompoundingPeriod implements CompoundingPeriod {
@SuppressWarnings("unused")
private final LocalDateInterval periodInterval;
private final List<EndOfDayBalance> endOfDayBalances;
public static MonthlyCompoundingPeriod create(final LocalDateInterval periodInterval, final List<EndOfDayBalance> allEndOfDayBalances) {
final List<EndOfDayBalance> endOfDayBalancesWithinPeriod = endOfDayBalancesWithinPeriodInterval(periodInterval, allEndOfDayBalances);
return new MonthlyCompoundingPeriod(periodInterval, endOfDayBalancesWithinPeriod);
}
@Override
public BigDecimal calculateInterest(final BigDecimal interestRateAsFraction, final long daysInYear) {
BigDecimal interestEarned = BigDecimal.ZERO;
for (EndOfDayBalance balance : endOfDayBalances) {
BigDecimal interestOnBalanceUnrounded = balance.calculateInterestOnBalance(BigDecimal.ZERO, interestRateAsFraction, daysInYear);
interestEarned = interestEarned.add(interestOnBalanceUnrounded);
}
return interestEarned;
}
@Override
public BigDecimal calculateInterest(final SavingsCompoundingInterestPeriodType compoundingInterestPeriodType,
final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestToCompound,
final BigDecimal interestRateAsFraction, final long daysInYear) {
BigDecimal interestEarned = BigDecimal.ZERO;
switch (interestCalculationType) {
case DAILY_BALANCE:
interestEarned = calculateUsingDailyBalanceMethod(compoundingInterestPeriodType, interestToCompound,
interestRateAsFraction, daysInYear);
break;
case AVERAGE_DAILY_BALANCE:
interestEarned = calculateUsingAverageDailyBalanceMethod(interestToCompound, interestRateAsFraction, daysInYear);
break;
case INVALID:
break;
}
return interestEarned;
}
private BigDecimal calculateUsingAverageDailyBalanceMethod(final BigDecimal interestToCompound,
final BigDecimal interestRateAsFraction, final long daysInYear) {
BigDecimal cumulativeBalance = BigDecimal.ZERO;
Integer numberOfDays = Integer.valueOf(0);
for (EndOfDayBalance balance : endOfDayBalances) {
BigDecimal endOfDayCumulativeBalance = balance.cumulativeBalance(interestToCompound);
cumulativeBalance = cumulativeBalance.add(endOfDayCumulativeBalance);
Integer balanceExistsForNumberOfDays = balance.getNumberOfDays();
numberOfDays = numberOfDays + balanceExistsForNumberOfDays;
}
BigDecimal interestEarned = BigDecimal.ZERO;
if (cumulativeBalance.compareTo(BigDecimal.ZERO) != 0 && numberOfDays > 0) {
BigDecimal averageDailyBalance = cumulativeBalance.divide(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64).setScale(9,
RoundingMode.HALF_EVEN);
BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64);
BigDecimal dailyInterestRate = interestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64);
BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64);
interestEarned = averageDailyBalance.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, RoundingMode.HALF_EVEN);
}
return interestEarned;
}
private BigDecimal calculateUsingDailyBalanceMethod(final SavingsCompoundingInterestPeriodType compoundingInterestPeriodType,
final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction, final long daysInYear) {
BigDecimal interestEarned = BigDecimal.ZERO;
BigDecimal interestOnBalanceUnrounded = BigDecimal.ZERO;
for (EndOfDayBalance balance : endOfDayBalances) {
switch (compoundingInterestPeriodType) {
case DAILY:
interestOnBalanceUnrounded = balance.calculateInterestOnBalanceAndInterest(interestToCompound, interestRateAsFraction,
daysInYear);
break;
case MONTHLY:
interestOnBalanceUnrounded = balance.calculateInterestOnBalance(interestToCompound, interestRateAsFraction, daysInYear);
break;
// case QUATERLY:
// break;
// case WEEKLY:
// break;
// case BIWEEKLY:
// break;
// case BI_ANNUAL:
// break;
// case ANNUAL:
// break;
// case NO_COMPOUNDING_SIMPLE_INTEREST:
// break;
case INVALID:
break;
}
interestEarned = interestEarned.add(interestOnBalanceUnrounded);
}
return interestEarned;
}
private static List<EndOfDayBalance> endOfDayBalancesWithinPeriodInterval(final LocalDateInterval compoundingPeriodInterval,
final List<EndOfDayBalance> allEndOfDayBalances) {
List<EndOfDayBalance> endOfDayBalancesForPeriodInterval = new ArrayList<EndOfDayBalance>();
for (EndOfDayBalance endOfDayBalance : allEndOfDayBalances) {
if (compoundingPeriodInterval.contains(endOfDayBalance.date())) {
final EndOfDayBalance cappedToPeriodEndDate = endOfDayBalance.upTo(compoundingPeriodInterval);
endOfDayBalancesForPeriodInterval.add(cappedToPeriodEndDate);
} else if (endOfDayBalance.contains(compoundingPeriodInterval)) {
final EndOfDayBalance cappedToPeriodEndDate = endOfDayBalance.upTo(compoundingPeriodInterval);
endOfDayBalancesForPeriodInterval.add(cappedToPeriodEndDate);
}
}
return endOfDayBalancesForPeriodInterval;
}
private MonthlyCompoundingPeriod(final LocalDateInterval periodInterval, final List<EndOfDayBalance> endOfDayBalances) {
this.periodInterval = periodInterval;
this.endOfDayBalances = endOfDayBalances;
}
}
@@ -0,0 +1,248 @@
package org.mifosplatform.portfolio.savings.domain.interest;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.domain.LocalDateInterval;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction;
public class PostingPeriod {
@SuppressWarnings("unused")
private final LocalDateInterval periodInterval;
private final MonetaryCurrency currency;
private final SavingsCompoundingInterestPeriodType interestCompoundingType;
private final BigDecimal interestRateAsFraction;
private final long daysInYear;
private final List<CompoundingPeriod> compoundingPeriods;
// interest posting details
private final LocalDate dateOfPostingTransaction;
private BigDecimal interestEarnedUnrounded;
private Money interestEarnedRounded;
// opening/closing details
@SuppressWarnings("unused")
private final Money openingBalance;
private final Money closingBalance;
private final SavingsInterestCalculationType interestCalculationType;
public static PostingPeriod createFrom(final LocalDateInterval periodInterval, final Money periodStartingBalance,
final List<SavingsAccountTransaction> orderedListOfTransactions, final MonetaryCurrency currency,
final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestRateAsFraction, final long daysInYear) {
List<EndOfDayBalance> accountEndOfDayBalances = new ArrayList<EndOfDayBalance>();
Money openingDayBalance = periodStartingBalance;
Money closeOfDayBalance = openingDayBalance;
for (SavingsAccountTransaction transaction : orderedListOfTransactions) {
if (transaction.fallsWithin(periodInterval)) {
// the balance of the transaction falls entirely within this
// period so no need to do any cropping/bounding
EndOfDayBalance endOfDayBalance = transaction.toEndOfDayBalance(openingDayBalance);
accountEndOfDayBalances.add(endOfDayBalance);
openingDayBalance = endOfDayBalance.closingBalance();
} else if (transaction.spansAnyPortionOf(periodInterval)) {
EndOfDayBalance endOfDayBalance = transaction.toEndOfDayBalanceBoundedBy(openingDayBalance, periodInterval);
accountEndOfDayBalances.add(endOfDayBalance);
closeOfDayBalance = endOfDayBalance.closingBalance();
openingDayBalance = closeOfDayBalance;
}
}
if (accountEndOfDayBalances.isEmpty()) {
EndOfDayBalance endOfDayBalance = EndOfDayBalance.from(periodInterval.startDate(), openingDayBalance, closeOfDayBalance,
periodInterval.daysInPeriodInclusiveOfEndDate());
accountEndOfDayBalances.add(endOfDayBalance);
closeOfDayBalance = endOfDayBalance.closingBalance();
openingDayBalance = closeOfDayBalance;
}
final List<CompoundingPeriod> compoundingPeriods = compoundingPeriodsInPostingPeriod(periodInterval, interestCompoundingPeriodType,
accountEndOfDayBalances);
return new PostingPeriod(periodInterval, currency, periodStartingBalance, openingDayBalance, interestCompoundingPeriodType,
interestCalculationType, interestRateAsFraction, daysInYear, compoundingPeriods);
}
private PostingPeriod(final LocalDateInterval periodInterval, final MonetaryCurrency currency, final Money openingBalance,
final Money closingBalance, final SavingsCompoundingInterestPeriodType interestCompoundingType,
final SavingsInterestCalculationType interestCalculationType, final BigDecimal interestRateAsFraction, final long daysInYear,
final List<CompoundingPeriod> compoundingPeriods) {
this.periodInterval = periodInterval;
this.currency = currency;
this.openingBalance = openingBalance;
this.closingBalance = closingBalance;
this.interestCompoundingType = interestCompoundingType;
this.interestCalculationType = interestCalculationType;
this.interestRateAsFraction = interestRateAsFraction;
this.daysInYear = daysInYear;
this.compoundingPeriods = compoundingPeriods;
this.dateOfPostingTransaction = periodInterval.endDate().plusDays(1);
}
public Money interest() {
return this.interestEarnedRounded;
}
public LocalDate dateOfPostingTransaction() {
return this.dateOfPostingTransaction;
}
public Money closingBalance() {
return this.closingBalance;
}
public BigDecimal calculateInterest(final BigDecimal interestFromPreviousPostingPeriod) {
BigDecimal interestEarned = BigDecimal.ZERO;
// for each compounding period accumulate the amount of interest
// to be applied to the balanced for interest calculation
BigDecimal interestCompounded = interestFromPreviousPostingPeriod;
for (CompoundingPeriod compoundingPeriod : this.compoundingPeriods) {
BigDecimal interestUnrounded = compoundingPeriod.calculateInterest(this.interestCompoundingType, this.interestCalculationType,
interestCompounded, this.interestRateAsFraction, this.daysInYear);
interestCompounded = interestCompounded.add(interestUnrounded);
interestEarned = interestEarned.add(interestUnrounded);
}
this.interestEarnedUnrounded = interestEarned;
this.interestEarnedRounded = Money.of(this.currency, this.interestEarnedUnrounded);
return interestEarned;
}
public Money getInterestEarned() {
return this.interestEarnedRounded;
}
private static List<CompoundingPeriod> compoundingPeriodsInPostingPeriod(final LocalDateInterval postingPeriodInterval,
final SavingsCompoundingInterestPeriodType interestPeriodType, final List<EndOfDayBalance> allEndOfDayBalances) {
List<CompoundingPeriod> compoundingPeriods = new ArrayList<CompoundingPeriod>();
CompoundingPeriod compoundingPeriod = null;
switch (interestPeriodType) {
case INVALID:
break;
case DAILY:
compoundingPeriod = DailyCompoundingPeriod.create(postingPeriodInterval, allEndOfDayBalances);
compoundingPeriods.add(compoundingPeriod);
break;
case MONTHLY:
final LocalDate postingPeriodEndDate = postingPeriodInterval.endDate();
LocalDate periodStartDate = postingPeriodInterval.startDate();
LocalDate periodEndDate = periodStartDate;
while (!periodStartDate.isAfter(postingPeriodEndDate) && !periodEndDate.isAfter(postingPeriodEndDate)) {
periodEndDate = determineInterestPeriodEndDateFrom(periodStartDate, interestPeriodType);
if (periodEndDate.isAfter(postingPeriodEndDate)) {
periodEndDate = postingPeriodEndDate;
}
final LocalDateInterval compoundingPeriodInterval = LocalDateInterval.create(periodStartDate, periodEndDate);
if (postingPeriodInterval.contains(compoundingPeriodInterval)) {
compoundingPeriod = MonthlyCompoundingPeriod.create(compoundingPeriodInterval, allEndOfDayBalances);
compoundingPeriods.add(compoundingPeriod);
}
// move periodStartDate forward to day after this period
periodStartDate = periodEndDate.plusDays(1);
}
break;
// case WEEKLY:
// break;
// case BIWEEKLY:
// break;
// case QUATERLY:
// break;
// case BI_ANNUAL:
// break;
// case ANNUAL:
// break;
// case NO_COMPOUNDING_SIMPLE_INTEREST:
// break;
}
return compoundingPeriods;
}
private static LocalDate determineInterestPeriodEndDateFrom(final LocalDate periodStartDate,
final SavingsCompoundingInterestPeriodType 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 BI_ANNUAL:
// // 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 = periodEndDate.dayOfMonth().withMaximumValue();
// break;
// case NO_COMPOUNDING_SIMPLE_INTEREST:
// periodEndDate = periodStartDate.monthOfYear().withMaximumValue();
// periodEndDate = periodEndDate.dayOfMonth().withMaximumValue();
// break;
}
return periodEndDate;
}
}
@@ -15,8 +15,10 @@ import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRul
*/
public class InsufficientAccountBalanceException extends AbstractPlatformDomainRuleException {
public InsufficientAccountBalanceException(final String paramName, final BigDecimal accountBalance, final BigDecimal withdrawalFee, final BigDecimal transactionAmount) {
super(withdrawalFee != null? "error.msg.savingsaccount.transaction.insufficient.account.balance.withdraw" : "error.msg.savingsaccount.transaction.insufficient.account.balance",
"Insufficient account balance.", paramName, accountBalance, withdrawalFee, transactionAmount);
public InsufficientAccountBalanceException(final String paramName, final BigDecimal accountBalance, final BigDecimal withdrawalFee,
final BigDecimal transactionAmount) {
super(withdrawalFee != null ? "error.msg.savingsaccount.transaction.insufficient.account.balance.withdraw"
: "error.msg.savingsaccount.transaction.insufficient.account.balance", "Insufficient account balance.", paramName,
accountBalance, withdrawalFee, transactionAmount);
}
}
@@ -16,16 +16,16 @@ import org.springframework.transaction.annotation.Transactional;
@Service
public class ActivateSavingsAccountCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService savingAccountWritePlatformService;
private final SavingsAccountWritePlatformService writePlatformService;
@Autowired
public ActivateSavingsAccountCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) {
this.savingAccountWritePlatformService = savingAccountWritePlatformService;
this.writePlatformService = savingAccountWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.savingAccountWritePlatformService.activate(command.getSavingsId(), command);
return this.writePlatformService.activate(command.getSavingsId(), command);
}
}
@@ -16,16 +16,17 @@ import org.springframework.transaction.annotation.Transactional;
@Service
public class CalculateInterestSavingsAccountCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService savingAccountWritePlatformService;
private final SavingsAccountWritePlatformService writePlatformService;
@Autowired
public CalculateInterestSavingsAccountCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) {
this.savingAccountWritePlatformService = savingAccountWritePlatformService;
public CalculateInterestSavingsAccountCommandHandler(
final SavingsAccountWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.savingAccountWritePlatformService.calculateInterest(command.getSavingsId(), command);
return this.writePlatformService.calculateInterest(command.getSavingsId());
}
}
@@ -16,16 +16,16 @@ import org.springframework.transaction.annotation.Transactional;
@Service
public class DepositSavingsAccountCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService savingAccountWritePlatformService;
private final SavingsAccountWritePlatformService writePlatformService;
@Autowired
public DepositSavingsAccountCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) {
this.savingAccountWritePlatformService = savingAccountWritePlatformService;
public DepositSavingsAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.savingAccountWritePlatformService.deposit(command.getSavingsId(), command);
return this.writePlatformService.deposit(command.getSavingsId(), command);
}
}
@@ -16,16 +16,16 @@ import org.springframework.transaction.annotation.Transactional;
@Service
public class PostInterestSavingsAccountCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService savingAccountWritePlatformService;
private final SavingsAccountWritePlatformService writePlatformService;
@Autowired
public PostInterestSavingsAccountCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) {
this.savingAccountWritePlatformService = savingAccountWritePlatformService;
public PostInterestSavingsAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.savingAccountWritePlatformService.postInterest(command.getSavingsId(), command);
return this.writePlatformService.postInterest(command.getSavingsId());
}
}
@@ -0,0 +1,32 @@
/**
* 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.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.SavingsApplicationProcessWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class SavingsAccountApplicationApprovalCommandHandler implements NewCommandSourceHandler {
private final SavingsApplicationProcessWritePlatformService writePlatformService;
@Autowired
public SavingsAccountApplicationApprovalCommandHandler(final SavingsApplicationProcessWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.approveApplication(command.entityId(), command);
}
}
@@ -0,0 +1,32 @@
/**
* 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.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.SavingsApplicationProcessWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class SavingsAccountApplicationApprovalUndoCommandHandler implements NewCommandSourceHandler {
private final SavingsApplicationProcessWritePlatformService writePlatformService;
@Autowired
public SavingsAccountApplicationApprovalUndoCommandHandler(final SavingsApplicationProcessWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.undoApplicationApproval(command.entityId(), command);
}
}
@@ -8,25 +8,25 @@ 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.mifosplatform.portfolio.savings.service.SavingsApplicationProcessWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class DeleteSavingsAccountCommandHandler implements NewCommandSourceHandler {
public class SavingsAccountApplicationDeletionCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService savingAccountWritePlatformService;
private final SavingsApplicationProcessWritePlatformService savingAccountWritePlatformService;
@Autowired
public DeleteSavingsAccountCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) {
public SavingsAccountApplicationDeletionCommandHandler(final SavingsApplicationProcessWritePlatformService savingAccountWritePlatformService) {
this.savingAccountWritePlatformService = savingAccountWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.savingAccountWritePlatformService.deleteSavingAccount(command.entityId());
return this.savingAccountWritePlatformService.deleteApplication(command.entityId());
}
}
@@ -8,25 +8,24 @@ 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.mifosplatform.portfolio.savings.service.SavingsApplicationProcessWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UpdateSavingsAccountCommandHandler implements NewCommandSourceHandler {
public class SavingsAccountApplicationModificationCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService savingAccountWritePlatformService;
private final SavingsApplicationProcessWritePlatformService savingAccountWritePlatformService;
@Autowired
public UpdateSavingsAccountCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) {
public SavingsAccountApplicationModificationCommandHandler(final SavingsApplicationProcessWritePlatformService savingAccountWritePlatformService) {
this.savingAccountWritePlatformService = savingAccountWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.savingAccountWritePlatformService.updateSavingAccount(command.entityId(), command);
return this.savingAccountWritePlatformService.modifyApplication(command.entityId(), command);
}
}
}
@@ -0,0 +1,32 @@
/**
* 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.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.SavingsApplicationProcessWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class SavingsAccountApplicationRejectedCommandHandler implements NewCommandSourceHandler {
private final SavingsApplicationProcessWritePlatformService writePlatformService;
@Autowired
public SavingsAccountApplicationRejectedCommandHandler(final SavingsApplicationProcessWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.rejectApplication(command.entityId(), command);
}
}
@@ -8,24 +8,24 @@ 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.mifosplatform.portfolio.savings.service.SavingsApplicationProcessWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class CreateSavingsAccountCommandHandler implements NewCommandSourceHandler {
public class SavingsAccountApplicationSubmittalCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService savingAccountWritePlatformService;
private final SavingsApplicationProcessWritePlatformService savingAccountWritePlatformService;
@Autowired
public CreateSavingsAccountCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) {
public SavingsAccountApplicationSubmittalCommandHandler(final SavingsApplicationProcessWritePlatformService savingAccountWritePlatformService) {
this.savingAccountWritePlatformService = savingAccountWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.savingAccountWritePlatformService.createSavingAccount(command);
return this.savingAccountWritePlatformService.submitApplication(command);
}
}
@@ -0,0 +1,33 @@
/**
* 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.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.SavingsApplicationProcessWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class SavingsAccountApplicationWithdrawnByApplicantCommandHandler implements NewCommandSourceHandler {
private final SavingsApplicationProcessWritePlatformService writePlatformService;
@Autowired
public SavingsAccountApplicationWithdrawnByApplicantCommandHandler(
final SavingsApplicationProcessWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.applicantWithdrawsFromApplication(command.entityId(), command);
}
}
@@ -16,16 +16,16 @@ import org.springframework.transaction.annotation.Transactional;
@Service
public class WithdrawSavingsAccountCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService savingAccountWritePlatformService;
private final SavingsAccountWritePlatformService writePlatformService;
@Autowired
public WithdrawSavingsAccountCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) {
this.savingAccountWritePlatformService = savingAccountWritePlatformService;
public WithdrawSavingsAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.savingAccountWritePlatformService.withdrawal(command.getSavingsId(), command);
return this.writePlatformService.withdrawal(command.getSavingsId(), command);
}
}
@@ -0,0 +1,129 @@
/**
* 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.service;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
@Component
public final class SavingsAccountApplicationTransitionApiJsonValidator {
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public SavingsAccountApplicationTransitionApiJsonValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
public void validateApproval(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Set<String> disbursementParameters = new HashSet<String>(Arrays.asList("approvedOnDate", "note", "locale", "dateFormat"));
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("savingsaccountapplication");
final JsonElement element = fromApiJsonHelper.parse(json);
final LocalDate approvedOnDate = fromApiJsonHelper.extractLocalDateNamed("approvedOnDate", element);
baseDataValidator.reset().parameter("approvedOnDate").value(approvedOnDate).notNull();
final String note = fromApiJsonHelper.extractStringNamed("note", element);
baseDataValidator.reset().parameter("note").value(note).notExceedingLengthOf(1000);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateRejection(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Set<String> disbursementParameters = new HashSet<String>(Arrays.asList("rejectedOnDate", "note", "locale", "dateFormat"));
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("savingsaccountapplication");
final JsonElement element = fromApiJsonHelper.parse(json);
final LocalDate rejectedOnDate = fromApiJsonHelper.extractLocalDateNamed("rejectedOnDate", element);
baseDataValidator.reset().parameter("rejectedOnDate").value(rejectedOnDate).notNull();
final String note = fromApiJsonHelper.extractStringNamed("note", element);
baseDataValidator.reset().parameter("note").value(note).notExceedingLengthOf(1000);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateApplicantWithdrawal(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Set<String> disbursementParameters = new HashSet<String>(Arrays.asList("withdrawnOnDate", "note", "locale", "dateFormat"));
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("savingsaccountapplication");
final JsonElement element = fromApiJsonHelper.parse(json);
final LocalDate withdrawnOnDate = fromApiJsonHelper.extractLocalDateNamed("withdrawnOnDate", element);
baseDataValidator.reset().parameter("withdrawnOnDate").value(withdrawnOnDate).notNull();
final String note = fromApiJsonHelper.extractStringNamed("note", element);
baseDataValidator.reset().parameter("note").value(note).notExceedingLengthOf(1000);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateForUndo(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Set<String> undoSupportedParameters = new HashSet<String>(Arrays.asList("note"));
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, undoSupportedParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource("savingsaccountapplication.undo");
final JsonElement element = fromApiJsonHelper.parse(json);
final String note = "note";
if (fromApiJsonHelper.parameterExists(note, element)) {
final String noteText = fromApiJsonHelper.extractStringNamed(note, element);
baseDataValidator.reset().parameter(note).value(noteText).notExceedingLengthOf(1000);
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
}
@@ -18,7 +18,7 @@ public interface SavingsAccountReadPlatformService {
SavingsAccountData retrieveOne(Long savingsId);
SavingsAccountData retrieveTemplate(Long clientId, Long groupId, Long productId);
SavingsAccountData retrieveTemplate(Long clientId, Long groupId, Long productId, boolean staffInSelectedOfficeOnly);
SavingsAccountTransactionData retrieveDepositTransactionTemplate(Long savingsId);
@@ -8,6 +8,7 @@ package org.mifosplatform.portfolio.savings.service;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -23,23 +24,26 @@ import org.mifosplatform.infrastructure.core.service.PaginationHelper;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.organisation.staff.data.StaffData;
import org.mifosplatform.organisation.staff.service.StaffReadPlatformService;
import org.mifosplatform.portfolio.client.data.ClientData;
import org.mifosplatform.portfolio.client.service.ClientReadPlatformService;
import org.mifosplatform.portfolio.group.data.GroupGeneralData;
import org.mifosplatform.portfolio.group.service.GroupReadPlatformService;
import org.mifosplatform.portfolio.group.service.SearchParameters;
import org.mifosplatform.portfolio.paymentdetail.data.PaymentDetailData;
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType;
import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType;
import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType;
import org.mifosplatform.portfolio.savings.data.SavingsAccountApplicationTimelineData;
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.SavingsCompoundingInterestPeriodType;
import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationDaysInYearType;
import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationType;
import org.mifosplatform.portfolio.savings.domain.SavingsInterestPostingPeriodType;
import org.mifosplatform.portfolio.savings.domain.SavingsPeriodFrequencyType;
import org.mifosplatform.portfolio.savings.exception.SavingsAccountNotFoundException;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
@@ -47,6 +51,7 @@ import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
@Service
public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountReadPlatformService {
@@ -56,10 +61,11 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
private final ClientReadPlatformService clientReadPlatformService;
private final GroupReadPlatformService groupReadPlatformService;
private final SavingsProductReadPlatformService savingsProductReadPlatformService;
private final StaffReadPlatformService staffReadPlatformService;
private final SavingsDropdownReadPlatformService dropdownReadPlatformService;
private final SavingsAccountTransactionTemplateMapper transactionTemplateMapper;
private final SavingsAccountTransactionsMapper transactionsMapper;
private final PaginationHelper<SavingsAccountData> paginationHelper = new PaginationHelper<SavingsAccountData>();
private final SavingAccountMapper savingAccountMapper = new SavingAccountMapper();
@@ -67,15 +73,16 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
public SavingsAccountReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource,
final ClientReadPlatformService clientReadPlatformService, final GroupReadPlatformService groupReadPlatformService,
final SavingsProductReadPlatformService savingProductReadPlatformService,
final SavingsDropdownReadPlatformService dropdownReadPlatformService) {
final StaffReadPlatformService staffReadPlatformService, final SavingsDropdownReadPlatformService dropdownReadPlatformService) {
this.context = context;
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.clientReadPlatformService = clientReadPlatformService;
this.groupReadPlatformService = groupReadPlatformService;
this.savingsProductReadPlatformService = savingProductReadPlatformService;
this.staffReadPlatformService = staffReadPlatformService;
this.dropdownReadPlatformService = dropdownReadPlatformService;
transactionTemplateMapper = new SavingsAccountTransactionTemplateMapper();
transactionsMapper = new SavingsAccountTransactionsMapper();
this.transactionTemplateMapper = new SavingsAccountTransactionTemplateMapper();
this.transactionsMapper = new SavingsAccountTransactionsMapper();
}
@Override
@@ -151,13 +158,40 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
public SavingAccountMapper() {
final StringBuilder sqlBuilder = new StringBuilder(400);
sqlBuilder.append("sa.id as id, sa.account_no as accountNo, sa.external_id as externalId, ");
sqlBuilder.append("sa.status_enum as statusEnum, sa.activation_date as activationDate, ");
sqlBuilder.append("c.id as clientId, c.display_name as clientName, ");
sqlBuilder.append("g.id as groupId, g.display_name as groupName, ");
sqlBuilder.append("sp.id as productId, sp.name as productName, ");
sqlBuilder.append("s.id fieldOfficerId, s.display_name as fieldOfficerName, ");
sqlBuilder.append("sa.status_enum as statusEnum, ");
sqlBuilder.append("sa.submittedon_date as submittedOnDate,");
sqlBuilder.append("sbu.username as submittedByUsername,");
sqlBuilder.append("sbu.firstname as submittedByFirstname, sbu.lastname as submittedByLastname,");
sqlBuilder.append("sa.rejectedon_date as rejectedOnDate,");
sqlBuilder.append("rbu.username as rejectedByUsername,");
sqlBuilder.append("rbu.firstname as rejectedByFirstname, rbu.lastname as rejectedByLastname,");
sqlBuilder.append("sa.withdrawnon_date as withdrawnOnDate,");
sqlBuilder.append("wbu.username as withdrawnByUsername,");
sqlBuilder.append("wbu.firstname as withdrawnByFirstname, wbu.lastname as withdrawnByLastname,");
sqlBuilder.append("sa.approvedon_date as approvedOnDate,");
sqlBuilder.append("abu.username as approvedByUsername,");
sqlBuilder.append("abu.firstname as approvedByFirstname, abu.lastname as approvedByLastname,");
sqlBuilder.append("sa.activatedon_date as activatedOnDate,");
sqlBuilder.append("avbu.username as activatedByUsername,");
sqlBuilder.append("avbu.firstname as activatedByFirstname, avbu.lastname as activatedByLastname,");
sqlBuilder.append("sa.closedon_date as closedOnDate,");
sqlBuilder.append("cbu.username as closedByUsername,");
sqlBuilder.append("cbu.firstname as closedByFirstname, cbu.lastname as closedByLastname,");
sqlBuilder.append("sa.currency_code as currencyCode, sa.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("sa.nominal_annual_interest_rate as nominalAnnualInterestRate, ");
sqlBuilder.append("sa.interest_compounding_period_enum as interestCompoundingPeriodType, ");
sqlBuilder.append("sa.interest_posting_period_enum as interestPostingPeriodType, ");
@@ -180,10 +214,17 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
sqlBuilder.append("sa.total_interest_posted_derived as totalInterestPosted, ");
sqlBuilder.append("sa.account_balance_derived as accountBalance ");
sqlBuilder.append("from m_savings_account sa ");
sqlBuilder.append("left outer join m_client c ON c.id = sa.client_id ");
sqlBuilder.append("left outer join m_group g ON g.id = sa.group_id ");
sqlBuilder.append("join m_savings_product sp ON sa.product_id = sp.id ");
sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code ");
sqlBuilder.append("left join m_client c ON c.id = sa.client_id ");
sqlBuilder.append("left join m_group g ON g.id = sa.group_id ");
sqlBuilder.append("left join m_staff s ON s.id = sa.field_officer_id ");
sqlBuilder.append("left join m_appuser sbu on sbu.id = sa.submittedon_userid ");
sqlBuilder.append("left join m_appuser rbu on rbu.id = sa.rejectedon_userid ");
sqlBuilder.append("left join m_appuser wbu on wbu.id = sa.withdrawnon_userid ");
sqlBuilder.append("left join m_appuser abu on abu.id = sa.approvedon_userid ");
sqlBuilder.append("left join m_appuser avbu on rbu.id = sa.activatedon_userid ");
sqlBuilder.append("left join m_appuser cbu on cbu.id = sa.closedon_userid ");
this.schemaSql = sqlBuilder.toString();
}
@@ -199,11 +240,6 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final String accountNo = rs.getString("accountNo");
final String externalId = rs.getString("externalId");
final Integer statusEnum = JdbcSupport.getInteger(rs, "statusEnum");
final SavingsAccountStatusEnumData status = SavingsEnumerations.status(statusEnum);
final LocalDate activationDate = JdbcSupport.getLocalDate(rs, "activationDate");
final Long groupId = JdbcSupport.getLong(rs, "groupId");
final String groupName = rs.getString("groupName");
final Long clientId = JdbcSupport.getLong(rs, "clientId");
@@ -212,6 +248,49 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final Long productId = rs.getLong("productId");
final String productName = rs.getString("productName");
final Long fieldOfficerId = rs.getLong("fieldOfficerId");
final String fieldOfficerName = rs.getString("fieldOfficerName");
final Integer statusEnum = JdbcSupport.getInteger(rs, "statusEnum");
final SavingsAccountStatusEnumData status = SavingsEnumerations.status(statusEnum);
final LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, "submittedOnDate");
final String submittedByUsername = rs.getString("submittedByUsername");
final String submittedByFirstname = rs.getString("submittedByFirstname");
final String submittedByLastname = rs.getString("submittedByLastname");
final LocalDate rejectedOnDate = JdbcSupport.getLocalDate(rs, "rejectedOnDate");
final String rejectedByUsername = rs.getString("rejectedByUsername");
final String rejectedByFirstname = rs.getString("rejectedByFirstname");
final String rejectedByLastname = rs.getString("rejectedByLastname");
final LocalDate withdrawnOnDate = JdbcSupport.getLocalDate(rs, "withdrawnOnDate");
final String withdrawnByUsername = rs.getString("withdrawnByUsername");
final String withdrawnByFirstname = rs.getString("withdrawnByFirstname");
final String withdrawnByLastname = rs.getString("withdrawnByLastname");
final LocalDate approvedOnDate = JdbcSupport.getLocalDate(rs, "approvedOnDate");
final String approvedByUsername = rs.getString("approvedByUsername");
final String approvedByFirstname = rs.getString("approvedByFirstname");
final String approvedByLastname = rs.getString("approvedByLastname");
final LocalDate activatedOnDate = JdbcSupport.getLocalDate(rs, "activatedOnDate");
final String activatedByUsername = rs.getString("activatedByUsername");
final String activatedByFirstname = rs.getString("activatedByFirstname");
final String activatedByLastname = rs.getString("activatedByLastname");
final LocalDate closedOnDate = JdbcSupport.getLocalDate(rs, "closedOnDate");
final String closedByUsername = rs.getString("closedByUsername");
final String closedByFirstname = rs.getString("closedByFirstname");
final String closedByLastname = rs.getString("closedByLastname");
final SavingsAccountApplicationTimelineData timeline = new SavingsAccountApplicationTimelineData(submittedOnDate,
submittedByUsername, submittedByFirstname, submittedByLastname, rejectedOnDate, rejectedByUsername,
rejectedByFirstname, rejectedByLastname, withdrawnOnDate, withdrawnByUsername, withdrawnByFirstname,
withdrawnByLastname, approvedOnDate, approvedByUsername, approvedByFirstname, approvedByLastname, activatedOnDate,
activatedByUsername, activatedByFirstname, activatedByLastname, closedOnDate, closedByUsername, closedByFirstname,
closedByLastname);
final String currencyCode = rs.getString("currencyCode");
final String currencyName = rs.getString("currencyName");
final String currencyNameCode = rs.getString("currencyNameCode");
@@ -226,7 +305,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs,
"interestCompoundingPeriodType")));
final EnumOptionData interestPostingPeriodType = SavingsEnumerations.interestPostingPeriodType(SavingsInterestPostingPeriodType
final EnumOptionData interestPostingPeriodType = SavingsEnumerations.interestPostingPeriodType(SavingsPostingInterestPeriodType
.fromInt(JdbcSupport.getInteger(rs, "interestPostingPeriodType")));
final EnumOptionData interestCalculationType = SavingsEnumerations.interestCalculationType(SavingsInterestCalculationType
@@ -277,27 +356,31 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final SavingsAccountSummaryData summary = new SavingsAccountSummaryData(currency, totalDeposits, totalWithdrawals,
totalWithdrawalFees, totalAnnualFees, totalInterestEarned, totalInterestPosted, accountBalance);
return SavingsAccountData.instance(id, accountNo, externalId, status, activationDate, groupId, groupName, clientId, clientName,
productId, productName, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, annualFeeOnMonthDay,
annualFeeNextDueDate, summary);
return SavingsAccountData.instance(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount,
annualFeeOnMonthDay, annualFeeNextDueDate, summary);
}
}
@Override
public SavingsAccountData retrieveTemplate(final Long clientId, final Long groupId, final Long productId) {
public SavingsAccountData retrieveTemplate(final Long clientId, final Long groupId, final Long productId,
final boolean staffInSelectedOfficeOnly) {
context.authenticatedUser();
AppUser loggedInUser = context.authenticatedUser();
Long officeId = loggedInUser.getOffice().getId();
ClientData client = null;
if (clientId != null) {
client = this.clientReadPlatformService.retrieveOne(clientId);
officeId = client.officeId();
}
GroupGeneralData group = null;
if (groupId != null) {
group = this.groupReadPlatformService.retrieveOne(groupId);
officeId = group.officeId();
}
final Collection<SavingsProductData> productOptions = this.savingsProductReadPlatformService.retrieveAllForLookup();
@@ -328,9 +411,35 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final Collection<SavingsAccountTransactionData> transactions = null;
template = SavingsAccountData.withTemplateOptions(template, productOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions);
Collection<StaffData> fieldOfficerOptions = null;
if (officeId != null) {
if (staffInSelectedOfficeOnly) {
// only bring back loan officers in selected branch/office
Collection<StaffData> fieldOfficersInBranch = this.staffReadPlatformService
.retrieveAllLoanOfficersInOfficeById(officeId);
if (!CollectionUtils.isEmpty(fieldOfficersInBranch)) {
fieldOfficerOptions = new ArrayList<StaffData>(fieldOfficersInBranch);
}
} else {
// by default bring back all officers in selected
// branch/office as well as officers in office above
// this office
final boolean restrictToLoanOfficersOnly = true;
Collection<StaffData> loanOfficersInHierarchy = this.staffReadPlatformService
.retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(officeId, restrictToLoanOfficersOnly);
if (!CollectionUtils.isEmpty(loanOfficersInHierarchy)) {
fieldOfficerOptions = new ArrayList<StaffData>(loanOfficersInHierarchy);
}
}
}
template = SavingsAccountData.withTemplateOptions(template, productOptions, fieldOfficerOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions);
} else {
String clientName = null;
@@ -345,6 +454,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
template = SavingsAccountData.withClientTemplate(clientId, clientName, groupId, groupName);
final Collection<StaffData> fieldOfficerOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
@@ -354,9 +464,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final Collection<SavingsAccountTransactionData> transactions = null;
template = SavingsAccountData.withTemplateOptions(template, productOptions, interestCompoundingPeriodTypeOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions);
template = SavingsAccountData.withTemplateOptions(template, productOptions, fieldOfficerOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions);
}
return template;
@@ -553,7 +663,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs,
"interestCompoundingPeriodType")));
final EnumOptionData interestPostingPeriodType = SavingsEnumerations.interestPostingPeriodType(SavingsInterestPostingPeriodType
final EnumOptionData interestPostingPeriodType = SavingsEnumerations.interestPostingPeriodType(SavingsPostingInterestPeriodType
.fromInt(JdbcSupport.getInteger(rs, "interestPostingPeriodType")));
EnumOptionData interestCalculationType = SavingsEnumerations.interestCalculationType(SavingsInterestCalculationType
@@ -604,16 +714,19 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
groupName = group.getName();
}
final Long fieldOfficerId = null;
final String fieldOfficerName = null;
final SavingsAccountStatusEnumData status = null;
final LocalDate activationDate = null;
final LocalDate annualFeeNextDueDate = null;
final SavingsAccountSummaryData summary = null;
return SavingsAccountData.instance(null, null, null, status, activationDate, groupId, groupName, clientId, clientName,
productId, productName, currency, nominalAnnualIterestRate, interestCompoundingPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, annualFeeOnMonthDay,
annualFeeNextDueDate, summary);
final SavingsAccountApplicationTimelineData timeline = SavingsAccountApplicationTimelineData.templateDefault();
return SavingsAccountData.instance(null, null, null, groupId, groupName, clientId, clientName, productId, productName,
fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualIterestRate, interestCompoundingPeriodType,
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount,
annualFeeOnMonthDay, annualFeeNextDueDate, summary);
}
}
}
@@ -10,21 +10,15 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
public interface SavingsAccountWritePlatformService {
CommandProcessingResult createSavingAccount(JsonCommand command);
CommandProcessingResult updateSavingAccount(Long accountId, JsonCommand command);
CommandProcessingResult deleteSavingAccount(Long accountId);
CommandProcessingResult activate(Long savingsId, JsonCommand command);
CommandProcessingResult deposit(Long savingsId, JsonCommand command);
CommandProcessingResult withdrawal(Long savingsId, JsonCommand command);
CommandProcessingResult applyAnnualFee(Long savingsId);
CommandProcessingResult calculateInterest(Long savingsId, JsonCommand command);
CommandProcessingResult calculateInterest(Long savingsId);
CommandProcessingResult postInterest(Long savingsId, JsonCommand command);
CommandProcessingResult postInterest(Long savingsId);
}
@@ -6,6 +6,8 @@
package org.mifosplatform.portfolio.savings.service;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
@@ -19,66 +21,45 @@ import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatfo
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.portfolio.client.domain.AccountNumberGenerator;
import org.mifosplatform.portfolio.client.domain.AccountNumberGeneratorFactory;
import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper;
import org.mifosplatform.portfolio.group.domain.Group;
import org.mifosplatform.portfolio.group.domain.GroupRepository;
import org.mifosplatform.portfolio.group.exception.GroupNotFoundException;
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
import org.mifosplatform.portfolio.paymentdetail.service.PaymentDetailWritePlatformService;
import org.mifosplatform.portfolio.savings.api.SavingsApiConstants;
import org.mifosplatform.portfolio.savings.data.SavingsAccountDataValidator;
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionDataValidator;
import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepositoryWrapper;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransactionRepository;
import org.mifosplatform.portfolio.savings.domain.SavingsProduct;
import org.mifosplatform.portfolio.savings.domain.SavingsProductRepository;
import org.mifosplatform.portfolio.savings.exception.SavingsProductNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.mifosplatform.portfolio.savings.domain.SavingsHelper;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements SavingsAccountWritePlatformService {
private final static Logger logger = LoggerFactory.getLogger(SavingsAccountWritePlatformServiceJpaRepositoryImpl.class);
private final PlatformSecurityContext context;
private final SavingsAccountRepositoryWrapper savingAccountRepository;
private final SavingsAccountTransactionRepository savingsAccountTransactionRepository;
private final SavingsAccountAssembler savingAccountAssembler;
private final SavingsAccountDataValidator savingsAccountDataValidator;
private final SavingsAccountTransactionDataValidator savingsAccountTransactionDataValidator;
private final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory;
private final ClientRepositoryWrapper clientRepository;
private final GroupRepository groupRepository;
private final SavingsProductRepository savingsProductRepository;
private final PaymentDetailWritePlatformService paymentDetailWritePlatformService;
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper;
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
private final SavingsHelper savingsHelper = new SavingsHelper();
@Autowired
public SavingsAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
final SavingsAccountRepositoryWrapper savingAccountRepository,
final SavingsAccountTransactionRepository savingsAccountTransactionRepository,
final SavingsAccountAssembler savingAccountAssembler, final SavingsAccountDataValidator savingsAccountDataValidator,
final SavingsAccountAssembler savingAccountAssembler,
final SavingsAccountTransactionDataValidator savingsAccountTransactionDataValidator,
final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory, final ClientRepositoryWrapper clientRepository,
final GroupRepository groupRepository, final SavingsProductRepository savingsProductRepository,
final PaymentDetailWritePlatformService paymentDetailWritePlatformService,
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper,
final JournalEntryWritePlatformService journalEntryWritePlatformService) {
@@ -87,184 +68,29 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
this.savingsAccountTransactionRepository = savingsAccountTransactionRepository;
this.savingAccountAssembler = savingAccountAssembler;
this.savingsAccountTransactionDataValidator = savingsAccountTransactionDataValidator;
this.accountIdentifierGeneratorFactory = accountIdentifierGeneratorFactory;
this.savingsAccountDataValidator = savingsAccountDataValidator;
this.clientRepository = clientRepository;
this.groupRepository = groupRepository;
this.savingsProductRepository = savingsProductRepository;
this.paymentDetailWritePlatformService = paymentDetailWritePlatformService;
this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper;
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
}
/*
* Guaranteed to throw an exception no matter what the data integrity issue
* is.
*/
private void handleDataIntegrityIssues(final JsonCommand command, final DataAccessException dve) {
final StringBuilder errorCodeBuilder = new StringBuilder("error.msg.").append(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
final Throwable realCause = dve.getMostSpecificCause();
if (realCause.getMessage().contains("sa_account_no_UNIQUE")) {
final String accountNo = command.stringValueOfParameterNamed("accountNo");
errorCodeBuilder.append(".duplicate.accountNo");
throw new PlatformDataIntegrityException(errorCodeBuilder.toString(), "Savings account with accountNo " + accountNo
+ " already exists", "accountNo", accountNo);
} else if (realCause.getMessage().contains("sa_external_id_UNIQUE")) {
final String externalId = command.stringValueOfParameterNamed("externalId");
errorCodeBuilder.append(".duplicate.externalId");
throw new PlatformDataIntegrityException(errorCodeBuilder.toString(), "Savings account with externalId " + externalId
+ " already exists", "externalId", externalId);
}
errorCodeBuilder.append(".unknown.data.integrity.issue");
logger.error(dve.getMessage(), dve);
throw new PlatformDataIntegrityException(errorCodeBuilder.toString(), "Unknown data integrity issue with savings account.");
}
@Transactional
@Override
public CommandProcessingResult createSavingAccount(final JsonCommand command) {
try {
this.context.authenticatedUser();
this.savingsAccountDataValidator.validateForCreate(command.json());
final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(command, existingTransactionIds,
existingReversedTransactionIds);
this.savingAccountRepository.save(account);
if (account.isAccountNumberRequiresAutoGeneration()) {
final AccountNumberGenerator accountNoGenerator = this.accountIdentifierGeneratorFactory
.determineLoanAccountNoGenerator(account.getId());
account.updateAccountNo(accountNoGenerator.generate());
this.savingAccountRepository.save(account);
}
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);
final Long savingsId = account.getId();
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(savingsId) //
.withOfficeId(account.officeId()) //
.withClientId(account.clientId()) //
.withGroupId(account.groupId()) //
.withSavingsId(savingsId) //
.build();
} catch (DataAccessException dve) {
handleDataIntegrityIssues(command, dve);
return CommandProcessingResult.empty();
}
}
@Transactional
@Override
public CommandProcessingResult updateSavingAccount(final Long savingsId, final JsonCommand command) {
try {
this.context.authenticatedUser();
this.savingsAccountDataValidator.validateForUpdate(command.json());
final Map<String, Object> changes = new LinkedHashMap<String, Object>(20);
final SavingsAccount account = this.savingAccountRepository.findOneWithNotFoundDetection(savingsId);
account.update(command, changes);
if (!changes.isEmpty()) {
if (changes.containsKey(SavingsApiConstants.clientIdParamName)) {
final Long clientId = command.longValueOfParameterNamed(SavingsApiConstants.clientIdParamName);
if (clientId != null) {
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
account.update(client);
} else {
final Client client = null;
account.update(client);
}
}
if (changes.containsKey(SavingsApiConstants.groupIdParamName)) {
final Long groupId = command.longValueOfParameterNamed(SavingsApiConstants.groupIdParamName);
if (groupId != null) {
final Group group = this.groupRepository.findOne(groupId);
if (group == null) { throw new GroupNotFoundException(groupId); }
account.update(group);
} else {
final Group group = null;
account.update(group);
}
}
if (changes.containsKey(SavingsApiConstants.productIdParamName)) {
final Long productId = command.longValueOfParameterNamed(SavingsApiConstants.productIdParamName);
final SavingsProduct product = this.savingsProductRepository.findOne(productId);
if (product == null) { throw new SavingsProductNotFoundException(productId); }
account.update(product);
}
this.savingAccountRepository.saveAndFlush(account);
}
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(savingsId) //
.withOfficeId(account.officeId()) //
.withClientId(account.clientId()) //
.withGroupId(account.groupId()) //
.withSavingsId(savingsId) //
.with(changes) //
.build();
} catch (DataAccessException dve) {
handleDataIntegrityIssues(command, dve);
return new CommandProcessingResult(Long.valueOf(-1));
}
}
@Transactional
@Override
public CommandProcessingResult deleteSavingAccount(final Long savingsId) {
this.context.authenticatedUser();
final SavingsAccount account = this.savingAccountRepository.findOneWithNotFoundDetection(savingsId);
this.savingAccountRepository.delete(account);
return new CommandProcessingResultBuilder() //
.withEntityId(savingsId) //
.withOfficeId(account.officeId()) //
.withClientId(account.clientId()) //
.withGroupId(account.groupId()) //
.withSavingsId(savingsId) //
.build();
}
@Transactional
@Override
public CommandProcessingResult activate(final Long savingsId, final JsonCommand command) {
this.context.authenticatedUser();
final AppUser user = this.context.authenticatedUser();
this.savingsAccountTransactionDataValidator.validateActivation(command);
final SavingsAccount account = this.savingAccountRepository.findOneWithNotFoundDetection(savingsId);
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
final LocalDate activationDate = command.localDateValueOfParameterNamed("activationDate");
final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
account.activate(fmt, activationDate, existingReversedTransactionIds, existingReversedTransactionIds);
this.savingAccountRepository.save(account);
final Map<String, Object> changes = account.activate(user, command, DateUtils.getLocalDateOfTenant(),
existingReversedTransactionIds, existingReversedTransactionIds);
if (!changes.isEmpty()) {
this.savingAccountRepository.save(account);
}
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);
@@ -274,6 +100,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
.withClientId(account.clientId()) //
.withGroupId(account.groupId()) //
.withSavingsId(savingsId) //
.with(changes) //
.build();
}
@@ -285,7 +112,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
this.savingsAccountTransactionDataValidator.validate(command);
final SavingsAccount account = this.savingAccountRepository.findOneWithNotFoundDetection(savingsId);
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
@@ -298,8 +125,18 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN);
final SavingsAccountTransaction deposit = account.deposit(fmt, transactionDate, transactionAmount, existingTransactionIds,
existingReversedTransactionIds, paymentDetail);
if (account.isBeforeLastPostingPeriod(transactionDate)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(this.savingsHelper, mc, today, existingTransactionIds, existingReversedTransactionIds);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(this.savingsHelper, mc, today);
}
final Long transactionId = saveTransactionToGenerateTransactionId(deposit);
this.savingAccountRepository.save(account);
@@ -329,7 +166,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
this.savingsAccountTransactionDataValidator.validate(command);
final SavingsAccount account = this.savingAccountRepository.findOneWithNotFoundDetection(savingsId);
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate");
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount");
@@ -342,8 +179,18 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN);
final SavingsAccountTransaction withdrawal = account.withdraw(fmt, transactionDate, transactionAmount, existingTransactionIds,
existingReversedTransactionIds, paymentDetail);
if (account.isBeforeLastPostingPeriod(transactionDate)) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(this.savingsHelper, mc, today, existingTransactionIds, existingReversedTransactionIds);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterestUsing(this.savingsHelper, mc, today);
}
final Long transactionId = saveTransactionToGenerateTransactionId(withdrawal);
this.savingAccountRepository.save(account);
@@ -363,7 +210,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Override
public CommandProcessingResult applyAnnualFee(final Long savingsId) {
final SavingsAccount account = this.savingAccountRepository.findOneWithNotFoundDetection(savingsId);
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
final LocalDate transactionDate = LocalDate.now();
@@ -372,8 +219,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final SavingsAccountTransaction annualFee = account.addAnnualFee(fmt, transactionDate, existingTransactionIds,
existingReversedTransactionIds);
final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN);
final SavingsAccountTransaction annualFee = account.addAnnualFee(this.savingsHelper, mc, fmt, transactionDate,
existingTransactionIds, existingReversedTransactionIds);
final Long transactionId = saveTransactionToGenerateTransactionId(annualFee);
this.savingAccountRepository.save(account);
@@ -388,14 +236,19 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
.build();
}
@Transactional
@Override
public CommandProcessingResult calculateInterest(final Long savingsId, final JsonCommand command) {
public CommandProcessingResult calculateInterest(final Long savingsId) {
this.context.authenticatedUser();
final SavingsAccount account = this.savingAccountRepository.findOneWithNotFoundDetection(savingsId);
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.calculateInterest(today);
final MathContext mc = new MathContext(15, RoundingMode.HALF_EVEN);
account.calculateInterestUsing(this.savingsHelper, mc, today);
this.savingAccountRepository.save(account);
return new CommandProcessingResultBuilder() //
@@ -409,15 +262,17 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Transactional
@Override
public CommandProcessingResult postInterest(final Long savingsId, final JsonCommand command) {
public CommandProcessingResult postInterest(final Long savingsId) {
this.context.authenticatedUser();
final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final SavingsAccount account = this.savingAccountRepository.findOneWithNotFoundDetection(savingsId);
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
final LocalDate today = DateUtils.getLocalDateOfTenant();
account.postInterest(today, existingTransactionIds, existingReversedTransactionIds);
final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN);
account.postInterest(this.savingsHelper, mc, today, existingTransactionIds, existingReversedTransactionIds);
this.savingAccountRepository.save(account);
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);
@@ -0,0 +1,26 @@
/**
* 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.service;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
public interface SavingsApplicationProcessWritePlatformService {
CommandProcessingResult submitApplication(JsonCommand command);
CommandProcessingResult modifyApplication(Long savingsId, JsonCommand command);
CommandProcessingResult deleteApplication(Long savingsId);
CommandProcessingResult approveApplication(Long savingsId, JsonCommand command);
CommandProcessingResult undoApplicationApproval(Long savingsId, JsonCommand command);
CommandProcessingResult rejectApplication(Long savingsId, JsonCommand command);
CommandProcessingResult applicantWithdrawsFromApplication(Long savingsId, JsonCommand command);
}
@@ -0,0 +1,382 @@
/**
* 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.service;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.portfolio.client.domain.AccountNumberGenerator;
import org.mifosplatform.portfolio.client.domain.AccountNumberGeneratorFactory;
import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper;
import org.mifosplatform.portfolio.group.domain.Group;
import org.mifosplatform.portfolio.group.domain.GroupRepository;
import org.mifosplatform.portfolio.group.exception.GroupNotFoundException;
import org.mifosplatform.portfolio.note.domain.Note;
import org.mifosplatform.portfolio.note.domain.NoteRepository;
import org.mifosplatform.portfolio.savings.SavingsApiConstants;
import org.mifosplatform.portfolio.savings.data.SavingsAccountDataValidator;
import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepositoryWrapper;
import org.mifosplatform.portfolio.savings.domain.SavingsProduct;
import org.mifosplatform.portfolio.savings.domain.SavingsProductRepository;
import org.mifosplatform.portfolio.savings.exception.SavingsProductNotFoundException;
import org.mifosplatform.useradministration.domain.AppUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl implements SavingsApplicationProcessWritePlatformService {
private final static Logger logger = LoggerFactory.getLogger(SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl.class);
private final PlatformSecurityContext context;
private final SavingsAccountRepositoryWrapper savingAccountRepository;
private final SavingsAccountAssembler savingAccountAssembler;
private final SavingsAccountDataValidator savingsAccountDataValidator;
private final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory;
private final ClientRepositoryWrapper clientRepository;
private final GroupRepository groupRepository;
private final SavingsProductRepository savingsProductRepository;
private final NoteRepository noteRepository;
private final SavingsAccountApplicationTransitionApiJsonValidator savingsAccountApplicationTransitionApiJsonValidator;
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper;
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
@Autowired
public SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
final SavingsAccountRepositoryWrapper savingAccountRepository, final SavingsAccountAssembler savingAccountAssembler,
final SavingsAccountDataValidator savingsAccountDataValidator,
final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory, final ClientRepositoryWrapper clientRepository,
final GroupRepository groupRepository, final SavingsProductRepository savingsProductRepository,
final NoteRepository noteRepository,
final SavingsAccountApplicationTransitionApiJsonValidator savingsAccountApplicationTransitionApiJsonValidator,
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper,
final JournalEntryWritePlatformService journalEntryWritePlatformService) {
this.context = context;
this.savingAccountRepository = savingAccountRepository;
this.savingAccountAssembler = savingAccountAssembler;
this.accountIdentifierGeneratorFactory = accountIdentifierGeneratorFactory;
this.savingsAccountDataValidator = savingsAccountDataValidator;
this.clientRepository = clientRepository;
this.groupRepository = groupRepository;
this.savingsProductRepository = savingsProductRepository;
this.noteRepository = noteRepository;
this.savingsAccountApplicationTransitionApiJsonValidator = savingsAccountApplicationTransitionApiJsonValidator;
this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper;
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
}
/*
* Guaranteed to throw an exception no matter what the data integrity issue
* is.
*/
private void handleDataIntegrityIssues(final JsonCommand command, final DataAccessException dve) {
final StringBuilder errorCodeBuilder = new StringBuilder("error.msg.").append(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
final Throwable realCause = dve.getMostSpecificCause();
if (realCause.getMessage().contains("sa_account_no_UNIQUE")) {
final String accountNo = command.stringValueOfParameterNamed("accountNo");
errorCodeBuilder.append(".duplicate.accountNo");
throw new PlatformDataIntegrityException(errorCodeBuilder.toString(), "Savings account with accountNo " + accountNo
+ " already exists", "accountNo", accountNo);
} else if (realCause.getMessage().contains("sa_external_id_UNIQUE")) {
final String externalId = command.stringValueOfParameterNamed("externalId");
errorCodeBuilder.append(".duplicate.externalId");
throw new PlatformDataIntegrityException(errorCodeBuilder.toString(), "Savings account with externalId " + externalId
+ " already exists", "externalId", externalId);
}
errorCodeBuilder.append(".unknown.data.integrity.issue");
logger.error(dve.getMessage(), dve);
throw new PlatformDataIntegrityException(errorCodeBuilder.toString(), "Unknown data integrity issue with savings account.");
}
@Transactional
@Override
public CommandProcessingResult submitApplication(final JsonCommand command) {
try {
this.savingsAccountDataValidator.validateForSubmit(command.json());
final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(command);
this.savingAccountRepository.save(account);
if (account.isAccountNumberRequiresAutoGeneration()) {
final AccountNumberGenerator accountNoGenerator = this.accountIdentifierGeneratorFactory
.determineSavingsAccountNoGenerator(account.getId());
account.updateAccountNo(accountNoGenerator.generate());
this.savingAccountRepository.save(account);
}
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);
final Long savingsId = account.getId();
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(savingsId) //
.withOfficeId(account.officeId()) //
.withClientId(account.clientId()) //
.withGroupId(account.groupId()) //
.withSavingsId(savingsId) //
.build();
} catch (DataAccessException dve) {
handleDataIntegrityIssues(command, dve);
return CommandProcessingResult.empty();
}
}
@Transactional
@Override
public CommandProcessingResult modifyApplication(final Long savingsId, final JsonCommand command) {
try {
this.savingsAccountDataValidator.validateForUpdate(command.json());
final Map<String, Object> changes = new LinkedHashMap<String, Object>(20);
final SavingsAccount account = this.savingAccountRepository.findOneWithNotFoundDetection(savingsId);
account.update(command, changes);
if (!changes.isEmpty()) {
if (changes.containsKey(SavingsApiConstants.clientIdParamName)) {
final Long clientId = command.longValueOfParameterNamed(SavingsApiConstants.clientIdParamName);
if (clientId != null) {
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
account.update(client);
} else {
final Client client = null;
account.update(client);
}
}
if (changes.containsKey(SavingsApiConstants.groupIdParamName)) {
final Long groupId = command.longValueOfParameterNamed(SavingsApiConstants.groupIdParamName);
if (groupId != null) {
final Group group = this.groupRepository.findOne(groupId);
if (group == null) { throw new GroupNotFoundException(groupId); }
account.update(group);
} else {
final Group group = null;
account.update(group);
}
}
if (changes.containsKey(SavingsApiConstants.productIdParamName)) {
final Long productId = command.longValueOfParameterNamed(SavingsApiConstants.productIdParamName);
final SavingsProduct product = this.savingsProductRepository.findOne(productId);
if (product == null) { throw new SavingsProductNotFoundException(productId); }
account.update(product);
}
this.savingAccountRepository.saveAndFlush(account);
}
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(savingsId) //
.withOfficeId(account.officeId()) //
.withClientId(account.clientId()) //
.withGroupId(account.groupId()) //
.withSavingsId(savingsId) //
.with(changes) //
.build();
} catch (DataAccessException dve) {
handleDataIntegrityIssues(command, dve);
return new CommandProcessingResult(Long.valueOf(-1));
}
}
@Transactional
@Override
public CommandProcessingResult deleteApplication(final Long savingsId) {
this.context.authenticatedUser();
final SavingsAccount account = this.savingAccountRepository.findOneWithNotFoundDetection(savingsId);
List<Note> relatedNotes = this.noteRepository.findBySavingsAccountId(savingsId);
this.noteRepository.deleteInBatch(relatedNotes);
this.savingAccountRepository.delete(account);
return new CommandProcessingResultBuilder() //
.withEntityId(savingsId) //
.withOfficeId(account.officeId()) //
.withClientId(account.clientId()) //
.withGroupId(account.groupId()) //
.withSavingsId(savingsId) //
.build();
}
@Transactional
@Override
public CommandProcessingResult approveApplication(final Long savingsId, final JsonCommand command) {
final AppUser currentUser = context.authenticatedUser();
this.savingsAccountApplicationTransitionApiJsonValidator.validateApproval(command.json());
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(savingsId);
final Map<String, Object> changes = savingsAccount.approveApplication(currentUser, command, DateUtils.getLocalDateOfTenant());
if (!changes.isEmpty()) {
this.savingAccountRepository.save(savingsAccount);
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.savingNote(savingsAccount, noteText);
changes.put("note", noteText);
this.noteRepository.save(note);
}
}
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(savingsId) //
.withOfficeId(savingsAccount.officeId()) //
.withClientId(savingsAccount.clientId()) //
.withGroupId(savingsAccount.groupId()) //
.withSavingsId(savingsId) //
.with(changes) //
.build();
}
@Transactional
@Override
public CommandProcessingResult undoApplicationApproval(final Long savingsId, final JsonCommand command) {
context.authenticatedUser();
this.savingsAccountApplicationTransitionApiJsonValidator.validateForUndo(command.json());
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(savingsId);
final Map<String, Object> changes = savingsAccount.undoApplicationApproval();
if (!changes.isEmpty()) {
this.savingAccountRepository.save(savingsAccount);
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.savingNote(savingsAccount, noteText);
changes.put("note", noteText);
this.noteRepository.save(note);
}
}
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(savingsId) //
.withOfficeId(savingsAccount.officeId()) //
.withClientId(savingsAccount.clientId()) //
.withGroupId(savingsAccount.groupId()) //
.withSavingsId(savingsId) //
.with(changes) //
.build();
}
@Transactional
@Override
public CommandProcessingResult rejectApplication(final Long savingsId, final JsonCommand command) {
final AppUser currentUser = context.authenticatedUser();
this.savingsAccountApplicationTransitionApiJsonValidator.validateRejection(command.json());
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(savingsId);
final Map<String, Object> changes = savingsAccount.rejectApplication(currentUser, command, DateUtils.getLocalDateOfTenant());
if (!changes.isEmpty()) {
this.savingAccountRepository.save(savingsAccount);
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.savingNote(savingsAccount, noteText);
changes.put("note", noteText);
this.noteRepository.save(note);
}
}
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(savingsId) //
.withOfficeId(savingsAccount.officeId()) //
.withClientId(savingsAccount.clientId()) //
.withGroupId(savingsAccount.groupId()) //
.withSavingsId(savingsId) //
.with(changes) //
.build();
}
@Transactional
@Override
public CommandProcessingResult applicantWithdrawsFromApplication(final Long savingsId, final JsonCommand command) {
final AppUser currentUser = context.authenticatedUser();
this.savingsAccountApplicationTransitionApiJsonValidator.validateApplicantWithdrawal(command.json());
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(savingsId);
final Map<String, Object> changes = savingsAccount.applicantWithdrawsFromApplication(currentUser, command,
DateUtils.getLocalDateOfTenant());
if (!changes.isEmpty()) {
this.savingAccountRepository.save(savingsAccount);
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.savingNote(savingsAccount, noteText);
changes.put("note", noteText);
this.noteRepository.save(note);
}
}
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(savingsId) //
.withOfficeId(savingsAccount.officeId()) //
.withClientId(savingsAccount.clientId()) //
.withGroupId(savingsAccount.groupId()) //
.withSavingsId(savingsId) //
.with(changes) //
.build();
}
private void postJournalEntries(final SavingsAccount savingsAccount, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds) {
final MonetaryCurrency currency = savingsAccount.getCurrency();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency);
final Map<String, Object> accountingBridgeData = savingsAccount.deriveAccountingBridgeData(applicationCurrency.toData(),
existingTransactionIds, existingReversedTransactionIds);
journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData);
}
}
@@ -10,12 +10,12 @@ import java.util.Collection;
import java.util.List;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.portfolio.savings.domain.SavingsCompoundingInterestPeriodType;
import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationDaysInYearType;
import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationType;
import org.mifosplatform.portfolio.savings.domain.SavingsInterestPostingPeriodType;
import org.mifosplatform.portfolio.savings.domain.SavingsPeriodFrequencyType;
import org.mifosplatform.portfolio.savings.domain.SavingsWithdrawalFeesType;
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType;
import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType;
import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType;
import org.mifosplatform.portfolio.savings.SavingsWithdrawalFeesType;
import org.springframework.stereotype.Service;
@Service
@@ -47,13 +47,13 @@ public class SavingsDropdownReadPlatformServiceImpl implements SavingsDropdownRe
public Collection<EnumOptionData> retrieveCompoundingInterestPeriodTypeOptions() {
List<EnumOptionData> allowedOptions = Arrays.asList( //
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.DAILY), //
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.WEEKLY), //
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.BIWEEKLY), //
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.MONTHLY), //
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.QUATERLY), //
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.BI_ANNUAL), //
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.ANNUAL), //
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.NO_COMPOUNDING_SIMPLE_INTEREST) //
// SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.WEEKLY), //
// SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.BIWEEKLY), //
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.MONTHLY)
// SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.QUATERLY), //
// SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.BI_ANNUAL), //
// SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.ANNUAL), //
// SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.NO_COMPOUNDING_SIMPLE_INTEREST) //
);
return allowedOptions;
@@ -62,10 +62,9 @@ public class SavingsDropdownReadPlatformServiceImpl implements SavingsDropdownRe
@Override
public Collection<EnumOptionData> retrieveInterestPostingPeriodTypeOptions() {
List<EnumOptionData> allowedOptions = Arrays.asList( //
SavingsEnumerations.interestPostingPeriodType(SavingsInterestPostingPeriodType.MONTHLY), //
SavingsEnumerations.interestPostingPeriodType(SavingsInterestPostingPeriodType.QUATERLY), //
SavingsEnumerations.interestPostingPeriodType(SavingsInterestPostingPeriodType.BI_ANNUAL), //
SavingsEnumerations.interestPostingPeriodType(SavingsInterestPostingPeriodType.ANNUAL) //
SavingsEnumerations.interestPostingPeriodType(SavingsPostingInterestPeriodType.MONTHLY), //
SavingsEnumerations.interestPostingPeriodType(SavingsPostingInterestPeriodType.QUATERLY), //
SavingsEnumerations.interestPostingPeriodType(SavingsPostingInterestPeriodType.ANNUAL) //
);
return allowedOptions;
@@ -6,16 +6,16 @@
package org.mifosplatform.portfolio.savings.service;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.portfolio.savings.SavingsAccountTransactionType;
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType;
import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType;
import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType;
import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType;
import org.mifosplatform.portfolio.savings.SavingsWithdrawalFeesType;
import org.mifosplatform.portfolio.savings.data.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.SavingsCompoundingInterestPeriodType;
import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationDaysInYearType;
import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationType;
import org.mifosplatform.portfolio.savings.domain.SavingsInterestPostingPeriodType;
import org.mifosplatform.portfolio.savings.domain.SavingsPeriodFrequencyType;
import org.mifosplatform.portfolio.savings.domain.SavingsWithdrawalFeesType;
public class SavingsEnumerations {
@@ -94,59 +94,81 @@ public class SavingsEnumerations {
public static SavingsAccountStatusEnumData status(final SavingsAccountStatusType type) {
final boolean submittedAndPendingApproval = type.isSubmittedAndPendingApproval();
final boolean isApproved = type.isApproved();
final boolean isRejected = type.isRejected();
final boolean isWithdrawnByApplicant = type.isApplicationWithdrawnByApplicant();
final boolean isActive = type.isActive();
final boolean isClosed = type.isClosed();
SavingsAccountStatusEnumData optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.INVALID.getValue().longValue(),
SavingsAccountStatusType.INVALID.getCode(), "Invalid", false, false, false);
SavingsAccountStatusType.INVALID.getCode(), "Invalid", submittedAndPendingApproval, isApproved, isRejected,
isWithdrawnByApplicant, isActive, isClosed);
switch (type) {
case INVALID:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.INVALID.getValue().longValue(),
SavingsAccountStatusType.INVALID.getCode(), "Invalid", type.isUnactivated(), type.isActive(), type.isClosed());
SavingsAccountStatusType.INVALID.getCode(), "Invalid", submittedAndPendingApproval, isApproved, isRejected,
isWithdrawnByApplicant, isActive, isClosed);
break;
case UNACTIVATED:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.UNACTIVATED.getValue().longValue(),
SavingsAccountStatusType.UNACTIVATED.getCode(), "Unactivated", type.isUnactivated(), type.isActive(),
type.isClosed());
case SUBMITTED_AND_PENDING_APPROVAL:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.getValue()
.longValue(), SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.getCode(), "Submitted and pending approval",
submittedAndPendingApproval, isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed);
break;
case REJECTED:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.REJECTED.getValue().longValue(),
SavingsAccountStatusType.REJECTED.getCode(), "Rejected", submittedAndPendingApproval, isApproved, isRejected,
isWithdrawnByApplicant, isActive, isClosed);
break;
case WITHDRAWN_BY_APPLICANT:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.WITHDRAWN_BY_APPLICANT.getValue().longValue(),
SavingsAccountStatusType.WITHDRAWN_BY_APPLICANT.getCode(), "Withdrawn by applicant", submittedAndPendingApproval,
isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed);
break;
case APPROVED:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.APPROVED.getValue().longValue(),
SavingsAccountStatusType.APPROVED.getCode(), "Approved", submittedAndPendingApproval, isApproved, isRejected,
isWithdrawnByApplicant, isActive, isClosed);
break;
case ACTIVE:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.ACTIVE.getValue().longValue(),
SavingsAccountStatusType.ACTIVE.getCode(), "Active", type.isUnactivated(), type.isActive(), type.isClosed());
SavingsAccountStatusType.ACTIVE.getCode(), "Active", submittedAndPendingApproval, isApproved, isRejected,
isWithdrawnByApplicant, isActive, isClosed);
break;
case CLOSED:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.CLOSED.getValue().longValue(),
SavingsAccountStatusType.CLOSED.getCode(), "Closed", type.isUnactivated(), type.isActive(), type.isClosed());
SavingsAccountStatusType.CLOSED.getCode(), "Closed", submittedAndPendingApproval, isApproved, isRejected,
isWithdrawnByApplicant, isActive, isClosed);
break;
}
return optionData;
}
public static EnumOptionData interestPostingPeriodType(final Integer type) {
return interestPostingPeriodType(SavingsInterestPostingPeriodType.fromInt(type));
return interestPostingPeriodType(SavingsPostingInterestPeriodType.fromInt(type));
}
public static EnumOptionData interestPostingPeriodType(final SavingsInterestPostingPeriodType type) {
public static EnumOptionData interestPostingPeriodType(final SavingsPostingInterestPeriodType type) {
final String codePrefix = "savings.interest.posting.period.";
EnumOptionData optionData = new EnumOptionData(SavingsInterestPostingPeriodType.INVALID.getValue().longValue(),
SavingsInterestPostingPeriodType.INVALID.getCode(), "Invalid");
EnumOptionData optionData = new EnumOptionData(SavingsPostingInterestPeriodType.INVALID.getValue().longValue(),
SavingsPostingInterestPeriodType.INVALID.getCode(), "Invalid");
switch (type) {
case INVALID:
break;
case MONTHLY:
optionData = new EnumOptionData(SavingsInterestPostingPeriodType.MONTHLY.getValue().longValue(), codePrefix
+ SavingsInterestPostingPeriodType.MONTHLY.getCode(), "Monthly");
optionData = new EnumOptionData(SavingsPostingInterestPeriodType.MONTHLY.getValue().longValue(), codePrefix
+ SavingsPostingInterestPeriodType.MONTHLY.getCode(), "Monthly");
break;
case QUATERLY:
optionData = new EnumOptionData(SavingsInterestPostingPeriodType.QUATERLY.getValue().longValue(), codePrefix
+ SavingsInterestPostingPeriodType.QUATERLY.getCode(), "Quarterly");
break;
case BI_ANNUAL:
optionData = new EnumOptionData(SavingsInterestPostingPeriodType.BI_ANNUAL.getValue().longValue(), codePrefix
+ SavingsInterestPostingPeriodType.BI_ANNUAL.getCode(), "Semi-Annual");
optionData = new EnumOptionData(SavingsPostingInterestPeriodType.QUATERLY.getValue().longValue(), codePrefix
+ SavingsPostingInterestPeriodType.QUATERLY.getCode(), "Quarterly");
break;
case ANNUAL:
optionData = new EnumOptionData(SavingsInterestPostingPeriodType.ANNUAL.getValue().longValue(), codePrefix
+ SavingsInterestPostingPeriodType.ANNUAL.getCode(), "Annually");
optionData = new EnumOptionData(SavingsPostingInterestPeriodType.ANNUAL.getValue().longValue(), codePrefix
+ SavingsPostingInterestPeriodType.ANNUAL.getCode(), "Annually");
break;
}
@@ -170,35 +192,51 @@ public class SavingsEnumerations {
optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.DAILY.getValue().longValue(), codePrefix
+ SavingsCompoundingInterestPeriodType.DAILY.getCode(), "Daily");
break;
case WEEKLY:
optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.WEEKLY.getValue().longValue(), codePrefix
+ SavingsCompoundingInterestPeriodType.WEEKLY.getCode(), "Weekly");
break;
case BIWEEKLY:
optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.BIWEEKLY.getValue().longValue(), codePrefix
+ SavingsCompoundingInterestPeriodType.BIWEEKLY.getCode(), "Bi-Weekly");
break;
// case WEEKLY:
// optionData = new
// EnumOptionData(SavingsCompoundingInterestPeriodType.WEEKLY.getValue().longValue(),
// codePrefix
// + SavingsCompoundingInterestPeriodType.WEEKLY.getCode(),
// "Weekly");
// break;
// case BIWEEKLY:
// optionData = new
// EnumOptionData(SavingsCompoundingInterestPeriodType.BIWEEKLY.getValue().longValue(),
// codePrefix
// + SavingsCompoundingInterestPeriodType.BIWEEKLY.getCode(),
// "Bi-Weekly");
// break;
case MONTHLY:
optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.MONTHLY.getValue().longValue(), codePrefix
+ SavingsCompoundingInterestPeriodType.MONTHLY.getCode(), "Monthly");
break;
case QUATERLY:
optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.QUATERLY.getValue().longValue(), codePrefix
+ SavingsCompoundingInterestPeriodType.QUATERLY.getCode(), "Quarterly");
break;
case BI_ANNUAL:
optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.BI_ANNUAL.getValue().longValue(), codePrefix
+ SavingsCompoundingInterestPeriodType.BI_ANNUAL.getCode(), "Semi-Annual");
break;
case ANNUAL:
optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.ANNUAL.getValue().longValue(), codePrefix
+ SavingsCompoundingInterestPeriodType.ANNUAL.getCode(), "Annually");
break;
case NO_COMPOUNDING_SIMPLE_INTEREST:
optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.NO_COMPOUNDING_SIMPLE_INTEREST.getValue().longValue(),
codePrefix + SavingsCompoundingInterestPeriodType.NO_COMPOUNDING_SIMPLE_INTEREST.getCode(),
"No Compounding - Simple Interest");
break;
// case QUATERLY:
// optionData = new
// EnumOptionData(SavingsCompoundingInterestPeriodType.QUATERLY.getValue().longValue(),
// codePrefix
// + SavingsCompoundingInterestPeriodType.QUATERLY.getCode(),
// "Quarterly");
// break;
// case BI_ANNUAL:
// optionData = new
// EnumOptionData(SavingsCompoundingInterestPeriodType.BI_ANNUAL.getValue().longValue(),
// codePrefix
// + SavingsCompoundingInterestPeriodType.BI_ANNUAL.getCode(),
// "Semi-Annual");
// break;
// case ANNUAL:
// optionData = new
// EnumOptionData(SavingsCompoundingInterestPeriodType.ANNUAL.getValue().longValue(),
// codePrefix
// + SavingsCompoundingInterestPeriodType.ANNUAL.getCode(), "Annually");
// break;
// case NO_COMPOUNDING_SIMPLE_INTEREST:
// optionData = new
// EnumOptionData(SavingsCompoundingInterestPeriodType.NO_COMPOUNDING_SIMPLE_INTEREST.getValue().longValue(),
// codePrefix +
// SavingsCompoundingInterestPeriodType.NO_COMPOUNDING_SIMPLE_INTEREST.getCode(),
// "No Compounding - Simple Interest");
// break;
}
return optionData;
@@ -24,7 +24,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.accountingRuleParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
@Service
public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements SavingsProductWritePlatformService {
@@ -205,5 +205,4 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService
logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Results affected by update: " + savingsIds.size());
}
}
@@ -4,20 +4,20 @@ import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
public class MonetaryCurrencyBuilder {
private String code = "XOF";
private int digitsAfterDecimal = 0;
private String code = "XOF";
private int digitsAfterDecimal = 0;
public MonetaryCurrency build() {
return new MonetaryCurrency(code, digitsAfterDecimal);
}
public MonetaryCurrency build() {
return new MonetaryCurrency(code, digitsAfterDecimal);
}
public MonetaryCurrencyBuilder withCode(String withCode) {
this.code = withCode;
return this;
}
public MonetaryCurrencyBuilder withCode(final String withCode) {
this.code = withCode;
return this;
}
public MonetaryCurrencyBuilder withDigitsAfterDecimal(int withDigitsAfterDecimal) {
this.digitsAfterDecimal = withDigitsAfterDecimal;
return this;
}
public MonetaryCurrencyBuilder withDigitsAfterDecimal(final int withDigitsAfterDecimal) {
this.digitsAfterDecimal = withDigitsAfterDecimal;
return this;
}
}
@@ -7,20 +7,20 @@ import org.mifosplatform.organisation.monetary.domain.Money;
public class MoneyBuilder {
private MonetaryCurrency currencyDetail = new MonetaryCurrencyBuilder().build();
private BigDecimal newAmount = BigDecimal.ZERO;
public Money build() {
return Money.of(currencyDetail, newAmount);
}
private MonetaryCurrency currencyDetail = new MonetaryCurrencyBuilder().build();
private BigDecimal newAmount = BigDecimal.ZERO;
public MoneyBuilder with(MonetaryCurrency withDetail) {
this.currencyDetail = withDetail;
return this;
}
public MoneyBuilder with(String withAmount) {
this.newAmount = BigDecimal.valueOf(Double.valueOf(withAmount));
return this;
}
public Money build() {
return Money.of(currencyDetail, newAmount);
}
public MoneyBuilder with(final MonetaryCurrency withDetail) {
this.currencyDetail = withDetail;
return this;
}
public MoneyBuilder with(final String withAmount) {
this.newAmount = BigDecimal.valueOf(Double.valueOf(withAmount));
return this;
}
}