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.springSecurityVersion = '3.1.4.RELEASE'
project.ext.hibernateCoreVersion = '4.1.9.Final' project.ext.hibernateCoreVersion = '4.1.9.Final'
project.ext.jerseyVersion = '1.17' 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.mysqlUser='root'
project.ext.mysqlPassword='mysql' project.ext.mysqlPassword='mysql'
@@ -123,8 +123,8 @@ dependencies {
[group: 'joda-time', name: 'joda-time', version: '2.1'], [group: 'joda-time', name: 'joda-time', version: '2.1'],
[group: 'net.sourceforge.javacsv', name: 'javacsv', version: '2.0'], [group: 'net.sourceforge.javacsv', name: 'javacsv', version: '2.0'],
[group: 'org.apache.commons', name: 'commons-email', version: '1.2'], [group: 'org.apache.commons', name: 'commons-email', version: '1.3.1'],
[group: 'commons-lang', name: 'commons-lang', version: '2.4'], [group: 'org.apache.commons', name: 'commons-lang3', version: '3.1'],
[group: 'org.slf4j', name: 'slf4j-api', version: '1.7.5'], [group: 'org.slf4j', name: 'slf4j-api', version: '1.7.5'],
[group: 'org.slf4j', name: 'jcl-over-slf4j', 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'] [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', 'org.mockito:mockito-all:1.9.5',
'com.jayway.restassured:rest-assured:1.7.2' '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.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; 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; 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; 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; import com.google.gson.JsonElement;
@@ -415,42 +415,6 @@ public class CommandWrapper {
return isUserResource() && isUpdate() && loggedInUserId.equals(this.entityId); 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() { public boolean isSavingsProductResource() {
return this.entityName.equalsIgnoreCase("SAVINGSPRODUCT"); return this.entityName.equalsIgnoreCase("SAVINGSPRODUCT");
} }
@@ -459,6 +423,22 @@ public class CommandWrapper {
return this.entityName.equalsIgnoreCase("SAVINGSACCOUNT"); 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() { public boolean isSavingsAccountActivation() {
return this.actionName.equalsIgnoreCase("ACTIVATE") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT"); return this.actionName.equalsIgnoreCase("ACTIVATE") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
} }
@@ -757,6 +757,42 @@ public class CommandWrapperBuilder {
return this; 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) { public CommandWrapperBuilder savingsAccountActivation(final Long accountId) {
this.actionName = "ACTIVATE"; this.actionName = "ACTIVATE";
this.entityName = "SAVINGSACCOUNT"; this.entityName = "SAVINGSACCOUNT";
@@ -1036,7 +1072,7 @@ public class CommandWrapperBuilder {
return this; return this;
} }
public CommandWrapperBuilder assignRole(Long groupId) { public CommandWrapperBuilder assignRole(final Long groupId) {
this.actionName = "ASSIGNROLE"; this.actionName = "ASSIGNROLE";
this.entityName = "GROUP"; this.entityName = "GROUP";
this.groupId = groupId; this.groupId = groupId;
@@ -1045,7 +1081,7 @@ public class CommandWrapperBuilder {
return this; return this;
} }
public CommandWrapperBuilder unassignRole(Long groupId, Long roleId) { public CommandWrapperBuilder unassignRole(final Long groupId, final Long roleId) {
this.actionName = "UNASSIGNROLE"; this.actionName = "UNASSIGNROLE";
this.entityName = "GROUP"; this.entityName = "GROUP";
this.groupId = groupId; this.groupId = groupId;
@@ -1054,7 +1090,7 @@ public class CommandWrapperBuilder {
return this; return this;
} }
public CommandWrapperBuilder updateRole(Long groupId, Long roleId) { public CommandWrapperBuilder updateRole(final Long groupId, final Long roleId) {
this.actionName = "UPDATEROLE"; this.actionName = "UPDATEROLE";
this.entityName = "GROUP"; this.entityName = "GROUP";
this.groupId = groupId; this.groupId = groupId;
@@ -1063,7 +1099,7 @@ public class CommandWrapperBuilder {
return this; return this;
} }
public CommandWrapperBuilder unassignClientStaff(Long clientId) { public CommandWrapperBuilder unassignClientStaff(final Long clientId) {
this.actionName = "UNASSIGNSTAFF"; this.actionName = "UNASSIGNSTAFF";
this.entityName = "CLIENT"; this.entityName = "CLIENT";
this.entityId = clientId; this.entityId = clientId;
@@ -1072,7 +1108,7 @@ public class CommandWrapperBuilder {
return this; return this;
} }
public CommandWrapperBuilder assignClientStaff(Long clientId) { public CommandWrapperBuilder assignClientStaff(final Long clientId) {
this.actionName = "ASSIGNSTAFF"; this.actionName = "ASSIGNSTAFF";
this.entityName = "CLIENT"; this.entityName = "CLIENT";
this.entityId = clientId; this.entityId = clientId;
@@ -374,11 +374,23 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
} }
} else if (wrapper.isSavingsAccountResource()) { } else if (wrapper.isSavingsAccountResource()) {
if (wrapper.isCreate()) { if (wrapper.isCreate()) {
handler = this.applicationContext.getBean("createSavingsAccountCommandHandler", NewCommandSourceHandler.class); handler = this.applicationContext
.getBean("savingsAccountApplicationSubmittalCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isUpdate()) { } else if (wrapper.isUpdate()) {
handler = this.applicationContext.getBean("updateSavingsAccountCommandHandler", NewCommandSourceHandler.class); handler = this.applicationContext.getBean("savingsAccountApplicationModificationCommandHandler",
NewCommandSourceHandler.class);
} else if (wrapper.isDelete()) { } 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()) { } else if (wrapper.isSavingsAccountDeposit()) {
handler = this.applicationContext.getBean("depositSavingsAccountCommandHandler", NewCommandSourceHandler.class); handler = this.applicationContext.getBean("depositSavingsAccountCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isSavingsAccountWithdrawal()) { } else if (wrapper.isSavingsAccountWithdrawal()) {
@@ -46,6 +46,7 @@ public final class JsonCommand {
private final Long supportedEntityId; private final Long supportedEntityId;
private final String transactionId; private final String transactionId;
private final String url; private final String url;
public static JsonCommand from(final String jsonCommand, final JsonElement parsedCommand, final FromJsonHelper fromApiJsonHelper, 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 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 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, public JsonCommand(final Long commandId, final String jsonCommand, final JsonElement parsedCommand,
final FromJsonHelper fromApiJsonHelper, final String entityName, final Long resourceId, final Long subresourceId, 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 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 supportedEntityType, final Long supportedEntityId, final String transactionId, final String url) {
final String url) {
this.commandId = commandId; this.commandId = commandId;
this.jsonCommand = jsonCommand; this.jsonCommand = jsonCommand;
this.parsedCommand = parsedCommand; this.parsedCommand = parsedCommand;
@@ -150,7 +150,7 @@ public final class JsonCommand {
} }
public String getUrl() { public String getUrl() {
return this.url; return this.url;
} }
private boolean differenceExists(final LocalDate baseValue, final LocalDate workingCopyValue) { private boolean differenceExists(final LocalDate baseValue, final LocalDate workingCopyValue) {
@@ -187,10 +187,10 @@ public final class JsonCommand {
boolean differenceExists = false; boolean differenceExists = false;
if (baseValue != null) { if (baseValue != null) {
if(workingCopyValue != null) { if (workingCopyValue != null) {
differenceExists = !baseValue.equals(workingCopyValue); differenceExists = !baseValue.equals(workingCopyValue);
} else { } else {
differenceExists = true; differenceExists = true;
} }
} else { } else {
differenceExists = workingCopyValue != null; differenceExists = workingCopyValue != null;
@@ -203,11 +203,11 @@ public final class JsonCommand {
boolean differenceExists = false; boolean differenceExists = false;
if (baseValue != null) { if (baseValue != null) {
if(workingCopyValue != null) { if (workingCopyValue != null) {
differenceExists = baseValue.compareTo(workingCopyValue) != 0; differenceExists = baseValue.compareTo(workingCopyValue) != 0;
}else { } else {
differenceExists = true; differenceExists = true;
} }
} else { } else {
differenceExists = workingCopyValue != null; differenceExists = workingCopyValue != null;
} }
@@ -301,6 +301,15 @@ public final class JsonCommand {
return StringUtils.defaultIfEmpty(value, ""); 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) { public boolean isChangeInBigDecimalParameterNamed(final String parameterName, final BigDecimal existingValue) {
boolean isChanged = false; boolean isChanged = false;
if (parameterExists(parameterName)) { if (parameterExists(parameterName)) {
@@ -314,19 +323,48 @@ public final class JsonCommand {
boolean isChanged = false; boolean isChanged = false;
if (parameterExists(parameterName)) { if (parameterExists(parameterName)) {
final BigDecimal workingValue = bigDecimalValueOfParameterNamed(parameterName); final BigDecimal workingValue = bigDecimalValueOfParameterNamed(parameterName);
if(workingValue == null && existingValue != null){ if (workingValue == null && existingValue != null) {
isChanged = true; isChanged = true;
}else{ } else {
isChanged = differenceExists(existingValue, workingValue); isChanged = differenceExists(existingValue, workingValue);
} }
} }
return isChanged; 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) { public BigDecimal bigDecimalValueOfParameterNamed(final String parameterName) {
return this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(parameterName, parsedCommand); 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) { public boolean isChangeInIntegerParameterNamed(final String parameterName, final Integer existingValue) {
boolean isChanged = false; boolean isChanged = false;
if (parameterExists(parameterName)) { if (parameterExists(parameterName)) {
@@ -340,9 +378,9 @@ public final class JsonCommand {
boolean isChanged = false; boolean isChanged = false;
if (parameterExists(parameterName)) { if (parameterExists(parameterName)) {
final Integer workingValue = integerValueOfParameterNamed(parameterName); final Integer workingValue = integerValueOfParameterNamed(parameterName);
if(workingValue == null && existingValue != null){ if (workingValue == null && existingValue != null) {
isChanged = true; isChanged = true;
}else{ } else {
isChanged = differenceExists(existingValue, workingValue); isChanged = differenceExists(existingValue, workingValue);
} }
} }
@@ -353,6 +391,10 @@ public final class JsonCommand {
return this.fromApiJsonHelper.extractIntegerWithLocaleNamed(parameterName, parsedCommand); 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) { public boolean isChangeInIntegerSansLocaleParameterNamed(final String parameterName, final Integer existingValue) {
boolean isChanged = false; boolean isChanged = false;
if (parameterExists(parameterName)) { if (parameterExists(parameterName)) {
@@ -209,22 +209,21 @@ public class DataValidatorBuilder {
public DataValidatorBuilder isOneOfTheseValues(final Object... values) { public DataValidatorBuilder isOneOfTheseValues(final Object... values) {
if (value == null && ignoreNullValue) { return this; } if (value == null && ignoreNullValue) { return this; }
if (value != null) { List<Object> valuesList = Arrays.asList(values);
List<Object> valuesList = Arrays.asList(values); String valuesListStr = StringUtils.join(valuesList, ", ");
String valuesListStr = StringUtils.join(valuesList, ", ");
if (!valuesList.contains(this.value)) { if (this.value == null || !valuesList.contains(this.value)) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter) StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".is.not.one.of.expected.enumerations"); .append(".is.not.one.of.expected.enumerations");
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(" must be one of [ ") StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(" must be one of [ ")
.append(valuesListStr).append(" ] ").append("."); .append(valuesListStr).append(" ] ").append(".");
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(),
defaultEnglishMessage.toString(), parameter, this.value, values); parameter, this.value, values);
dataValidationErrors.add(error); dataValidationErrors.add(error);
}
} }
return this; return this;
} }
@@ -238,8 +237,8 @@ public class DataValidatorBuilder {
if (valuesList.contains(this.value)) { if (valuesList.contains(this.value)) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter) StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".is.one.of.unwanted.enumerations"); .append(".is.one.of.unwanted.enumerations");
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(" must not be any of [ ") StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter)
.append(valuesListStr).append(" ] ").append("."); .append(" must not be any of [ ").append(valuesListStr).append(" ] ").append(".");
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), parameter, this.value, values); defaultEnglishMessage.toString(), parameter, this.value, values);
@@ -268,6 +267,9 @@ public class DataValidatorBuilder {
return this; return this;
} }
/*
* should be used with .notNull() before it
*/
public DataValidatorBuilder zeroOrPositiveAmount() { public DataValidatorBuilder zeroOrPositiveAmount() {
if (value == null && ignoreNullValue) { return this; } if (value == null && ignoreNullValue) { return this; }
@@ -286,6 +288,30 @@ public class DataValidatorBuilder {
return this; 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() { public DataValidatorBuilder integerGreaterThanZero() {
if (value == null && ignoreNullValue) { return this; } if (value == null && ignoreNullValue) { return this; }
@@ -304,6 +330,9 @@ public class DataValidatorBuilder {
return this; return this;
} }
/*
* should be used with .notNull() before it
*/
public DataValidatorBuilder longGreaterThanZero() { public DataValidatorBuilder longGreaterThanZero() {
if (value == null && ignoreNullValue) { return this; } if (value == null && ignoreNullValue) { return this; }
@@ -341,7 +370,7 @@ public class DataValidatorBuilder {
public DataValidatorBuilder jsonArrayNotEmpty() { public DataValidatorBuilder jsonArrayNotEmpty() {
if (value == null && ignoreNullValue) { return this; } if (value == null && ignoreNullValue) { return this; }
JsonArray array = (JsonArray)value; JsonArray array = (JsonArray) value;
if (value != null && !array.iterator().hasNext()) { if (value != null && !array.iterator().hasNext()) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter) StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".cannot.be.empty"); .append(".cannot.be.empty");
@@ -433,8 +462,8 @@ public class DataValidatorBuilder {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter) StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".must.be.provided.when.").append(parameterName).append(".is.").append(parameterValue); .append(".must.be.provided.when.").append(parameterName).append(".is.").append(parameterValue);
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter) StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter).append(" must be provided when ")
.append(" must be provided when ").append(parameterName).append(" is ").append(parameterValue); .append(parameterName).append(" is ").append(parameterValue);
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(),
parameter, value, parameterName, parameterValue); parameter, value, parameterName, parameterValue);
dataValidationErrors.add(error); dataValidationErrors.add(error);
@@ -457,12 +486,12 @@ public class DataValidatorBuilder {
} }
public DataValidatorBuilder inMinAndMaxAmountRange(final BigDecimal minimumAmount, final BigDecimal maximumAmount) { 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())); BigDecimal amount = BigDecimal.valueOf(Double.valueOf(value.toString()));
if (amount.compareTo(minimumAmount) == -1 || amount.compareTo(maximumAmount) == 1) { if (amount.compareTo(minimumAmount) == -1 || amount.compareTo(maximumAmount) == 1) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter) StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".amount.is.not.within.min.max.range"); .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(" ."); .append(" must be between ").append(minimumAmount).append(" and ").append(maximumAmount).append(" .");
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), parameter, amount, minimumAmount, maximumAmount); defaultEnglishMessage.toString(), parameter, amount, minimumAmount, maximumAmount);
@@ -474,12 +503,12 @@ public class DataValidatorBuilder {
} }
public DataValidatorBuilder notLessThanMin(final BigDecimal min) { public DataValidatorBuilder notLessThanMin(final BigDecimal min) {
if (min != null && value != null){ if (min != null && value != null) {
BigDecimal amount = BigDecimal.valueOf(Double.valueOf(value.toString())); BigDecimal amount = BigDecimal.valueOf(Double.valueOf(value.toString()));
if (amount.compareTo(min) == -1) { if (amount.compareTo(min) == -1) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter) StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".is.less.than.min"); .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); .append(" must not be less than minimum value ").append(min);
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), parameter, amount, min); defaultEnglishMessage.toString(), parameter, amount, min);
@@ -491,12 +520,12 @@ public class DataValidatorBuilder {
} }
public DataValidatorBuilder notGreaterThanMax(final BigDecimal max) { public DataValidatorBuilder notGreaterThanMax(final BigDecimal max) {
if (max != null && value != null){ if (max != null && value != null) {
BigDecimal amount = BigDecimal.valueOf(Double.valueOf(value.toString())); BigDecimal amount = BigDecimal.valueOf(Double.valueOf(value.toString()));
if (amount.compareTo(max) == 1) { if (amount.compareTo(max) == 1) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter) StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".is.greater.than.max"); .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); .append(" must not be more than maximum value ").append(max);
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), parameter, amount, max); defaultEnglishMessage.toString(), parameter, amount, max);
@@ -579,22 +608,21 @@ public class DataValidatorBuilder {
} }
public DataValidatorBuilder matchesRegularExpression(final String expression) { public DataValidatorBuilder matchesRegularExpression(final String expression) {
if (value == null && ignoreNullValue) { return this; } if (value == null && ignoreNullValue) { return this; }
if (value != null && !value.toString().matches(expression)) { if (value != null && !value.toString().matches(expression)) {
StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter) StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(resource).append(".").append(parameter)
.append(".does.not.match.regexp"); .append(".does.not.match.regexp");
StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter) StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(parameter)
.append(" must match the provided regular expression [ ") .append(" must match the provided regular expression [ ").append(expression).append(" ] ").append(".");
.append(expression).append(" ] ").append(".");
ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(),
defaultEnglishMessage.toString(), parameter, this.value, expression); parameter, this.value, expression);
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
return this; return this;
} }
private DataValidatorBuilder validateStringFor(final String validInputs) { private DataValidatorBuilder validateStringFor(final String validInputs) {
@@ -610,8 +638,8 @@ public class DataValidatorBuilder {
if (validationErr) { if (validationErr) {
final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".")
.append(this.parameter).append(".value.should.true.or.false"); .append(this.parameter).append(".value.should.true.or.false");
final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter) final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter).append(
.append(" value should true or false "); " value should true or false ");
final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
defaultEnglishMessage.toString(), this.parameter, this.value); defaultEnglishMessage.toString(), this.parameter, this.value);
this.dataValidationErrors.add(error); this.dataValidationErrors.add(error);
@@ -5,6 +5,8 @@
*/ */
package org.mifosplatform.infrastructure.core.domain; 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.Days;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
@@ -38,6 +40,14 @@ public class LocalDateInterval {
return Days.daysBetween(this.startDate, this.endDate).getDays(); 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) { public boolean contains(final LocalDate target) {
return isBetweenInclusive(this.startDate, this.endDate, target); return isBetweenInclusive(this.startDate, this.endDate, target);
} }
@@ -49,4 +59,9 @@ public class LocalDateInterval {
public boolean fallsBefore(final LocalDate dateToCheck) { public boolean fallsBefore(final LocalDate dateToCheck) {
return this.endDate.isBefore(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; 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.lang.reflect.Type;
import java.util.ArrayList; import java.util.ArrayList;
@@ -452,7 +452,7 @@ public final class Client extends AbstractPersistable<Long> {
this.staff = null; this.staff = null;
} }
public void assignStaff(Staff staff) { public void assignStaff(final Staff staff) {
this.staff = staff; this.staff = staff;
} }
@@ -466,4 +466,8 @@ public final class Client extends AbstractPersistable<Long> {
public Integer getStatus() { public Integer getStatus() {
return status; 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(); this.status = GroupingTypeStatus.ACTIVE.getValue();
} }
public boolean isActivatedAfter(final LocalDate submittedOn) {
return getActivationLocalDate().isAfter(submittedOn);
}
public boolean isNotActive() { public boolean isNotActive() {
return !isActive(); return !isActive();
} }
@@ -137,8 +137,8 @@ public class Loan extends AbstractPersistable<Long> {
@Column(name = "loan_status_id", nullable = false) @Column(name = "loan_status_id", nullable = false)
private Integer loanStatus; private Integer loanStatus;
@Column(name = "sync_disbursement_with_meeting", nullable = true) @Column(name = "sync_disbursement_with_meeting", nullable = true)
private Boolean syncDisbursementWithMeeting; private Boolean syncDisbursementWithMeeting;
// loan application states // loan application states
@Temporal(TemporalType.DATE) @Temporal(TemporalType.DATE)
@@ -932,9 +932,9 @@ public class Loan extends AbstractPersistable<Long> {
final String syncDisbursementParameterName = "syncDisbursementWithMeeting"; final String syncDisbursementParameterName = "syncDisbursementWithMeeting";
if (command.isChangeInBooleanParameterNamed(syncDisbursementParameterName, isSyncDisbursementWithMeeting())) { if (command.isChangeInBooleanParameterNamed(syncDisbursementParameterName, isSyncDisbursementWithMeeting())) {
final Boolean valueAsInput = command.booleanObjectValueOfParameterNamed(syncDisbursementParameterName); final Boolean valueAsInput = command.booleanObjectValueOfParameterNamed(syncDisbursementParameterName);
actualChanges.put(syncDisbursementParameterName, valueAsInput); actualChanges.put(syncDisbursementParameterName, valueAsInput);
this.syncDisbursementWithMeeting = valueAsInput; this.syncDisbursementWithMeeting = valueAsInput;
} }
final String interestChargedFromDateParamName = "interestChargedFromDate"; final String interestChargedFromDateParamName = "interestChargedFromDate";
@@ -1100,18 +1100,12 @@ public class Loan extends AbstractPersistable<Long> {
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.a.future.date", errorMessage, submittedOn); throw new InvalidLoanStateTransitionException("submittal", "cannot.be.a.future.date", errorMessage, submittedOn);
} }
if (!(client == null)) { if (client != null && client.isActivatedAfter(submittedOn)) {
if (submittedOn.isBefore(client.getActivationLocalDate())) { final String errorMessage = "The date on which a loan is submitted cannot be earlier than client's activation date.";
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);
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.client.activation.date", errorMessage, } else if (group != null && group.isActivatedAfter(submittedOn)) {
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);
} 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 (submittedOn.isAfter(getExpectedDisbursedOnLocalDate())) { if (submittedOn.isAfter(getExpectedDisbursedOnLocalDate())) {
@@ -1379,14 +1373,17 @@ public class Loan extends AbstractPersistable<Long> {
final Integer loanTermFrequency = this.termFrequency; final Integer loanTermFrequency = this.termFrequency;
final PeriodFrequencyType loanTermPeriodFrequencyType = PeriodFrequencyType.fromInt(this.termPeriodFrequencyType); final PeriodFrequencyType loanTermPeriodFrequencyType = PeriodFrequencyType.fromInt(this.termPeriodFrequencyType);
//If ExpectedFirstRepaymentOnDate is null and repayment is synced with meeting then use firstRepaymentMeetingDate // If ExpectedFirstRepaymentOnDate is null and repayment is synced with
//to generate repayment schedule dates // meeting then use firstRepaymentMeetingDate
final LocalDate firstRepaymentOnDate = (getExpectedFirstRepaymentOnDate() == null) ? firstRepaymentMeetingDate : getExpectedFirstRepaymentOnDate(); // to generate repayment schedule dates
final LocalDate firstRepaymentOnDate = (getExpectedFirstRepaymentOnDate() == null) ? firstRepaymentMeetingDate
: getExpectedFirstRepaymentOnDate();
LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency,
loanTermPeriodFrequencyType, getDisbursementDate(), firstRepaymentOnDate, getInArrearsTolerance(), loanTermPeriodFrequencyType, getDisbursementDate(), firstRepaymentOnDate, getInArrearsTolerance(),
this.loanRepaymentScheduleDetail); 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); updateLoanSchedule(loanSchedule);
} }
@@ -2348,35 +2345,38 @@ public class Loan extends AbstractPersistable<Long> {
return this.closedOnDate; return this.closedOnDate;
} }
public void updateLoanRepaymentScheduleDates(final LocalDate meetingStartDate, final String recuringRule, final boolean isHolidayEnabled, final List<Holiday> holidays) { public void updateLoanRepaymentScheduleDates(final LocalDate meetingStartDate, final String recuringRule,
//update repayment dates of repayment schedule installaments final boolean isHolidayEnabled, final List<Holiday> holidays) {
LocalDate tmpFromDate = this.getDisbursementDate();//first repayment's from date is same as disbursement date. // update repayment dates of repayment schedule installaments
LocalDate tmpFromDate = this.getDisbursementDate();// first repayment's
// from date is same
// as disbursement
// date.
LocalDate newRepaymentDate = null; LocalDate newRepaymentDate = null;
for (LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : this.repaymentScheduleInstallments) { for (LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : this.repaymentScheduleInstallments) {
final LocalDate oldDueDate = loanRepaymentScheduleInstallment.getDueDate(); 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())) { if (oldDueDate.isAfter(meetingStartDate) && oldDueDate.isAfter(DateUtils.getLocalDateOfTenant())) {
final PeriodFrequencyType repaymentPeriodFrequencyType = this.loanRepaymentScheduleDetail.getRepaymentPeriodFrequencyType(); final PeriodFrequencyType repaymentPeriodFrequencyType = this.loanRepaymentScheduleDetail.getRepaymentPeriodFrequencyType();
final String frequency = CalendarHelper.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType); final String frequency = CalendarHelper.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType);
final Integer loanRepaymentInterval = this.loanRepaymentScheduleDetail.getRepayEvery(); final Integer loanRepaymentInterval = this.loanRepaymentScheduleDetail.getRepayEvery();
// reset repayment date with new meeting date // reset repayment date with new meeting date
newRepaymentDate = CalendarHelper.getNewRepaymentMeetingDate(recuringRule, meetingStartDate, oldDueDate, loanRepaymentInterval, frequency); newRepaymentDate = CalendarHelper.getNewRepaymentMeetingDate(recuringRule, meetingStartDate, oldDueDate,
if(isHolidayEnabled){ loanRepaymentInterval, frequency);
if (isHolidayEnabled) {
newRepaymentDate = HolidayUtil.getRepaymentRescheduleDateToIfHoliday(newRepaymentDate, holidays); newRepaymentDate = HolidayUtil.getRepaymentRescheduleDateToIfHoliday(newRepaymentDate, holidays);
} }
loanRepaymentScheduleInstallment.updateDueDate(newRepaymentDate); loanRepaymentScheduleInstallment.updateDueDate(newRepaymentDate);
//reset from date to get actual daysInPeriod // reset from date to get actual daysInPeriod
loanRepaymentScheduleInstallment.updateFromDate(tmpFromDate); loanRepaymentScheduleInstallment.updateFromDate(tmpFromDate);
tmpFromDate = newRepaymentDate;//update with new repayment date tmpFromDate = newRepaymentDate;// update with new repayment date
}else{ } else {
tmpFromDate = oldDueDate; tmpFromDate = oldDueDate;
} }
} }
} }
public void applyHolidayToRepaymentScheduleDates(final Holiday holiday) { public void applyHolidayToRepaymentScheduleDates(final Holiday holiday) {
// first repayment's from date is same as disbursement date. // first repayment's from date is same as disbursement date.
LocalDate tmpFromDate = this.getDisbursementDate(); LocalDate tmpFromDate = this.getDisbursementDate();
@@ -2384,8 +2384,9 @@ public class Loan extends AbstractPersistable<Long> {
for (LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : this.repaymentScheduleInstallments) { for (LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : this.repaymentScheduleInstallments) {
final LocalDate oldDueDate = loanRepaymentScheduleInstallment.getDueDate(); final LocalDate oldDueDate = loanRepaymentScheduleInstallment.getDueDate();
//update from date if it's not same as previous installament's due date. // update from date if it's not same as previous installament's due
if(!loanRepaymentScheduleInstallment.getFromDate().isEqual(tmpFromDate)){ // date.
if (!loanRepaymentScheduleInstallment.getFromDate().isEqual(tmpFromDate)) {
loanRepaymentScheduleInstallment.updateFromDate(tmpFromDate); loanRepaymentScheduleInstallment.updateFromDate(tmpFromDate);
} }
if (oldDueDate.isAfter(holiday.getToDateLocalDate())) break; if (oldDueDate.isAfter(holiday.getToDateLocalDate())) break;
@@ -2412,11 +2413,11 @@ public class Loan extends AbstractPersistable<Long> {
return this.loanProductCounter; return this.loanProductCounter;
} }
public void updateClientLoanCounter(Integer newLoanCounter) { public void updateClientLoanCounter(final Integer newLoanCounter) {
this.loanCounter = newLoanCounter; this.loanCounter = newLoanCounter;
} }
public void updateLoanProductLoanCounter(Integer newLoanProductLoanCounter) { public void updateLoanProductLoanCounter(final Integer newLoanProductLoanCounter) {
this.loanProductCounter = newLoanProductLoanCounter; this.loanProductCounter = newLoanProductLoanCounter;
} }
@@ -2432,7 +2433,6 @@ public class Loan extends AbstractPersistable<Long> {
return this.termFrequency; return this.termFrequency;
} }
public Integer getTermPeriodFrequencyType() { public Integer getTermPeriodFrequencyType() {
return this.termPeriodFrequencyType; return this.termPeriodFrequencyType;
} }
@@ -27,6 +27,6 @@ public class LoanApplicationApprovalCommandHandler implements NewCommandSourceHa
@Override @Override
public CommandProcessingResult processCommand(final JsonCommand command) { 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 @Override
public CommandProcessingResult processCommand(final JsonCommand command) { 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 @Override
public CommandProcessingResult processCommand(final JsonCommand command) { 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 @Override
public CommandProcessingResult processCommand(final JsonCommand command) { 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 @Override
public CommandProcessingResult processCommand(final JsonCommand command) { 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 @Override
public CommandProcessingResult processCommand(final JsonCommand command) { 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 @Override
public CommandProcessingResult processCommand(final JsonCommand command) { 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); baseDataValidator.reset().parameter(note).value(noteText).notExceedingLengthOf(1000);
} }
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
"Validation errors exist.", dataValidationErrors); }
} }
public void validateMinMaxConstraintValues(final JsonElement element, final LoanProduct loanProduct) { public void validateMinMaxConstraintValues(final JsonElement element, final LoanProduct loanProduct) {
@@ -10,17 +10,17 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
public interface LoanApplicationWritePlatformService { 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 @Transactional
@Override @Override
public CommandProcessingResult submitLoanApplication(final JsonCommand command) { public CommandProcessingResult submitApplication(final JsonCommand command) {
final AppUser currentUser = context.authenticatedUser(); final AppUser currentUser = context.authenticatedUser();
@@ -205,7 +205,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
@Transactional @Transactional
@Override @Override
public CommandProcessingResult modifyLoanApplication(final Long loanId, final JsonCommand command) { public CommandProcessingResult modifyApplication(final Long loanId, final JsonCommand command) {
try { try {
context.authenticatedUser(); context.authenticatedUser();
@@ -384,7 +384,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
@Transactional @Transactional
@Override @Override
public CommandProcessingResult deleteLoanApplication(final Long loanId) { public CommandProcessingResult deleteApplication(final Long loanId) {
context.authenticatedUser(); context.authenticatedUser();
@@ -408,7 +408,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
@Transactional @Transactional
@Override @Override
public CommandProcessingResult approveLoanApplication(final Long loanId, final JsonCommand command) { public CommandProcessingResult approveApplication(final Long loanId, final JsonCommand command) {
final AppUser currentUser = context.authenticatedUser(); final AppUser currentUser = context.authenticatedUser();
@@ -441,7 +441,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
@Transactional @Transactional
@Override @Override
public CommandProcessingResult undoLoanApplicationApproval(final Long loanId, final JsonCommand command) { public CommandProcessingResult undoApplicationApproval(final Long loanId, final JsonCommand command) {
context.authenticatedUser(); context.authenticatedUser();
@@ -473,7 +473,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
@Transactional @Transactional
@Override @Override
public CommandProcessingResult rejectLoanApplication(final Long loanId, final JsonCommand command) { public CommandProcessingResult rejectApplication(final Long loanId, final JsonCommand command) {
final AppUser currentUser = context.authenticatedUser(); final AppUser currentUser = context.authenticatedUser();
@@ -505,7 +505,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
@Transactional @Transactional
@Override @Override
public CommandProcessingResult applicantWithdrawsFromLoanApplication(final Long loanId, final JsonCommand command) { public CommandProcessingResult applicantWithdrawsFromApplication(final Long loanId, final JsonCommand command) {
final AppUser currentUser = context.authenticatedUser(); 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.group.domain.Group;
import org.mifosplatform.portfolio.loanaccount.domain.Loan; import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
import org.mifosplatform.useradministration.domain.AppUser; import org.mifosplatform.useradministration.domain.AppUser;
@Entity @Entity
@@ -53,10 +54,10 @@ public class Note extends AbstractAuditableCustom<AppUser, Long> {
@Column(name = "note_type_enum") @Column(name = "note_type_enum")
private final Integer noteTypeId; private final Integer noteTypeId;
// @SuppressWarnings("unused") @SuppressWarnings("unused")
// @ManyToOne @ManyToOne
// @JoinColumn(name = "saving_account_id", nullable = true) @JoinColumn(name = "savings_account_id", nullable = true)
// private SavingAccount savingAccount; private SavingsAccount savingsAccount;
public static Note clientNoteFromJson(final Client client, final JsonCommand command) { public static Note clientNoteFromJson(final Client client, final JsonCommand command) {
final String note = command.stringValueOfParameterNamed("note"); final String note = command.stringValueOfParameterNamed("note");
@@ -76,10 +77,9 @@ public class Note extends AbstractAuditableCustom<AppUser, Long> {
return new Note(loan, loanTransaction, note); return new Note(loan, loanTransaction, note);
} }
// public static Note savingNote(final SavingAccount account, final String public static Note savingNote(final SavingsAccount account, final String note) {
// note) { return new Note(account, note);
// return new Note(account, note); }
// }
private Note(final Client client, final String note) { private Note(final Client client, final String note) {
this.client = client; this.client = client;
@@ -118,12 +118,12 @@ public class Note extends AbstractAuditableCustom<AppUser, Long> {
this.noteTypeId = null; this.noteTypeId = null;
} }
// public Note(final SavingAccount account, final String note) { public Note(final SavingsAccount account, final String note) {
// this.savingAccount = account; this.savingsAccount = account;
// this.client = account.getClient(); this.client = account.getClient();
// this.note = note; this.note = note;
// this.noteTypeId = NoteType.SAVING_ACCOUNT.getValue(); this.noteTypeId = NoteType.SAVING_ACCOUNT.getValue();
// } }
public Map<String, Object> update(final JsonCommand command) { public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(7); 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 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, * 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/. * 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 * 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, * 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/. * 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.Arrays;
import java.util.HashSet; import java.util.HashSet;
@@ -32,8 +32,10 @@ public class SavingsApiConstants {
public static final String clientIdParamName = "clientId"; public static final String clientIdParamName = "clientId";
public static final String groupIdParamName = "groupId"; public static final String groupIdParamName = "groupId";
public static final String productIdParamName = "productId"; 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 activeParamName = "active";
public static final String activationDateParamName = "activationDate"; public static final String activationDateParamName = "activatedOnDate";
public static final String nameParamName = "name"; public static final String nameParamName = "name";
public static final String descriptionParamName = "description"; public static final String descriptionParamName = "description";
public static final String currencyCodeParamName = "currencyCode"; public static final String currencyCodeParamName = "currencyCode";
@@ -52,7 +54,7 @@ public class SavingsApiConstants {
public static final String annualFeeOnMonthDayParamName = "annualFeeOnMonthDay"; public static final String annualFeeOnMonthDayParamName = "annualFeeOnMonthDay";
public static final String accountingRuleParamName = "accountingRule"; public static final String accountingRuleParamName = "accountingRule";
public static final String paymentTypeIdParamName = "paymentTypeId"; 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 checkNumberParamName = "checkNumber";
public static final String routingCodeParamName = "routingCode"; public static final String routingCodeParamName = "routingCode";
public static final String receiptNumberParamName = "receiptNumber"; 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, public static final Set<String> SAVINGS_ACCOUNT_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
dateFormatParamName, monthDayFormatParamName, accountNoParamName, externalIdParamName, clientIdParamName, groupIdParamName, dateFormatParamName, monthDayFormatParamName, accountNoParamName, externalIdParamName, clientIdParamName, groupIdParamName,
productIdParamName, activeParamName, activationDateParamName, nominalAnnualInterestRateParamName, productIdParamName, fieldOfficerIdParamName, submittedOnDateParamName, nominalAnnualInterestRateParamName,
interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName,
interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName, interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName,
lockinPeriodFrequencyTypeParamName, withdrawalFeeAmountParamName, withdrawalFeeTypeParamName, annualFeeAmountParamName, 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( public static final Set<String> SAVINGS_ACCOUNT_TRANSACTION_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(
localeParamName, dateFormatParamName, transactionDateParamName, transactionAmountParamName, paymentTypeIdParamName, 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, public static final Set<String> SAVINGS_TRANSACTION_RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(idParamName,
"accountId", accountNoParamName, "currency", "amount")); "accountId", accountNoParamName, "currency", "amount"));
@@ -3,7 +3,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file, * 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/. * 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> * <p>
@@ -15,13 +19,13 @@ public enum SavingsCompoundingInterestPeriodType {
INVALID(0, "savingsCompoundingInterestPeriodType.invalid"), // INVALID(0, "savingsCompoundingInterestPeriodType.invalid"), //
DAILY(1, "savingsCompoundingInterestPeriodType.daily"), // DAILY(1, "savingsCompoundingInterestPeriodType.daily"), //
WEEKLY(2, "savingsCompoundingInterestPeriodType.weekly"), // // WEEKLY(2, "savingsCompoundingInterestPeriodType.weekly"), //
BIWEEKLY(3, "savingsCompoundingInterestPeriodType.biweekly"), // // BIWEEKLY(3, "savingsCompoundingInterestPeriodType.biweekly"), //
MONTHLY(4, "savingsCompoundingInterestPeriodType.monthly"), // MONTHLY(4, "savingsCompoundingInterestPeriodType.monthly");
QUATERLY(5, "savingsCompoundingInterestPeriodType.quarterly"), // // QUATERLY(5, "savingsCompoundingInterestPeriodType.quarterly"), //
BI_ANNUAL(6, "savingsCompoundingInterestPeriodType.biannual"), // // BI_ANNUAL(6, "savingsCompoundingInterestPeriodType.biannual"), //
ANNUAL(7, "savingsCompoundingInterestPeriodType.annual"), // // ANNUAL(7, "savingsCompoundingInterestPeriodType.annual"), //
NO_COMPOUNDING_SIMPLE_INTEREST(8, "savingsCompoundingInterestPeriodType.nocompounding"); // NO_COMPOUNDING_SIMPLE_INTEREST(8, "savingsCompoundingInterestPeriodType.nocompounding");
private final Integer value; private final Integer value;
private final String code; private final String code;
@@ -39,6 +43,15 @@ public enum SavingsCompoundingInterestPeriodType {
return code; 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) { public static SavingsCompoundingInterestPeriodType fromInt(final Integer type) {
SavingsCompoundingInterestPeriodType repaymentFrequencyType = SavingsCompoundingInterestPeriodType.INVALID; SavingsCompoundingInterestPeriodType repaymentFrequencyType = SavingsCompoundingInterestPeriodType.INVALID;
if (type != null) { if (type != null) {
@@ -47,25 +60,31 @@ public enum SavingsCompoundingInterestPeriodType {
repaymentFrequencyType = SavingsCompoundingInterestPeriodType.DAILY; repaymentFrequencyType = SavingsCompoundingInterestPeriodType.DAILY;
break; break;
case 2: case 2:
repaymentFrequencyType = SavingsCompoundingInterestPeriodType.WEEKLY; // repaymentFrequencyType =
// SavingsCompoundingInterestPeriodType.WEEKLY;
break; break;
case 3: case 3:
repaymentFrequencyType = SavingsCompoundingInterestPeriodType.BIWEEKLY; // repaymentFrequencyType =
// SavingsCompoundingInterestPeriodType.BIWEEKLY;
break; break;
case 4: case 4:
repaymentFrequencyType = SavingsCompoundingInterestPeriodType.MONTHLY; repaymentFrequencyType = SavingsCompoundingInterestPeriodType.MONTHLY;
break; break;
case 5: case 5:
repaymentFrequencyType = SavingsCompoundingInterestPeriodType.QUATERLY; // repaymentFrequencyType =
// SavingsCompoundingInterestPeriodType.QUATERLY;
break; break;
case 6: case 6:
repaymentFrequencyType = SavingsCompoundingInterestPeriodType.BI_ANNUAL; // repaymentFrequencyType =
// SavingsCompoundingInterestPeriodType.BI_ANNUAL;
break; break;
case 7: case 7:
repaymentFrequencyType = SavingsCompoundingInterestPeriodType.ANNUAL; // repaymentFrequencyType =
// SavingsCompoundingInterestPeriodType.ANNUAL;
break; break;
case 8: case 8:
repaymentFrequencyType = SavingsCompoundingInterestPeriodType.NO_COMPOUNDING_SIMPLE_INTEREST; // repaymentFrequencyType =
// SavingsCompoundingInterestPeriodType.NO_COMPOUNDING_SIMPLE_INTEREST;
break; break;
} }
} }
@@ -3,7 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file, * 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/. * You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
package org.mifosplatform.portfolio.savings.domain; package org.mifosplatform.portfolio.savings;
/** /**
* <ul> * <ul>
@@ -3,7 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file, * 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/. * You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
package org.mifosplatform.portfolio.savings.domain; package org.mifosplatform.portfolio.savings;
/** /**
* <ul> * <ul>
@@ -3,7 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file, * 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/. * 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. * 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, * 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/. * 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 { 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.core.serialization.DefaultToApiJsonSerializer;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants; import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants;
import org.mifosplatform.portfolio.savings.SavingsApiConstants;
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionData; import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionData;
import org.mifosplatform.portfolio.savings.service.SavingsAccountReadPlatformService; import org.mifosplatform.portfolio.savings.service.SavingsAccountReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -72,7 +73,8 @@ public class SavingsAccountTransactionsApiResource {
@Path("template") @Path("template")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ 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 final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME); 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.Consumes;
import javax.ws.rs.DELETE; import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.POST; import javax.ws.rs.POST;
import javax.ws.rs.PUT; 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.serialization.DefaultToApiJsonSerializer;
import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.infrastructure.core.service.Page;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; 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.group.service.SearchParameters;
import org.mifosplatform.portfolio.savings.SavingsApiConstants;
import org.mifosplatform.portfolio.savings.data.SavingsAccountData; import org.mifosplatform.portfolio.savings.data.SavingsAccountData;
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionData; import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionData;
import org.mifosplatform.portfolio.savings.data.SavingsProductData; import org.mifosplatform.portfolio.savings.data.SavingsProductData;
@@ -81,12 +84,15 @@ public class SavingsAccountsApiResource {
@Path("template") @Path("template")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String retrieveTemplate(@QueryParam("clientId") final Long clientId, @QueryParam("groupId") final Long groupId, public String template(@QueryParam("clientId") final Long clientId, @QueryParam("groupId") final Long groupId,
@QueryParam("productId") final Long productId, @Context final UriInfo uriInfo) { @QueryParam("productId") final Long productId,
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly,
@Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME); 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()); final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, savingsAccount, SavingsApiConstants.SAVINGS_ACCOUNT_RESPONSE_DATA_PARAMETERS); return this.toApiJsonSerializer.serialize(settings, savingsAccount, SavingsApiConstants.SAVINGS_ACCOUNT_RESPONSE_DATA_PARAMETERS);
@@ -113,7 +119,7 @@ public class SavingsAccountsApiResource {
@POST @POST
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ 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(); final CommandWrapper commandRequest = new CommandWrapperBuilder().createSavingsAccount().withJson(apiRequestBodyAsJson).build();
@@ -147,6 +153,7 @@ public class SavingsAccountsApiResource {
Collection<SavingsAccountTransactionData> transactions = null; Collection<SavingsAccountTransactionData> transactions = null;
Collection<SavingsProductData> productOptions = null; Collection<SavingsProductData> productOptions = null;
Collection<StaffData> fieldOfficerOptions = null;
Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null; Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
Collection<EnumOptionData> interestCompoundingPostingTypeOptions = null; Collection<EnumOptionData> interestCompoundingPostingTypeOptions = null;
Collection<EnumOptionData> interestCalculationTypeOptions = null; Collection<EnumOptionData> interestCalculationTypeOptions = null;
@@ -174,16 +181,49 @@ public class SavingsAccountsApiResource {
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
if (settings.isTemplate()) { if (settings.isTemplate()) {
productOptions = this.savingsProductReadPlatformService.retrieveAllForLookup(); 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(); interestCompoundingPeriodTypeOptions = this.dropdownReadPlatformService.retrieveCompoundingInterestPeriodTypeOptions();
interestCompoundingPostingTypeOptions = this.dropdownReadPlatformService.retrieveInterestPostingPeriodTypeOptions(); interestCompoundingPostingTypeOptions = this.dropdownReadPlatformService.retrieveInterestPostingPeriodTypeOptions();
interestCalculationTypeOptions = this.dropdownReadPlatformService.retrieveInterestCalculationTypeOptions(); interestCalculationTypeOptions = this.dropdownReadPlatformService.retrieveInterestCalculationTypeOptions();
interestCalculationDaysInYearTypeOptions = this.dropdownReadPlatformService.retrieveInterestCalculationDaysInYearTypeOptions();
lockinPeriodFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveLockinPeriodFrequencyTypeOptions(); lockinPeriodFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveLockinPeriodFrequencyTypeOptions();
withdrawalFeeTypeOptions = this.dropdownReadPlatformService.retrievewithdrawalFeeTypeOptions(); withdrawalFeeTypeOptions = this.dropdownReadPlatformService.retrievewithdrawalFeeTypeOptions();
} }
return SavingsAccountData.withTemplateOptions(savingsAccount, productOptions, interestCompoundingPeriodTypeOptions, return SavingsAccountData.withTemplateOptions(savingsAccount, productOptions, fieldOfficerOptions,
interestCompoundingPostingTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, interestCompoundingPeriodTypeOptions, interestCompoundingPostingTypeOptions, interestCalculationTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions); interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions);
} }
@PUT @PUT
@@ -210,7 +250,19 @@ public class SavingsAccountsApiResource {
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson); final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
CommandProcessingResult result = null; 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(); final CommandWrapper commandRequest = builder.savingsAccountActivation(accountId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (is(commandParam, "calculateInterest")) { } else if (is(commandParam, "calculateInterest")) {
@@ -223,8 +275,8 @@ public class SavingsAccountsApiResource {
if (result == null) { if (result == null) {
// //
throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate", "calculateInterest", throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "reject", "withdrawnByApplicant", "approve",
"postInterest" }); "undoapproval", "activate", "calculateInterest", "postInterest" });
} }
return this.toApiJsonSerializer.serialize(result); 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.data.CurrencyData;
import org.mifosplatform.organisation.monetary.service.CurrencyReadPlatformService; import org.mifosplatform.organisation.monetary.service.CurrencyReadPlatformService;
import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants; 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.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.SavingsDropdownReadPlatformService;
import org.mifosplatform.portfolio.savings.service.SavingsEnumerations; import org.mifosplatform.portfolio.savings.service.SavingsEnumerations;
import org.mifosplatform.portfolio.savings.service.SavingsProductReadPlatformService; import org.mifosplatform.portfolio.savings.service.SavingsProductReadPlatformService;
@@ -180,7 +181,7 @@ public class SavingsProductsApiResource {
.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.DAILY); .compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.DAILY);
final EnumOptionData interestPostingPeriodType = SavingsEnumerations final EnumOptionData interestPostingPeriodType = SavingsEnumerations
.interestPostingPeriodType(SavingsInterestPostingPeriodType.MONTHLY); .interestPostingPeriodType(SavingsPostingInterestPeriodType.MONTHLY);
final EnumOptionData interestCalculationType = SavingsEnumerations final EnumOptionData interestCalculationType = SavingsEnumerations
.interestCalculationType(SavingsInterestCalculationType.DAILY_BALANCE); .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.joda.time.MonthDay;
import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.organisation.staff.data.StaffData;
/** /**
* Immutable data object representing a savings account. * Immutable data object representing a savings account.
@@ -21,14 +22,16 @@ public class SavingsAccountData {
private final Long id; private final Long id;
private final String accountNo; private final String accountNo;
private final String externalId; private final String externalId;
private final SavingsAccountStatusEnumData status;
private final LocalDate activationDate;
private final Long groupId; private final Long groupId;
private final String groupName; private final String groupName;
private final Long clientId; private final Long clientId;
private final String clientName; private final String clientName;
private final Long savingsProductId; private final Long savingsProductId;
private final String savingsProductName; 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 CurrencyData currency;
private final BigDecimal nominalAnnualInterestRate; private final BigDecimal nominalAnnualInterestRate;
private final EnumOptionData interestCompoundingPeriodType; private final EnumOptionData interestCompoundingPeriodType;
@@ -53,6 +56,8 @@ public class SavingsAccountData {
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final Collection<SavingsProductData> productOptions; private final Collection<SavingsProductData> productOptions;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final Collection<StaffData> fieldOfficerOptions;
@SuppressWarnings("unused")
private final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions; private final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final Collection<EnumOptionData> interestPostingPeriodTypeOptions; private final Collection<EnumOptionData> interestPostingPeriodTypeOptions;
@@ -65,17 +70,19 @@ public class SavingsAccountData {
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final Collection<EnumOptionData> withdrawalFeeTypeOptions; private final Collection<EnumOptionData> withdrawalFeeTypeOptions;
public static SavingsAccountData instance(final Long id, final String accountNo, final String externalId, public static SavingsAccountData instance(final Long id, final String accountNo, final String externalId, final Long groupId,
final SavingsAccountStatusEnumData status, final LocalDate activationDate, final Long groupId, final String groupName, final String groupName, final Long clientId, final String clientName, final Long productId, final String productName,
final Long clientId, final String clientName, final Long productId, final String productName, final CurrencyData currency, final Long fieldOfficerId, final String fieldOfficerName, final SavingsAccountStatusEnumData status,
final BigDecimal interestRate, final EnumOptionData interestCompoundingPeriodType, final SavingsAccountApplicationTimelineData timeline, final CurrencyData currency, final BigDecimal interestRate,
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType, final EnumOptionData interestCompoundingPeriodType, final EnumOptionData interestPostingPeriodType,
final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType,
final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount, final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency,
final EnumOptionData withdrawalFeeType, final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay, final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount, final EnumOptionData withdrawalFeeType,
final LocalDate annualFeeNextDueDate, final SavingsAccountSummaryData summary) { final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay, final LocalDate annualFeeNextDueDate,
final SavingsAccountSummaryData summary) {
final Collection<SavingsProductData> productOptions = null; final Collection<SavingsProductData> productOptions = null;
final Collection<StaffData> fieldOfficerOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null; final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null; final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null; final Collection<EnumOptionData> interestCalculationTypeOptions = null;
@@ -85,32 +92,34 @@ public class SavingsAccountData {
final Collection<SavingsAccountTransactionData> transactions = null; final Collection<SavingsAccountTransactionData> transactions = null;
return new SavingsAccountData(id, accountNo, externalId, status, activationDate, groupId, groupName, clientId, clientName, return new SavingsAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
productId, productName, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, fieldOfficerId, fieldOfficerName, status, timeline, currency, interestRate, interestCompoundingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, annualFeeOnMonthDay, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount,
annualFeeNextDueDate, summary, transactions, productOptions, interestCompoundingPeriodTypeOptions, annualFeeOnMonthDay, annualFeeNextDueDate, summary, transactions, productOptions, fieldOfficerOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions); interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
} }
public static SavingsAccountData withTemplateOptions(final SavingsAccountData account, 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> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions, final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions, final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions, final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
final Collection<SavingsAccountTransactionData> transactions) { final Collection<SavingsAccountTransactionData> transactions) {
return new SavingsAccountData(account.id, account.accountNo, account.externalId, account.status, account.activationDate, return new SavingsAccountData(account.id, account.accountNo, account.externalId, account.groupId, account.groupName,
account.groupId, account.groupName, account.clientId, account.clientName, account.savingsProductId, account.clientId, account.clientName, account.savingsProductId, account.savingsProductName, account.fieldofficerId,
account.savingsProductName, account.currency, account.nominalAnnualInterestRate, account.interestCompoundingPeriodType, account.fieldofficerName, account.status, account.timeline, account.currency, account.nominalAnnualInterestRate,
account.interestPostingPeriodType, account.interestCalculationType, account.interestCalculationDaysInYearType, account.interestCompoundingPeriodType, account.interestPostingPeriodType, account.interestCalculationType,
account.minRequiredOpeningBalance, account.lockinPeriodFrequency, account.lockinPeriodFrequencyType, account.interestCalculationDaysInYearType, account.minRequiredOpeningBalance, account.lockinPeriodFrequency,
account.withdrawalFeeAmount, account.withdrawalFeeType, account.annualFeeAmount, account.annualFeeOnMonthDay, account.lockinPeriodFrequencyType, account.withdrawalFeeAmount, account.withdrawalFeeType, account.annualFeeAmount,
account.annualFeeNextDueDate, account.summary, transactions, productOptions, interestCompoundingPeriodTypeOptions, account.annualFeeOnMonthDay, account.annualFeeNextDueDate, account.summary, transactions, productOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions); interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
withdrawalFeeTypeOptions);
} }
public static SavingsAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId, public static SavingsAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId,
@@ -119,10 +128,12 @@ public class SavingsAccountData {
final Long id = null; final Long id = null;
final String accountNo = null; final String accountNo = null;
final String externalId = null; final String externalId = null;
final SavingsAccountStatusEnumData status = null;
final LocalDate activationDate = null;
final Long productId = null; final Long productId = null;
final String productName = 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 CurrencyData currency = null;
final BigDecimal nominalAnnualInterestRate = null; final BigDecimal nominalAnnualInterestRate = null;
final EnumOptionData interestPeriodType = null; final EnumOptionData interestPeriodType = null;
@@ -142,6 +153,7 @@ public class SavingsAccountData {
final Collection<SavingsAccountTransactionData> transactions = null; final Collection<SavingsAccountTransactionData> transactions = null;
final Collection<SavingsProductData> productOptions = null; final Collection<SavingsProductData> productOptions = null;
final Collection<StaffData> fieldOfficerOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null; final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null; final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null; final Collection<EnumOptionData> interestCalculationTypeOptions = null;
@@ -149,25 +161,26 @@ public class SavingsAccountData {
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null; final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null; final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
return new SavingsAccountData(id, accountNo, externalId, status, activationDate, groupId, groupName, clientId, clientName, return new SavingsAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
productId, productName, currency, nominalAnnualInterestRate, interestPeriodType, interestPostingPeriodType, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, annualFeeOnMonthDay, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount,
annualFeeNextDueDate, summary, transactions, productOptions, interestCompoundingPeriodTypeOptions, annualFeeOnMonthDay, annualFeeNextDueDate, summary, transactions, productOptions, fieldOfficerOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions); interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions);
} }
private SavingsAccountData(final Long id, final String accountNo, final String externalId, final SavingsAccountStatusEnumData status, private SavingsAccountData(final Long id, final String accountNo, final String externalId, final Long groupId, final String groupName,
final LocalDate activationDate, final Long groupId, final String groupName, final Long clientId, final String clientName, final Long clientId, final String clientName, final Long productId, final String productName, final Long fieldofficerId,
final Long productId, final String productName, final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, final String fieldofficerName, final SavingsAccountStatusEnumData status, final SavingsAccountApplicationTimelineData timeline,
final EnumOptionData interestPeriodType, final EnumOptionData interestPostingPeriodType, final CurrencyData currency, final BigDecimal nominalAnnualInterestRate, final EnumOptionData interestPeriodType,
final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType, final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency, final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount, final EnumOptionData withdrawalFeeType, final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final BigDecimal withdrawalFeeAmount,
final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay, final LocalDate annualFeeNextDueDate, final EnumOptionData withdrawalFeeType, final BigDecimal annualFeeAmount, final MonthDay annualFeeOnMonthDay,
final SavingsAccountSummaryData summary, final Collection<SavingsAccountTransactionData> transactions, final LocalDate annualFeeNextDueDate, final SavingsAccountSummaryData summary,
final Collection<SavingsProductData> productOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions, final Collection<SavingsAccountTransactionData> transactions, final Collection<SavingsProductData> productOptions,
final Collection<StaffData> fieldOfficerOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
final Collection<EnumOptionData> interestPostingPeriodTypeOptions, final Collection<EnumOptionData> interestPostingPeriodTypeOptions,
final Collection<EnumOptionData> interestCalculationTypeOptions, final Collection<EnumOptionData> interestCalculationTypeOptions,
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions, final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions,
@@ -175,14 +188,16 @@ public class SavingsAccountData {
this.id = id; this.id = id;
this.accountNo = accountNo; this.accountNo = accountNo;
this.externalId = externalId; this.externalId = externalId;
this.status = status;
this.activationDate = activationDate;
this.groupId = groupId; this.groupId = groupId;
this.groupName = groupName; this.groupName = groupName;
this.clientId = clientId; this.clientId = clientId;
this.clientName = clientName; this.clientName = clientName;
this.savingsProductId = productId; this.savingsProductId = productId;
this.savingsProductName = productName; this.savingsProductName = productName;
this.fieldofficerId = fieldofficerId;
this.fieldofficerName = fieldofficerName;
this.status = status;
this.timeline = timeline;
this.currency = currency; this.currency = currency;
this.nominalAnnualInterestRate = nominalAnnualInterestRate; this.nominalAnnualInterestRate = nominalAnnualInterestRate;
this.interestCompoundingPeriodType = interestPeriodType; this.interestCompoundingPeriodType = interestPeriodType;
@@ -202,6 +217,7 @@ public class SavingsAccountData {
this.transactions = transactions; this.transactions = transactions;
this.productOptions = productOptions; this.productOptions = productOptions;
this.fieldOfficerOptions = fieldOfficerOptions;
this.interestCompoundingPeriodTypeOptions = interestCompoundingPeriodTypeOptions; this.interestCompoundingPeriodTypeOptions = interestCompoundingPeriodTypeOptions;
this.interestPostingPeriodTypeOptions = interestPostingPeriodTypeOptions; this.interestPostingPeriodTypeOptions = interestPostingPeriodTypeOptions;
this.interestCalculationTypeOptions = interestCalculationTypeOptions; this.interestCalculationTypeOptions = interestCalculationTypeOptions;
@@ -5,25 +5,24 @@
*/ */
package org.mifosplatform.portfolio.savings.data; package org.mifosplatform.portfolio.savings.data;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.accountNoParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountNoParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activationDateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeAmountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.clientIdParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeOnMonthDayParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.externalIdParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.clientIdParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.groupIdParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.externalIdParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.groupIdParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationDaysInYearTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCompoundingPeriodTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestPostingPeriodTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.minRequiredOpeningBalanceParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.productIdParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nominalAnnualInterestRateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.submittedOnDateParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.productIdParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeAmountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeTypeParamName;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.math.BigDecimal; 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.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -56,7 +55,7 @@ public class SavingsAccountDataValidator {
this.fromApiJsonHelper = fromApiJsonHelper; this.fromApiJsonHelper = fromApiJsonHelper;
} }
public void validateForCreate(final String json) { public void validateForSubmit(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
@@ -85,13 +84,9 @@ public class SavingsAccountDataValidator {
final Long productId = fromApiJsonHelper.extractLongNamed(productIdParamName, element); final Long productId = fromApiJsonHelper.extractLongNamed(productIdParamName, element);
baseDataValidator.reset().parameter(productIdParamName).value(productId).notNull().integerGreaterThanZero(); baseDataValidator.reset().parameter(productIdParamName).value(productId).notNull().integerGreaterThanZero();
final Boolean active = fromApiJsonHelper.extractBooleanNamed(activeParamName, element); final LocalDate submittedOnDate = fromApiJsonHelper.extractLocalDateNamed(submittedOnDateParamName, element);
baseDataValidator.reset().parameter(activeParamName).value(active).notNull(); if (submittedOnDate == null) {
baseDataValidator.reset().parameter(submittedOnDateParamName).value(submittedOnDate).notNull();
LocalDate activationDate = null;
if (active != null && active.booleanValue()) {
activationDate = fromApiJsonHelper.extractLocalDateNamed(activationDateParamName, element);
baseDataValidator.reset().parameter(activationDateParamName).value(activationDate).notNull();
} }
if (fromApiJsonHelper.parameterExists(accountNoParamName, element)) { if (fromApiJsonHelper.parameterExists(accountNoParamName, element)) {
@@ -140,32 +135,32 @@ public class SavingsAccountDataValidator {
if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) { if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) {
final BigDecimal minOpeningBalance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minRequiredOpeningBalanceParamName, final BigDecimal minOpeningBalance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minRequiredOpeningBalanceParamName,
element); element);
baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).positiveAmount(); baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).zeroOrPositiveAmount();
} }
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) { if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) {
final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, element); final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).ignoreIfNull() baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).integerZeroOrGreater();
.positiveAmount();
if (lockinPeriodFrequency != null) { if (lockinPeriodFrequency != null) {
final Integer lockinPeriodFrequencyType = fromApiJsonHelper.extractIntegerSansLocaleNamed( final Integer lockinPeriodFrequencyType = fromApiJsonHelper.extractIntegerSansLocaleNamed(
lockinPeriodFrequencyTypeParamName, element); lockinPeriodFrequencyTypeParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).notNull() baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).notNull()
.inMinMaxRange(1, 3); .inMinMaxRange(0, 3);
} }
} }
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyTypeParamName, element)) { if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyTypeParamName, element)) {
final Integer lockinPeriodFrequencyType = fromApiJsonHelper.extractIntegerSansLocaleNamed(lockinPeriodFrequencyTypeParamName, final Integer lockinPeriodFrequencyType = fromApiJsonHelper.extractIntegerSansLocaleNamed(lockinPeriodFrequencyTypeParamName,
element); element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).inMinMaxRange(1, 3); baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).inMinMaxRange(0, 3);
if (lockinPeriodFrequencyType != null) { if (lockinPeriodFrequencyType != null) {
final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName,
element); 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 final BigDecimal withdrawalFeeAmount = fromApiJsonHelper
.extractBigDecimalWithLocaleNamed(withdrawalFeeAmountParamName, element); .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)) { if (this.fromApiJsonHelper.parameterExists(withdrawalFeeTypeParamName, element)) {
final Integer withdrawalFeeType = fromApiJsonHelper.extractIntegerSansLocaleNamed(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)) { if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) {
final BigDecimal annualFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(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) { if (annualFeeAmount != null) {
MonthDay monthDayOfAnnualFee = fromApiJsonHelper.extractMonthDayNamed(annualFeeOnMonthDayParamName, element); 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); throwExceptionIfValidationWarningsExist(dataValidationErrors);
} }
@@ -293,50 +310,38 @@ public class SavingsAccountDataValidator {
if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) { if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) {
final BigDecimal minOpeningBalance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minRequiredOpeningBalanceParamName, final BigDecimal minOpeningBalance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minRequiredOpeningBalanceParamName,
element); element);
baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).positiveAmount(); baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).ignoreIfNull()
.zeroOrPositiveAmount();
} }
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) { if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) {
final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, element); final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).ignoreIfNull() baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).ignoreIfNull()
.positiveAmount(); .integerZeroOrGreater();
if (lockinPeriodFrequency != null) {
final Integer lockinPeriodFrequencyType = fromApiJsonHelper.extractIntegerSansLocaleNamed(
lockinPeriodFrequencyTypeParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyTypeParamName).value(lockinPeriodFrequencyType).notNull()
.inMinMaxRange(1, 3);
}
} }
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyTypeParamName, element)) { if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyTypeParamName, element)) {
final Integer lockinPeriodFrequencyType = fromApiJsonHelper.extractIntegerSansLocaleNamed(lockinPeriodFrequencyTypeParamName, final Integer lockinPeriodFrequencyType = fromApiJsonHelper.extractIntegerSansLocaleNamed(lockinPeriodFrequencyTypeParamName,
element); 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();
}
} }
if (this.fromApiJsonHelper.parameterExists(withdrawalFeeAmountParamName, element)) { if (this.fromApiJsonHelper.parameterExists(withdrawalFeeAmountParamName, element)) {
final BigDecimal withdrawalFeeAmount = fromApiJsonHelper final BigDecimal withdrawalFeeAmount = fromApiJsonHelper
.extractBigDecimalWithLocaleNamed(withdrawalFeeAmountParamName, element); .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)) { if (this.fromApiJsonHelper.parameterExists(withdrawalFeeTypeParamName, element)) {
final Integer withdrawalFeeType = fromApiJsonHelper.extractIntegerSansLocaleNamed(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)) { if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) {
final BigDecimal annualFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(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)) { if (this.fromApiJsonHelper.parameterExists(annualFeeOnMonthDayParamName, element)) {
@@ -16,18 +16,27 @@ public class SavingsAccountStatusEnumData {
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final String value; private final String value;
@SuppressWarnings("unused") @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") @SuppressWarnings("unused")
private final boolean active; private final boolean active;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private final boolean closed; private final boolean closed;
public SavingsAccountStatusEnumData(final Long id, final String code, final String value, final boolean unactivated, public SavingsAccountStatusEnumData(final Long id, final String code, final String value, final boolean submittedAndPendingApproval,
final boolean active, final boolean closed) { final boolean approved, final boolean rejected, final boolean withdrawnByApplicant, final boolean active, final boolean closed) {
this.id = id; this.id = id;
this.code = code; this.code = code;
this.value = value; this.value = value;
this.unactivated = unactivated; this.submittedAndPendingApproval = submittedAndPendingApproval;
this.approved = approved;
this.rejected = rejected;
this.withdrawnByApplicant = withdrawnByApplicant;
this.active = active; this.active = active;
this.closed = closed; this.closed = closed;
} }
@@ -5,15 +5,15 @@
*/ */
package org.mifosplatform.portfolio.savings.data; package org.mifosplatform.portfolio.savings.data;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.accountNumberParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.activationDateParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activationDateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.bankNumberParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.bankNumberParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.checkNumberParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.checkNumberParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.paymentTypeIdParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.paymentTypeIdParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.receiptNumberParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.receiptNumberParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.routingCodeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.routingCodeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.transactionAccountNumberParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.transactionAmountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.transactionAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.transactionDateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.transactionDateParamName;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.math.BigDecimal; 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.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -74,7 +74,7 @@ public class SavingsAccountTransactionDataValidator {
// Validate all string payment detail fields for max length // Validate all string payment detail fields for max length
final Integer paymentTypeId = fromApiJsonHelper.extractIntegerWithLocaleNamed(paymentTypeIdParamName, element); final Integer paymentTypeId = fromApiJsonHelper.extractIntegerWithLocaleNamed(paymentTypeIdParamName, element);
baseDataValidator.reset().parameter(paymentTypeIdParamName).value(paymentTypeId).ignoreIfNull().integerGreaterThanZero(); 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)); routingCodeParamName, receiptNumberParamName, bankNumberParamName));
for (String paymentDetailParameterName : paymentDetailParameters) { for (String paymentDetailParameterName : paymentDetailParameters) {
final String paymentDetailParameterValue = fromApiJsonHelper.extractStringNamed(paymentDetailParameterName, element); final String paymentDetailParameterValue = fromApiJsonHelper.extractStringNamed(paymentDetailParameterName, element);
@@ -5,7 +5,7 @@
*/ */
package org.mifosplatform.portfolio.savings.data; 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 * Immutable data object represent savings account transaction type
@@ -5,24 +5,24 @@
*/ */
package org.mifosplatform.portfolio.savings.data; 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.SavingsApiConstants.SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeAmountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeOnMonthDayParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.currencyCodeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.descriptionParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.digitsAfterDecimalParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationDaysInYearTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCompoundingPeriodTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestPostingPeriodTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.minRequiredOpeningBalanceParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nameParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nameParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nominalAnnualInterestRateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeAmountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeTypeParamName;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.math.BigDecimal; 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.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -90,7 +91,7 @@ public class SavingsProductDataValidator {
final Integer interestCompoundingPeriodType = fromApiJsonHelper.extractIntegerSansLocaleNamed( final Integer interestCompoundingPeriodType = fromApiJsonHelper.extractIntegerSansLocaleNamed(
interestCompoundingPeriodTypeParamName, element); interestCompoundingPeriodTypeParamName, element);
baseDataValidator.reset().parameter(interestCompoundingPeriodTypeParamName).value(interestCompoundingPeriodType).notNull() 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, final Integer interestPostingPeriodType = fromApiJsonHelper.extractIntegerSansLocaleNamed(interestPostingPeriodTypeParamName,
element); element);
@@ -109,14 +110,13 @@ public class SavingsProductDataValidator {
if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) { if (this.fromApiJsonHelper.parameterExists(minRequiredOpeningBalanceParamName, element)) {
final BigDecimal minOpeningBalance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minRequiredOpeningBalanceParamName, final BigDecimal minOpeningBalance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minRequiredOpeningBalanceParamName,
element); element);
baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).positiveAmount(); baseDataValidator.reset().parameter(minRequiredOpeningBalanceParamName).value(minOpeningBalance).zeroOrPositiveAmount();
} }
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) { if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyParamName, element)) {
final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, element); final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).ignoreIfNull() baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).integerZeroOrGreater();
.positiveAmount();
if (lockinPeriodFrequency != null) { if (lockinPeriodFrequency != null) {
final Integer lockinPeriodFrequencyType = fromApiJsonHelper.extractIntegerSansLocaleNamed( final Integer lockinPeriodFrequencyType = fromApiJsonHelper.extractIntegerSansLocaleNamed(
@@ -134,24 +134,39 @@ public class SavingsProductDataValidator {
if (lockinPeriodFrequencyType != null) { if (lockinPeriodFrequencyType != null) {
final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName,
element); 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)) { if (this.fromApiJsonHelper.parameterExists(withdrawalFeeTypeParamName, element)) {
final Integer withdrawalFeeType = fromApiJsonHelper.extractIntegerSansLocaleNamed(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) { if (withdrawalFeeType != null) {
final BigDecimal withdrawalFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(withdrawalFeeAmountParamName, final BigDecimal withdrawalFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(withdrawalFeeAmountParamName,
element); element);
baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(withdrawalFeeAmount).notNull().positiveAmount(); baseDataValidator.reset().parameter(withdrawalFeeAmountParamName).value(withdrawalFeeAmount).notNull()
.zeroOrPositiveAmount();
} }
} }
if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) { if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) {
final BigDecimal annualFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(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) { if (annualFeeAmount != null) {
MonthDay monthDayOfAnnualFee = fromApiJsonHelper.extractMonthDayNamed(annualFeeOnMonthDayParamName, element); 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 // accounting related data validation
final Integer accountingRuleType = fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault()); final Integer accountingRuleType = fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 3); baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 3);
@@ -232,7 +256,7 @@ public class SavingsProductDataValidator {
final Integer interestCompoundingPeriodType = fromApiJsonHelper.extractIntegerSansLocaleNamed( final Integer interestCompoundingPeriodType = fromApiJsonHelper.extractIntegerSansLocaleNamed(
interestCompoundingPeriodTypeParamName, element); interestCompoundingPeriodTypeParamName, element);
baseDataValidator.reset().parameter(interestCompoundingPeriodTypeParamName).value(interestCompoundingPeriodType).notNull() baseDataValidator.reset().parameter(interestCompoundingPeriodTypeParamName).value(interestCompoundingPeriodType).notNull()
.inMinMaxRange(1, 8); .isOneOfTheseValues(SavingsCompoundingInterestPeriodType.integerValues());
} }
if (this.fromApiJsonHelper.parameterExists(interestCalculationTypeParamName, element)) { if (this.fromApiJsonHelper.parameterExists(interestCalculationTypeParamName, element)) {
@@ -249,11 +273,17 @@ public class SavingsProductDataValidator {
.notNull().isOneOfTheseValues(360, 365); .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); final Integer lockinPeriodFrequency = fromApiJsonHelper.extractIntegerWithLocaleNamed(lockinPeriodFrequencyParamName, element);
baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).ignoreIfNull() baseDataValidator.reset().parameter(lockinPeriodFrequencyParamName).value(lockinPeriodFrequency).ignoreIfNull()
.positiveAmount(); .integerZeroOrGreater();
} }
if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyTypeParamName, element)) { if (this.fromApiJsonHelper.parameterExists(lockinPeriodFrequencyTypeParamName, element)) {
@@ -265,7 +295,8 @@ public class SavingsProductDataValidator {
if (this.fromApiJsonHelper.parameterExists(withdrawalFeeAmountParamName, element)) { if (this.fromApiJsonHelper.parameterExists(withdrawalFeeAmountParamName, element)) {
final BigDecimal withdrawalFeeAmount = fromApiJsonHelper final BigDecimal withdrawalFeeAmount = fromApiJsonHelper
.extractBigDecimalWithLocaleNamed(withdrawalFeeAmountParamName, element); .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)) { if (this.fromApiJsonHelper.parameterExists(withdrawalFeeTypeParamName, element)) {
@@ -276,7 +307,7 @@ public class SavingsProductDataValidator {
if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) { if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) {
final BigDecimal annualFeeAmount = fromApiJsonHelper.extractBigDecimalWithLocaleNamed(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)) { if (this.fromApiJsonHelper.parameterExists(annualFeeOnMonthDayParamName, element)) {
@@ -319,9 +350,6 @@ public class SavingsProductDataValidator {
/** /**
* Validation for advanced accounting options * Validation for advanced accounting options
*
* @param baseDataValidator
* @param element
*/ */
private void validatePaymentChannelFundSourceMappings(final DataValidatorBuilder baseDataValidator, final JsonElement element) { private void validatePaymentChannelFundSourceMappings(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
if (fromApiJsonHelper.parameterExists(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), 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; package org.mifosplatform.portfolio.savings.domain;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activationDateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountNoParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeAmountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeOnMonthDayParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.clientIdParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationDaysInYearTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.externalIdParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.fieldOfficerIdParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCompoundingPeriodTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.groupIdParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestPostingPeriodTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.minRequiredOpeningBalanceParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nominalAnnualInterestRateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeAmountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeTypeParamName; 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.math.BigDecimal;
import java.util.List;
import java.util.Locale;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.MonthDay; 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.api.JsonCommand;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper; 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.Client;
import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper;
import org.mifosplatform.portfolio.group.domain.Group; import org.mifosplatform.portfolio.group.domain.Group;
import org.mifosplatform.portfolio.group.domain.GroupRepository; import org.mifosplatform.portfolio.group.domain.GroupRepositoryWrapper;
import org.mifosplatform.portfolio.group.exception.GroupNotFoundException; 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.mifosplatform.portfolio.savings.exception.SavingsProductNotFoundException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -46,18 +55,24 @@ public class SavingsAccountAssembler {
private final SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper; private final SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper;
private final ClientRepositoryWrapper clientRepository; private final ClientRepositoryWrapper clientRepository;
private final GroupRepository groupRepository; private final GroupRepositoryWrapper groupRepository;
private final StaffRepositoryWrapper staffRepository;
private final SavingsProductRepository savingProductRepository; private final SavingsProductRepository savingProductRepository;
private final SavingsAccountRepositoryWrapper savingsAccountRepository;
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
@Autowired @Autowired
public SavingsAccountAssembler(final SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper, public SavingsAccountAssembler(final SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper,
final ClientRepositoryWrapper clientRepository, final GroupRepository groupRepository, final ClientRepositoryWrapper clientRepository, final GroupRepositoryWrapper groupRepository,
final SavingsProductRepository savingProductRepository, final FromJsonHelper fromApiJsonHelper) { final StaffRepositoryWrapper staffRepository, final SavingsProductRepository savingProductRepository,
final SavingsAccountRepositoryWrapper savingsAccountRepository,
final FromJsonHelper fromApiJsonHelper) {
this.savingsAccountTransactionSummaryWrapper = savingsAccountTransactionSummaryWrapper; this.savingsAccountTransactionSummaryWrapper = savingsAccountTransactionSummaryWrapper;
this.clientRepository = clientRepository; this.clientRepository = clientRepository;
this.groupRepository = groupRepository; this.groupRepository = groupRepository;
this.staffRepository = staffRepository;
this.savingProductRepository = savingProductRepository; this.savingProductRepository = savingProductRepository;
this.savingsAccountRepository = savingsAccountRepository;
this.fromApiJsonHelper = fromApiJsonHelper; this.fromApiJsonHelper = fromApiJsonHelper;
} }
@@ -65,36 +80,39 @@ public class SavingsAccountAssembler {
* Assembles a new {@link SavingsAccount} from JSON details passed in * Assembles a new {@link SavingsAccount} from JSON details passed in
* request inheriting details where relevant from chosen * request inheriting details where relevant from chosen
* {@link SavingsProduct}. * {@link SavingsProduct}.
*
* @param existingReversedTransactionIds
* @param existingTransactionIds
*/ */
public SavingsAccount assembleFrom(final JsonCommand command, List<Long> existingTransactionIds, public SavingsAccount assembleFrom(final JsonCommand command) {
List<Long> existingReversedTransactionIds) {
final JsonElement element = command.parsedJson(); final JsonElement element = command.parsedJson();
final String accountNo = fromApiJsonHelper.extractStringNamed("accountNo", element); final String accountNo = fromApiJsonHelper.extractStringNamed(accountNoParamName, element);
final String externalId = fromApiJsonHelper.extractStringNamed("externalId", element); final String externalId = fromApiJsonHelper.extractStringNamed(externalIdParamName, element);
final Long productId = fromApiJsonHelper.extractLongNamed("productId", element); final Long productId = fromApiJsonHelper.extractLongNamed(productIdParamName, element);
final SavingsProduct product = this.savingProductRepository.findOne(productId); final SavingsProduct product = this.savingProductRepository.findOne(productId);
if (product == null) { throw new SavingsProductNotFoundException(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; Client client = null;
Group group = null; Group group = null;
Staff fieldOfficer = null;
final Long clientId = fromApiJsonHelper.extractLongNamed(clientIdParamName, element);
if (clientId != null) { if (clientId != null) {
client = this.clientRepository.findOneWithNotFoundDetection(clientId); client = this.clientRepository.findOneWithNotFoundDetection(clientId);
} }
final Long groupId = fromApiJsonHelper.extractLongNamed(groupIdParamName, element);
if (groupId != null) { if (groupId != null) {
group = this.groupRepository.findOne(groupId); group = this.groupRepository.findOneWithNotFoundDetection(groupId);
if (group == null) { throw new GroupNotFoundException(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; BigDecimal interestRate = null;
if (command.parameterExists(nominalAnnualInterestRateParamName)) { if (command.parameterExists(nominalAnnualInterestRateParamName)) {
interestRate = command.bigDecimalValueOfParameterNamed(nominalAnnualInterestRateParamName); interestRate = command.bigDecimalValueOfParameterNamed(nominalAnnualInterestRateParamName);
@@ -102,9 +120,6 @@ public class SavingsAccountAssembler {
interestRate = product.nominalAnnualInterestRate(); interestRate = product.nominalAnnualInterestRate();
} }
final boolean active = fromApiJsonHelper.extractBooleanNamed(activeParamName, element);
final LocalDate activationDate = fromApiJsonHelper.extractLocalDateNamed(activationDateParamName, element);
SavingsCompoundingInterestPeriodType interestCompoundingPeriodType = null; SavingsCompoundingInterestPeriodType interestCompoundingPeriodType = null;
final Integer interestPeriodTypeValue = command.integerValueOfParameterNamed(interestCompoundingPeriodTypeParamName); final Integer interestPeriodTypeValue = command.integerValueOfParameterNamed(interestCompoundingPeriodTypeParamName);
if (interestPeriodTypeValue != null) { if (interestPeriodTypeValue != null) {
@@ -113,10 +128,10 @@ public class SavingsAccountAssembler {
interestCompoundingPeriodType = product.interestCompoundingPeriodType(); interestCompoundingPeriodType = product.interestCompoundingPeriodType();
} }
SavingsInterestPostingPeriodType interestPostingPeriodType = null; SavingsPostingInterestPeriodType interestPostingPeriodType = null;
final Integer interestPostingPeriodTypeValue = command.integerValueOfParameterNamed(interestPostingPeriodTypeParamName); final Integer interestPostingPeriodTypeValue = command.integerValueOfParameterNamed(interestPostingPeriodTypeParamName);
if (interestPostingPeriodTypeValue != null) { if (interestPostingPeriodTypeValue != null) {
interestPostingPeriodType = SavingsInterestPostingPeriodType.fromInt(interestPostingPeriodTypeValue); interestPostingPeriodType = SavingsPostingInterestPeriodType.fromInt(interestPostingPeriodTypeValue);
} else { } else {
interestPostingPeriodType = product.interestPostingPeriodType(); interestPostingPeriodType = product.interestPostingPeriodType();
} }
@@ -194,17 +209,20 @@ public class SavingsAccountAssembler {
monthDayOfAnnualFee = product.monthDayOfAnnualFee(); monthDayOfAnnualFee = product.monthDayOfAnnualFee();
} }
final SavingsAccount account = SavingsAccount.createNewAccount(client, group, product, accountNo, externalId, interestRate, final SavingsAccount account = SavingsAccount.createNewApplicationForSubmittal(client, group, product, fieldOfficer, accountNo, externalId,
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, submittedOnDate, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
annualFeeAmount, monthDayOfAnnualFee); withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, monthDayOfAnnualFee);
account.setHelpers(this.savingsAccountTransactionSummaryWrapper); account.setHelpers(this.savingsAccountTransactionSummaryWrapper);
if (active) { account.validateNewApplicationState(DateUtils.getLocalDateOfTenant());
final Locale locale = command.extractLocale();
final DateTimeFormatter formatter = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); return account;
account.activate(formatter, activationDate, existingTransactionIds, existingReversedTransactionIds); }
}
public SavingsAccount assembleFrom(final Long savingsId) {
SavingsAccount account = this.savingsAccountRepository.findOneWithNotFoundDetection(savingsId);
account.setHelpers(this.savingsAccountTransactionSummaryWrapper);
return account; 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 { public enum SavingsAccountStatusType {
INVALID(0, "savingsAccountStatusType.invalid"), // 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"), // ACTIVE(300, "savingsAccountStatusType.active"), //
WITHDRAWN_BY_APPLICANT(400, "savingsAccountStatusType.withdrawn.by.applicant"), //
REJECTED(500, "savingsAccountStatusType.rejected"), //
CLOSED(600, "savingsAccountStatusType.closed"); CLOSED(600, "savingsAccountStatusType.closed");
private final Integer value; private final Integer value;
@@ -23,11 +26,20 @@ public enum SavingsAccountStatusType {
SavingsAccountStatusType enumeration = SavingsAccountStatusType.INVALID; SavingsAccountStatusType enumeration = SavingsAccountStatusType.INVALID;
switch (type) { switch (type) {
case 100: case 100:
enumeration = SavingsAccountStatusType.UNACTIVATED; enumeration = SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL;
break;
case 200:
enumeration = SavingsAccountStatusType.APPROVED;
break; break;
case 300: case 300:
enumeration = SavingsAccountStatusType.ACTIVE; enumeration = SavingsAccountStatusType.ACTIVE;
break; break;
case 400:
enumeration = SavingsAccountStatusType.WITHDRAWN_BY_APPLICANT;
break;
case 500:
enumeration = SavingsAccountStatusType.REJECTED;
break;
case 600: case 600:
enumeration = SavingsAccountStatusType.CLOSED; enumeration = SavingsAccountStatusType.CLOSED;
break; break;
@@ -52,8 +64,20 @@ public enum SavingsAccountStatusType {
return code; return code;
} }
public boolean isUnactivated() { public boolean isSubmittedAndPendingApproval() {
return this.value.equals(SavingsAccountStatusType.UNACTIVATED.getValue()); 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() { public boolean isActive() {
@@ -13,6 +13,7 @@ import javax.persistence.Embeddable;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.portfolio.savings.domain.interest.PostingPeriod;
/** /**
* {@link SavingsAccountSummary} encapsulates all the summary details of a * {@link SavingsAccountSummary} encapsulates all the summary details of a
@@ -60,19 +61,15 @@ public final class SavingsAccountSummary {
.minus(this.totalWithdrawalFees).minus(this.totalAnnualFees).getAmount(); .minus(this.totalWithdrawalFees).minus(this.totalAnnualFees).getAmount();
} }
public void updateFromInterestPeriodSummaries(final MonetaryCurrency currency, public void updateFromInterestPeriodSummaries(final MonetaryCurrency currency, final List<PostingPeriod> allPostingPeriods) {
final List<InterestCompoundingPeriodSummary> compoundingPeriods) {
// get the max compounded interest Money totalEarned = Money.zero(currency);
final BigDecimal compoundedInterestToDate = compoundingPeriods.get(compoundingPeriods.size() - 1).compoundedInterest();
final Money maxIntrestCompoundedToDate = Money.of(currency, compoundedInterestToDate);
BigDecimal totalInterestUnrounded = BigDecimal.ZERO; for (PostingPeriod period : allPostingPeriods) {
for (InterestCompoundingPeriodSummary interestPeriodSummary : compoundingPeriods) { totalEarned = totalEarned.plus(period.interest());
totalInterestUnrounded = totalInterestUnrounded.add(interestPeriodSummary.interestUnrounded());
} }
this.totalInterestEarned = maxIntrestCompoundedToDate.getAmountDefaultedToNullIfZero(); this.totalInterestEarned = totalEarned.getAmount();
} }
public boolean isLessThanOrEqualToAccountBalance(final Money amount) { 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.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail; 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.data.SavingsAccountTransactionEnumData;
import org.mifosplatform.portfolio.savings.domain.interest.EndOfDayBalance;
import org.mifosplatform.portfolio.savings.service.SavingsEnumerations; import org.mifosplatform.portfolio.savings.service.SavingsEnumerations;
import org.springframework.data.jpa.domain.AbstractPersistable; 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; 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) { public boolean hasNotAmount(final Money amountToCheck) {
final Money transactionAmount = getAmount(amountToCheck.getCurrency()); final Money transactionAmount = getAmount(amountToCheck.getCurrency());
return transactionAmount.isNotEqualTo(amountToCheck); return transactionAmount.isNotEqualTo(amountToCheck);
@@ -249,4 +230,99 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
return thisTransactionData; 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; package org.mifosplatform.portfolio.savings.domain;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeAmountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeOnMonthDayParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.currencyCodeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.descriptionParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.digitsAfterDecimalParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationDaysInYearTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCompoundingPeriodTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestPostingPeriodTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.localeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.localeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.minRequiredOpeningBalanceParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nameParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nominalAnnualInterestRateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeAmountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nameParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.accountingRuleParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeTypeParamName;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.persistence.Column; import javax.persistence.Column;
@@ -36,9 +39,17 @@ import javax.persistence.Table;
import org.joda.time.MonthDay; import org.joda.time.MonthDay;
import org.mifosplatform.accounting.common.AccountingRuleType; import org.mifosplatform.accounting.common.AccountingRuleType;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.exception.GeneralPlatformDomainRuleException; import org.mifosplatform.infrastructure.core.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.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money; 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; import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity @Entity
@@ -68,7 +79,7 @@ public class SavingsProduct extends AbstractPersistable<Long> {
private Integer interestCompoundingPeriodType; 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) @Column(name = "interest_posting_period_enum", nullable = false)
private Integer interestPostingPeriodType; 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, public static SavingsProduct createNew(final String name, final String description, final MonetaryCurrency currency,
final BigDecimal interestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, final BigDecimal interestRate, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType, final BigDecimal annualFeeAmount, 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, private SavingsProduct(final String name, final String description, final MonetaryCurrency currency, final BigDecimal interestRate,
final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType, final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
final SavingsInterestPostingPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType, final SavingsPostingInterestPeriodType interestPostingPeriodType, final SavingsInterestCalculationType interestCalculationType,
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance, final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType, final BigDecimal annualFeeAmount, final BigDecimal withdrawalFeeAmount, final SavingsWithdrawalFeesType withdrawalFeeType, final BigDecimal annualFeeAmount,
@@ -157,16 +168,16 @@ public class SavingsProduct extends AbstractPersistable<Long> {
} }
this.lockinPeriodFrequency = lockinPeriodFrequency; this.lockinPeriodFrequency = lockinPeriodFrequency;
if (lockinPeriodFrequencyType != null) { if (lockinPeriodFrequency != null && lockinPeriodFrequencyType != null) {
this.lockinPeriodFrequencyType = lockinPeriodFrequencyType.getValue(); this.lockinPeriodFrequencyType = lockinPeriodFrequencyType.getValue();
} }
this.withdrawalFeeAmount = withdrawalFeeAmount; this.withdrawalFeeAmount = withdrawalFeeAmount;
if (withdrawalFeeType != null) { if (withdrawalFeeAmount != null && withdrawalFeeType != null) {
this.withdrawalFeeType = withdrawalFeeType.getValue(); this.withdrawalFeeType = withdrawalFeeType.getValue();
} }
this.annualFeeAmount = annualFeeAmount; this.annualFeeAmount = annualFeeAmount;
if (annualFeeOnMonthDay != null) { if (annualFeeAmount != null && annualFeeOnMonthDay != null) {
this.annualFeeOnMonth = annualFeeOnMonthDay.getMonthOfYear(); this.annualFeeOnMonth = annualFeeOnMonthDay.getMonthOfYear();
this.annualFeeOnDay = annualFeeOnMonthDay.getDayOfMonth(); this.annualFeeOnDay = annualFeeOnMonthDay.getDayOfMonth();
} }
@@ -192,8 +203,8 @@ public class SavingsProduct extends AbstractPersistable<Long> {
return SavingsCompoundingInterestPeriodType.fromInt(this.interestCompoundingPeriodType); return SavingsCompoundingInterestPeriodType.fromInt(this.interestCompoundingPeriodType);
} }
public SavingsInterestPostingPeriodType interestPostingPeriodType() { public SavingsPostingInterestPeriodType interestPostingPeriodType() {
return SavingsInterestPostingPeriodType.fromInt(this.interestPostingPeriodType); return SavingsPostingInterestPeriodType.fromInt(this.interestPostingPeriodType);
} }
public SavingsInterestCalculationType interestCalculationType() { public SavingsInterestCalculationType interestCalculationType() {
@@ -295,7 +306,7 @@ public class SavingsProduct extends AbstractPersistable<Long> {
if (command.isChangeInIntegerParameterNamed(interestPostingPeriodTypeParamName, this.interestPostingPeriodType)) { if (command.isChangeInIntegerParameterNamed(interestPostingPeriodTypeParamName, this.interestPostingPeriodType)) {
final Integer newValue = command.integerValueOfParameterNamed(interestPostingPeriodTypeParamName); final Integer newValue = command.integerValueOfParameterNamed(interestPostingPeriodTypeParamName);
actualChanges.put(interestPostingPeriodTypeParamName, newValue); actualChanges.put(interestPostingPeriodTypeParamName, newValue);
this.interestPostingPeriodType = SavingsInterestPostingPeriodType.fromInt(newValue).getValue(); this.interestPostingPeriodType = SavingsPostingInterestPeriodType.fromInt(newValue).getValue();
} }
if (command.isChangeInIntegerParameterNamed(interestCalculationTypeParamName, this.interestCalculationType)) { if (command.isChangeInIntegerParameterNamed(interestCalculationTypeParamName, this.interestCalculationType)) {
@@ -310,15 +321,16 @@ public class SavingsProduct extends AbstractPersistable<Long> {
this.interestCalculationDaysInYearType = SavingsInterestCalculationDaysInYearType.fromInt(newValue).getValue(); this.interestCalculationDaysInYearType = SavingsInterestCalculationDaysInYearType.fromInt(newValue).getValue();
} }
if (command.isChangeInBigDecimalParameterNamed(minRequiredOpeningBalanceParamName, this.minRequiredOpeningBalance)) { if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(minRequiredOpeningBalanceParamName,
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(minRequiredOpeningBalanceParamName); this.minRequiredOpeningBalance)) {
final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minRequiredOpeningBalanceParamName);
actualChanges.put(minRequiredOpeningBalanceParamName, newValue); actualChanges.put(minRequiredOpeningBalanceParamName, newValue);
actualChanges.put(localeParamName, localeAsInput); actualChanges.put(localeParamName, localeAsInput);
this.minRequiredOpeningBalance = newValue; this.minRequiredOpeningBalance = newValue;
} }
if (command.isChangeInIntegerParameterNamed(lockinPeriodFrequencyParamName, this.lockinPeriodFrequency)) { if (command.isChangeInIntegerParameterNamedDefaultingZeroToNull(lockinPeriodFrequencyParamName, this.lockinPeriodFrequency)) {
final Integer newValue = command.integerValueOfParameterNamed(lockinPeriodFrequencyParamName); final Integer newValue = command.integerValueOfParameterNamedDefaultToNullIfZero(lockinPeriodFrequencyParamName);
actualChanges.put(lockinPeriodFrequencyParamName, newValue); actualChanges.put(lockinPeriodFrequencyParamName, newValue);
actualChanges.put(localeParamName, localeAsInput); actualChanges.put(localeParamName, localeAsInput);
this.lockinPeriodFrequency = newValue; this.lockinPeriodFrequency = newValue;
@@ -327,47 +339,63 @@ public class SavingsProduct extends AbstractPersistable<Long> {
if (command.isChangeInIntegerParameterNamed(lockinPeriodFrequencyTypeParamName, this.lockinPeriodFrequencyType)) { if (command.isChangeInIntegerParameterNamed(lockinPeriodFrequencyTypeParamName, this.lockinPeriodFrequencyType)) {
final Integer newValue = command.integerValueOfParameterNamed(lockinPeriodFrequencyTypeParamName); final Integer newValue = command.integerValueOfParameterNamed(lockinPeriodFrequencyTypeParamName);
actualChanges.put(lockinPeriodFrequencyTypeParamName, newValue); 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)) { // set period type to null if frequency is null
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(withdrawalFeeAmountParamName); 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(withdrawalFeeAmountParamName, newValue);
actualChanges.put(localeParamName, localeAsInput); actualChanges.put(localeParamName, localeAsInput);
this.withdrawalFeeAmount = newValue; this.withdrawalFeeAmount = newValue;
} }
if (command.isChangeInIntegerParameterNamed(withdrawalFeeTypeParamName, this.withdrawalFeeType)) { if (command.isChangeInIntegerParameterNamedDefaultingZeroToNull(withdrawalFeeTypeParamName, this.withdrawalFeeType)) {
final Integer newValue = command.integerValueOfParameterNamed(withdrawalFeeTypeParamName); final Integer newValue = command.integerValueOfParameterNamedDefaultToNullIfZero(withdrawalFeeTypeParamName);
actualChanges.put(withdrawalFeeTypeParamName, newValue); 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)) { // set period type to null if frequency is null
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(annualFeeAmountParamName); 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(annualFeeAmountParamName, newValue);
actualChanges.put(localeParamName, localeAsInput); actualChanges.put(localeParamName, localeAsInput);
this.annualFeeAmount = newValue; this.annualFeeAmount = newValue;
} }
if (command.isChangeInIntegerParameterNamed(annualFeeOnMonthDayParamName, this.annualFeeOnDay)) { if (command.isChangeInIntegerParameterNamedDefaultingZeroToNull(annualFeeOnMonthDayParamName, this.annualFeeOnDay)) {
final MonthDay monthDay = command.extractMonthDayNamed(annualFeeOnMonthDayParamName); final MonthDay monthDay = command.extractMonthDayNamed(annualFeeOnMonthDayParamName);
final String actualValueEntered = command.stringValueOfParameterNamed(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(annualFeeOnMonthDayParamName, actualValueEntered);
actualChanges.put(localeParamName, localeAsInput); actualChanges.put(localeParamName, localeAsInput);
this.annualFeeOnDay = newValue; this.annualFeeOnDay = newValue;
} }
if (command.isChangeInIntegerParameterNamed(annualFeeOnMonthDayParamName, this.annualFeeOnMonth)) { if (command.isChangeInIntegerParameterNamedDefaultingZeroToNull(annualFeeOnMonthDayParamName, this.annualFeeOnMonth)) {
final MonthDay monthDay = command.extractMonthDayNamed(annualFeeOnMonthDayParamName); final MonthDay monthDay = command.extractMonthDayNamed(annualFeeOnMonthDayParamName);
final String actualValueEntered = command.stringValueOfParameterNamed(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(annualFeeOnMonthDayParamName, actualValueEntered);
actualChanges.put(localeParamName, localeAsInput); actualChanges.put(localeParamName, localeAsInput);
this.annualFeeOnMonth = newValue; 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)) { if (command.isChangeInIntegerParameterNamed(accountingRuleParamName, this.accountingRule)) {
final Integer newValue = command.integerValueOfParameterNamed(accountingRuleParamName); final Integer newValue = command.integerValueOfParameterNamed(accountingRuleParamName);
actualChanges.put(accountingRuleParamName, newValue); actualChanges.put(accountingRuleParamName, newValue);
@@ -382,42 +410,72 @@ public class SavingsProduct extends AbstractPersistable<Long> {
} }
private void validateAnnualFeeDetails() { 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() { final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
return (this.annualFeeAmount == null && (this.annualFeeOnDay != null || this.annualFeeOnMonth != null)) final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|| ((this.annualFeeOnDay == null || this.annualFeeOnMonth == null) && this.annualFeeAmount != null); .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() { 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() { final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
return (this.withdrawalFeeAmount == null && this.withdrawalFeeType != null) final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|| (this.withdrawalFeeType == null && this.withdrawalFeeAmount != null); .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() { 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() { final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
return (this.lockinPeriodFrequency == null && this.lockinPeriodFrequencyType != null) final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|| (this.lockinPeriodFrequencyType == null && this.lockinPeriodFrequency != null); .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() { public boolean isCashBasedAccountingEnabled() {
@@ -5,22 +5,22 @@
*/ */
package org.mifosplatform.portfolio.savings.domain; package org.mifosplatform.portfolio.savings.domain;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeAmountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.annualFeeOnMonthDayParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeOnMonthDayParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.currencyCodeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.currencyCodeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.descriptionParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.descriptionParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.digitsAfterDecimalParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationDaysInYearTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationDaysInYearTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCalculationTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCalculationTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestCompoundingPeriodTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestCompoundingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.interestPostingPeriodTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.interestPostingPeriodTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.lockinPeriodFrequencyTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.minRequiredOpeningBalanceParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.minRequiredOpeningBalanceParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nameParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nameParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.nominalAnnualInterestRateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeAmountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeAmountParamName;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.withdrawalFeeTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeTypeParamName;
import java.math.BigDecimal; import java.math.BigDecimal;
@@ -28,6 +28,12 @@ import org.joda.time.MonthDay;
import org.mifosplatform.accounting.common.AccountingRuleType; import org.mifosplatform.accounting.common.AccountingRuleType;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.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; import org.springframework.stereotype.Component;
@Component @Component
@@ -50,10 +56,10 @@ public class SavingsProductAssembler {
interestCompoundingPeriodType = SavingsCompoundingInterestPeriodType.fromInt(interestPeriodTypeValue); interestCompoundingPeriodType = SavingsCompoundingInterestPeriodType.fromInt(interestPeriodTypeValue);
} }
SavingsInterestPostingPeriodType interestPostingPeriodType = null; SavingsPostingInterestPeriodType interestPostingPeriodType = null;
final Integer interestPostingPeriodTypeValue = command.integerValueOfParameterNamed(interestPostingPeriodTypeParamName); final Integer interestPostingPeriodTypeValue = command.integerValueOfParameterNamed(interestPostingPeriodTypeParamName);
if (interestPostingPeriodTypeValue != null) { if (interestPostingPeriodTypeValue != null) {
interestPostingPeriodType = SavingsInterestPostingPeriodType.fromInt(interestPostingPeriodTypeValue); interestPostingPeriodType = SavingsPostingInterestPeriodType.fromInt(interestPostingPeriodTypeValue);
} }
SavingsInterestCalculationType interestCalculationType = null; SavingsInterestCalculationType interestCalculationType = null;
@@ -69,23 +75,24 @@ public class SavingsProductAssembler {
interestCalculationDaysInYearType = SavingsInterestCalculationDaysInYearType.fromInt(interestCalculationDaysInYearTypeValue); 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; SavingsPeriodFrequencyType lockinPeriodFrequencyType = null;
final Integer lockinPeriodFrequencyTypeValue = command.integerValueOfParameterNamed(lockinPeriodFrequencyTypeParamName); final Integer lockinPeriodFrequencyTypeValue = command.integerValueOfParameterNamed(lockinPeriodFrequencyTypeParamName);
if (lockinPeriodFrequencyTypeValue != null) { if (lockinPeriodFrequencyTypeValue != null) {
lockinPeriodFrequencyType = SavingsPeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue); lockinPeriodFrequencyType = SavingsPeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue);
} }
final BigDecimal withdrawalFeeAmount = command.bigDecimalValueOfParameterNamed(withdrawalFeeAmountParamName); final BigDecimal withdrawalFeeAmount = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(withdrawalFeeAmountParamName);
SavingsWithdrawalFeesType withdrawalFeeType = null; SavingsWithdrawalFeesType withdrawalFeeType = null;
final Integer withdrawalFeeTypeValue = command.integerValueOfParameterNamed(withdrawalFeeTypeParamName); final Integer withdrawalFeeTypeValue = command.integerValueOfParameterNamed(withdrawalFeeTypeParamName);
if (withdrawalFeeTypeValue != null) { if (withdrawalFeeTypeValue != null) {
withdrawalFeeType = SavingsWithdrawalFeesType.fromInt(withdrawalFeeTypeValue); withdrawalFeeType = SavingsWithdrawalFeesType.fromInt(withdrawalFeeTypeValue);
} }
final BigDecimal annualFeeAmount = command.bigDecimalValueOfParameterNamed(annualFeeAmountParamName); final BigDecimal annualFeeAmount = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(annualFeeAmountParamName);
final MonthDay monthDayOfAnnualFee = command.extractMonthDayNamed(annualFeeOnMonthDayParamName); final MonthDay monthDayOfAnnualFee = command.extractMonthDayNamed(annualFeeOnMonthDayParamName);
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(command.integerValueOfParameterNamed("accountingRule")); 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 class InsufficientAccountBalanceException extends AbstractPlatformDomainRuleException {
public InsufficientAccountBalanceException(final String paramName, final BigDecimal accountBalance, final BigDecimal withdrawalFee, final BigDecimal transactionAmount) { public InsufficientAccountBalanceException(final String paramName, final BigDecimal accountBalance, final BigDecimal withdrawalFee,
super(withdrawalFee != null? "error.msg.savingsaccount.transaction.insufficient.account.balance.withdraw" : "error.msg.savingsaccount.transaction.insufficient.account.balance", final BigDecimal transactionAmount) {
"Insufficient account balance.", paramName, accountBalance, withdrawalFee, 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 @Service
public class ActivateSavingsAccountCommandHandler implements NewCommandSourceHandler { public class ActivateSavingsAccountCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService savingAccountWritePlatformService; private final SavingsAccountWritePlatformService writePlatformService;
@Autowired @Autowired
public ActivateSavingsAccountCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) { public ActivateSavingsAccountCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) {
this.savingAccountWritePlatformService = savingAccountWritePlatformService; this.writePlatformService = savingAccountWritePlatformService;
} }
@Transactional @Transactional
@Override @Override
public CommandProcessingResult processCommand(final JsonCommand command) { 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 @Service
public class CalculateInterestSavingsAccountCommandHandler implements NewCommandSourceHandler { public class CalculateInterestSavingsAccountCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService savingAccountWritePlatformService; private final SavingsAccountWritePlatformService writePlatformService;
@Autowired @Autowired
public CalculateInterestSavingsAccountCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) { public CalculateInterestSavingsAccountCommandHandler(
this.savingAccountWritePlatformService = savingAccountWritePlatformService; final SavingsAccountWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
} }
@Transactional @Transactional
@Override @Override
public CommandProcessingResult processCommand(final JsonCommand command) { 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 @Service
public class DepositSavingsAccountCommandHandler implements NewCommandSourceHandler { public class DepositSavingsAccountCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService savingAccountWritePlatformService; private final SavingsAccountWritePlatformService writePlatformService;
@Autowired @Autowired
public DepositSavingsAccountCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) { public DepositSavingsAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
this.savingAccountWritePlatformService = savingAccountWritePlatformService; this.writePlatformService = writePlatformService;
} }
@Transactional @Transactional
@Override @Override
public CommandProcessingResult processCommand(final JsonCommand command) { 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 @Service
public class PostInterestSavingsAccountCommandHandler implements NewCommandSourceHandler { public class PostInterestSavingsAccountCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService savingAccountWritePlatformService; private final SavingsAccountWritePlatformService writePlatformService;
@Autowired @Autowired
public PostInterestSavingsAccountCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) { public PostInterestSavingsAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
this.savingAccountWritePlatformService = savingAccountWritePlatformService; this.writePlatformService = writePlatformService;
} }
@Transactional @Transactional
@Override @Override
public CommandProcessingResult processCommand(final JsonCommand command) { 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.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@Service @Service
public class DeleteSavingsAccountCommandHandler implements NewCommandSourceHandler { public class SavingsAccountApplicationDeletionCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService savingAccountWritePlatformService; private final SavingsApplicationProcessWritePlatformService savingAccountWritePlatformService;
@Autowired @Autowired
public DeleteSavingsAccountCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) { public SavingsAccountApplicationDeletionCommandHandler(final SavingsApplicationProcessWritePlatformService savingAccountWritePlatformService) {
this.savingAccountWritePlatformService = savingAccountWritePlatformService; this.savingAccountWritePlatformService = savingAccountWritePlatformService;
} }
@Transactional @Transactional
@Override @Override
public CommandProcessingResult processCommand(final JsonCommand command) { 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.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@Service @Service
public class UpdateSavingsAccountCommandHandler implements NewCommandSourceHandler { public class SavingsAccountApplicationModificationCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService savingAccountWritePlatformService; private final SavingsApplicationProcessWritePlatformService savingAccountWritePlatformService;
@Autowired @Autowired
public UpdateSavingsAccountCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) { public SavingsAccountApplicationModificationCommandHandler(final SavingsApplicationProcessWritePlatformService savingAccountWritePlatformService) {
this.savingAccountWritePlatformService = savingAccountWritePlatformService; this.savingAccountWritePlatformService = savingAccountWritePlatformService;
} }
@Transactional @Transactional
@Override @Override
public CommandProcessingResult processCommand(final JsonCommand command) { 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.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@Service @Service
public class CreateSavingsAccountCommandHandler implements NewCommandSourceHandler { public class SavingsAccountApplicationSubmittalCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService savingAccountWritePlatformService; private final SavingsApplicationProcessWritePlatformService savingAccountWritePlatformService;
@Autowired @Autowired
public CreateSavingsAccountCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) { public SavingsAccountApplicationSubmittalCommandHandler(final SavingsApplicationProcessWritePlatformService savingAccountWritePlatformService) {
this.savingAccountWritePlatformService = savingAccountWritePlatformService; this.savingAccountWritePlatformService = savingAccountWritePlatformService;
} }
@Transactional @Transactional
@Override @Override
public CommandProcessingResult processCommand(final JsonCommand command) { 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 @Service
public class WithdrawSavingsAccountCommandHandler implements NewCommandSourceHandler { public class WithdrawSavingsAccountCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService savingAccountWritePlatformService; private final SavingsAccountWritePlatformService writePlatformService;
@Autowired @Autowired
public WithdrawSavingsAccountCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) { public WithdrawSavingsAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
this.savingAccountWritePlatformService = savingAccountWritePlatformService; this.writePlatformService = writePlatformService;
} }
@Transactional @Transactional
@Override @Override
public CommandProcessingResult processCommand(final JsonCommand command) { 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 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); SavingsAccountTransactionData retrieveDepositTransactionTemplate(Long savingsId);
@@ -8,6 +8,7 @@ package org.mifosplatform.portfolio.savings.service;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; 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.core.service.RoutingDataSource;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.monetary.data.CurrencyData; 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.data.ClientData;
import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; import org.mifosplatform.portfolio.client.service.ClientReadPlatformService;
import org.mifosplatform.portfolio.group.data.GroupGeneralData; import org.mifosplatform.portfolio.group.data.GroupGeneralData;
import org.mifosplatform.portfolio.group.service.GroupReadPlatformService; import org.mifosplatform.portfolio.group.service.GroupReadPlatformService;
import org.mifosplatform.portfolio.group.service.SearchParameters; import org.mifosplatform.portfolio.group.service.SearchParameters;
import org.mifosplatform.portfolio.paymentdetail.data.PaymentDetailData; 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.SavingsAccountData;
import org.mifosplatform.portfolio.savings.data.SavingsAccountStatusEnumData; import org.mifosplatform.portfolio.savings.data.SavingsAccountStatusEnumData;
import org.mifosplatform.portfolio.savings.data.SavingsAccountSummaryData; import org.mifosplatform.portfolio.savings.data.SavingsAccountSummaryData;
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionData; import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionData;
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionEnumData; import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionEnumData;
import org.mifosplatform.portfolio.savings.data.SavingsProductData; 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.portfolio.savings.exception.SavingsAccountNotFoundException;
import org.mifosplatform.useradministration.domain.AppUser; import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired; 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.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
@Service @Service
public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountReadPlatformService { public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountReadPlatformService {
@@ -56,6 +61,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
private final ClientReadPlatformService clientReadPlatformService; private final ClientReadPlatformService clientReadPlatformService;
private final GroupReadPlatformService groupReadPlatformService; private final GroupReadPlatformService groupReadPlatformService;
private final SavingsProductReadPlatformService savingsProductReadPlatformService; private final SavingsProductReadPlatformService savingsProductReadPlatformService;
private final StaffReadPlatformService staffReadPlatformService;
private final SavingsDropdownReadPlatformService dropdownReadPlatformService; private final SavingsDropdownReadPlatformService dropdownReadPlatformService;
private final SavingsAccountTransactionTemplateMapper transactionTemplateMapper; private final SavingsAccountTransactionTemplateMapper transactionTemplateMapper;
private final SavingsAccountTransactionsMapper transactionsMapper; private final SavingsAccountTransactionsMapper transactionsMapper;
@@ -67,15 +73,16 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
public SavingsAccountReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource, public SavingsAccountReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource,
final ClientReadPlatformService clientReadPlatformService, final GroupReadPlatformService groupReadPlatformService, final ClientReadPlatformService clientReadPlatformService, final GroupReadPlatformService groupReadPlatformService,
final SavingsProductReadPlatformService savingProductReadPlatformService, final SavingsProductReadPlatformService savingProductReadPlatformService,
final SavingsDropdownReadPlatformService dropdownReadPlatformService) { final StaffReadPlatformService staffReadPlatformService, final SavingsDropdownReadPlatformService dropdownReadPlatformService) {
this.context = context; this.context = context;
this.jdbcTemplate = new JdbcTemplate(dataSource); this.jdbcTemplate = new JdbcTemplate(dataSource);
this.clientReadPlatformService = clientReadPlatformService; this.clientReadPlatformService = clientReadPlatformService;
this.groupReadPlatformService = groupReadPlatformService; this.groupReadPlatformService = groupReadPlatformService;
this.savingsProductReadPlatformService = savingProductReadPlatformService; this.savingsProductReadPlatformService = savingProductReadPlatformService;
this.staffReadPlatformService = staffReadPlatformService;
this.dropdownReadPlatformService = dropdownReadPlatformService; this.dropdownReadPlatformService = dropdownReadPlatformService;
transactionTemplateMapper = new SavingsAccountTransactionTemplateMapper(); this.transactionTemplateMapper = new SavingsAccountTransactionTemplateMapper();
transactionsMapper = new SavingsAccountTransactionsMapper(); this.transactionsMapper = new SavingsAccountTransactionsMapper();
} }
@Override @Override
@@ -151,13 +158,40 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
public SavingAccountMapper() { public SavingAccountMapper() {
final StringBuilder sqlBuilder = new StringBuilder(400); 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.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("c.id as clientId, c.display_name as clientName, ");
sqlBuilder.append("g.id as groupId, g.display_name as groupName, "); sqlBuilder.append("g.id as groupId, g.display_name as groupName, ");
sqlBuilder.append("sp.id as productId, sp.name as productName, "); 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("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.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, ");
sqlBuilder.append("sa.nominal_annual_interest_rate as nominalAnnualInterestRate, "); sqlBuilder.append("sa.nominal_annual_interest_rate as nominalAnnualInterestRate, ");
sqlBuilder.append("sa.interest_compounding_period_enum as interestCompoundingPeriodType, "); sqlBuilder.append("sa.interest_compounding_period_enum as interestCompoundingPeriodType, ");
sqlBuilder.append("sa.interest_posting_period_enum as interestPostingPeriodType, "); 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.total_interest_posted_derived as totalInterestPosted, ");
sqlBuilder.append("sa.account_balance_derived as accountBalance "); sqlBuilder.append("sa.account_balance_derived as accountBalance ");
sqlBuilder.append("from m_savings_account sa "); 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_savings_product sp ON sa.product_id = sp.id ");
sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code "); 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(); this.schemaSql = sqlBuilder.toString();
} }
@@ -199,11 +240,6 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final String accountNo = rs.getString("accountNo"); final String accountNo = rs.getString("accountNo");
final String externalId = rs.getString("externalId"); 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 Long groupId = JdbcSupport.getLong(rs, "groupId");
final String groupName = rs.getString("groupName"); final String groupName = rs.getString("groupName");
final Long clientId = JdbcSupport.getLong(rs, "clientId"); final Long clientId = JdbcSupport.getLong(rs, "clientId");
@@ -212,6 +248,49 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final Long productId = rs.getLong("productId"); final Long productId = rs.getLong("productId");
final String productName = rs.getString("productName"); 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 currencyCode = rs.getString("currencyCode");
final String currencyName = rs.getString("currencyName"); final String currencyName = rs.getString("currencyName");
final String currencyNameCode = rs.getString("currencyNameCode"); final String currencyNameCode = rs.getString("currencyNameCode");
@@ -226,7 +305,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs, .compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs,
"interestCompoundingPeriodType"))); "interestCompoundingPeriodType")));
final EnumOptionData interestPostingPeriodType = SavingsEnumerations.interestPostingPeriodType(SavingsInterestPostingPeriodType final EnumOptionData interestPostingPeriodType = SavingsEnumerations.interestPostingPeriodType(SavingsPostingInterestPeriodType
.fromInt(JdbcSupport.getInteger(rs, "interestPostingPeriodType"))); .fromInt(JdbcSupport.getInteger(rs, "interestPostingPeriodType")));
final EnumOptionData interestCalculationType = SavingsEnumerations.interestCalculationType(SavingsInterestCalculationType final EnumOptionData interestCalculationType = SavingsEnumerations.interestCalculationType(SavingsInterestCalculationType
@@ -277,27 +356,31 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final SavingsAccountSummaryData summary = new SavingsAccountSummaryData(currency, totalDeposits, totalWithdrawals, final SavingsAccountSummaryData summary = new SavingsAccountSummaryData(currency, totalDeposits, totalWithdrawals,
totalWithdrawalFees, totalAnnualFees, totalInterestEarned, totalInterestPosted, accountBalance); totalWithdrawalFees, totalAnnualFees, totalInterestEarned, totalInterestPosted, accountBalance);
return SavingsAccountData.instance(id, accountNo, externalId, status, activationDate, groupId, groupName, clientId, clientName, return SavingsAccountData.instance(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
productId, productName, currency, nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, annualFeeOnMonthDay, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount,
annualFeeNextDueDate, summary); annualFeeOnMonthDay, annualFeeNextDueDate, summary);
} }
} }
@Override @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; ClientData client = null;
if (clientId != null) { if (clientId != null) {
client = this.clientReadPlatformService.retrieveOne(clientId); client = this.clientReadPlatformService.retrieveOne(clientId);
officeId = client.officeId();
} }
GroupGeneralData group = null; GroupGeneralData group = null;
if (groupId != null) { if (groupId != null) {
group = this.groupReadPlatformService.retrieveOne(groupId); group = this.groupReadPlatformService.retrieveOne(groupId);
officeId = group.officeId();
} }
final Collection<SavingsProductData> productOptions = this.savingsProductReadPlatformService.retrieveAllForLookup(); final Collection<SavingsProductData> productOptions = this.savingsProductReadPlatformService.retrieveAllForLookup();
@@ -328,9 +411,35 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final Collection<SavingsAccountTransactionData> transactions = null; final Collection<SavingsAccountTransactionData> transactions = null;
template = SavingsAccountData.withTemplateOptions(template, productOptions, interestCompoundingPeriodTypeOptions, Collection<StaffData> fieldOfficerOptions = null;
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions); 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 { } else {
String clientName = null; String clientName = null;
@@ -345,6 +454,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
template = SavingsAccountData.withClientTemplate(clientId, clientName, groupId, groupName); template = SavingsAccountData.withClientTemplate(clientId, clientName, groupId, groupName);
final Collection<StaffData> fieldOfficerOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null; final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null; final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null; final Collection<EnumOptionData> interestCalculationTypeOptions = null;
@@ -354,9 +464,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
final Collection<SavingsAccountTransactionData> transactions = null; final Collection<SavingsAccountTransactionData> transactions = null;
template = SavingsAccountData.withTemplateOptions(template, productOptions, interestCompoundingPeriodTypeOptions, template = SavingsAccountData.withTemplateOptions(template, productOptions, fieldOfficerOptions,
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions); interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions);
} }
return template; return template;
@@ -553,7 +663,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs, .compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs,
"interestCompoundingPeriodType"))); "interestCompoundingPeriodType")));
final EnumOptionData interestPostingPeriodType = SavingsEnumerations.interestPostingPeriodType(SavingsInterestPostingPeriodType final EnumOptionData interestPostingPeriodType = SavingsEnumerations.interestPostingPeriodType(SavingsPostingInterestPeriodType
.fromInt(JdbcSupport.getInteger(rs, "interestPostingPeriodType"))); .fromInt(JdbcSupport.getInteger(rs, "interestPostingPeriodType")));
EnumOptionData interestCalculationType = SavingsEnumerations.interestCalculationType(SavingsInterestCalculationType EnumOptionData interestCalculationType = SavingsEnumerations.interestCalculationType(SavingsInterestCalculationType
@@ -604,16 +714,19 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
groupName = group.getName(); groupName = group.getName();
} }
final Long fieldOfficerId = null;
final String fieldOfficerName = null;
final SavingsAccountStatusEnumData status = null; final SavingsAccountStatusEnumData status = null;
final LocalDate activationDate = null;
final LocalDate annualFeeNextDueDate = null; final LocalDate annualFeeNextDueDate = null;
final SavingsAccountSummaryData summary = null; final SavingsAccountSummaryData summary = null;
return SavingsAccountData.instance(null, null, null, status, activationDate, groupId, groupName, clientId, clientName, final SavingsAccountApplicationTimelineData timeline = SavingsAccountApplicationTimelineData.templateDefault();
productId, productName, currency, nominalAnnualIterestRate, interestCompoundingPeriodType, interestPostingPeriodType,
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, return SavingsAccountData.instance(null, null, null, groupId, groupName, clientId, clientName, productId, productName,
lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount, annualFeeOnMonthDay, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualIterestRate, interestCompoundingPeriodType,
annualFeeNextDueDate, summary); interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeAmount, withdrawalFeeType, annualFeeAmount,
annualFeeOnMonthDay, annualFeeNextDueDate, summary);
} }
} }
} }
@@ -10,12 +10,6 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
public interface SavingsAccountWritePlatformService { public interface SavingsAccountWritePlatformService {
CommandProcessingResult createSavingAccount(JsonCommand command);
CommandProcessingResult updateSavingAccount(Long accountId, JsonCommand command);
CommandProcessingResult deleteSavingAccount(Long accountId);
CommandProcessingResult activate(Long savingsId, JsonCommand command); CommandProcessingResult activate(Long savingsId, JsonCommand command);
CommandProcessingResult deposit(Long savingsId, JsonCommand command); CommandProcessingResult deposit(Long savingsId, JsonCommand command);
@@ -24,7 +18,7 @@ public interface SavingsAccountWritePlatformService {
CommandProcessingResult applyAnnualFee(Long savingsId); 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; package org.mifosplatform.portfolio.savings.service;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; 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.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; 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.core.service.DateUtils;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; 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.domain.PaymentDetail;
import org.mifosplatform.portfolio.paymentdetail.service.PaymentDetailWritePlatformService; 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.data.SavingsAccountTransactionDataValidator;
import org.mifosplatform.portfolio.savings.domain.SavingsAccount; import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler; import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepositoryWrapper; import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepositoryWrapper;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction; import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransactionRepository; import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransactionRepository;
import org.mifosplatform.portfolio.savings.domain.SavingsProduct; import org.mifosplatform.portfolio.savings.domain.SavingsHelper;
import org.mifosplatform.portfolio.savings.domain.SavingsProductRepository; import org.mifosplatform.useradministration.domain.AppUser;
import org.mifosplatform.portfolio.savings.exception.SavingsProductNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@Service @Service
public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements SavingsAccountWritePlatformService { public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements SavingsAccountWritePlatformService {
private final static Logger logger = LoggerFactory.getLogger(SavingsAccountWritePlatformServiceJpaRepositoryImpl.class);
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final SavingsAccountRepositoryWrapper savingAccountRepository; private final SavingsAccountRepositoryWrapper savingAccountRepository;
private final SavingsAccountTransactionRepository savingsAccountTransactionRepository; private final SavingsAccountTransactionRepository savingsAccountTransactionRepository;
private final SavingsAccountAssembler savingAccountAssembler; private final SavingsAccountAssembler savingAccountAssembler;
private final SavingsAccountDataValidator savingsAccountDataValidator;
private final SavingsAccountTransactionDataValidator savingsAccountTransactionDataValidator; 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 PaymentDetailWritePlatformService paymentDetailWritePlatformService;
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper; private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper;
private final JournalEntryWritePlatformService journalEntryWritePlatformService; private final JournalEntryWritePlatformService journalEntryWritePlatformService;
private final SavingsHelper savingsHelper = new SavingsHelper();
@Autowired @Autowired
public SavingsAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, public SavingsAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
final SavingsAccountRepositoryWrapper savingAccountRepository, final SavingsAccountRepositoryWrapper savingAccountRepository,
final SavingsAccountTransactionRepository savingsAccountTransactionRepository, final SavingsAccountTransactionRepository savingsAccountTransactionRepository,
final SavingsAccountAssembler savingAccountAssembler, final SavingsAccountDataValidator savingsAccountDataValidator, final SavingsAccountAssembler savingAccountAssembler,
final SavingsAccountTransactionDataValidator savingsAccountTransactionDataValidator, final SavingsAccountTransactionDataValidator savingsAccountTransactionDataValidator,
final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory, final ClientRepositoryWrapper clientRepository,
final GroupRepository groupRepository, final SavingsProductRepository savingsProductRepository,
final PaymentDetailWritePlatformService paymentDetailWritePlatformService, final PaymentDetailWritePlatformService paymentDetailWritePlatformService,
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper, final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper,
final JournalEntryWritePlatformService journalEntryWritePlatformService) { final JournalEntryWritePlatformService journalEntryWritePlatformService) {
@@ -87,184 +68,29 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
this.savingsAccountTransactionRepository = savingsAccountTransactionRepository; this.savingsAccountTransactionRepository = savingsAccountTransactionRepository;
this.savingAccountAssembler = savingAccountAssembler; this.savingAccountAssembler = savingAccountAssembler;
this.savingsAccountTransactionDataValidator = savingsAccountTransactionDataValidator; this.savingsAccountTransactionDataValidator = savingsAccountTransactionDataValidator;
this.accountIdentifierGeneratorFactory = accountIdentifierGeneratorFactory;
this.savingsAccountDataValidator = savingsAccountDataValidator;
this.clientRepository = clientRepository;
this.groupRepository = groupRepository;
this.savingsProductRepository = savingsProductRepository;
this.paymentDetailWritePlatformService = paymentDetailWritePlatformService; this.paymentDetailWritePlatformService = paymentDetailWritePlatformService;
this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper; this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper;
this.journalEntryWritePlatformService = journalEntryWritePlatformService; 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 @Transactional
@Override @Override
public CommandProcessingResult activate(final Long savingsId, final JsonCommand command) { public CommandProcessingResult activate(final Long savingsId, final JsonCommand command) {
this.context.authenticatedUser(); final AppUser user = this.context.authenticatedUser();
this.savingsAccountTransactionDataValidator.validateActivation(command); 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> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>(); final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
account.activate(fmt, activationDate, existingReversedTransactionIds, existingReversedTransactionIds); final Map<String, Object> changes = account.activate(user, command, DateUtils.getLocalDateOfTenant(),
existingReversedTransactionIds, existingReversedTransactionIds);
this.savingAccountRepository.save(account); if (!changes.isEmpty()) {
this.savingAccountRepository.save(account);
}
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);
@@ -274,6 +100,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
.withClientId(account.clientId()) // .withClientId(account.clientId()) //
.withGroupId(account.groupId()) // .withGroupId(account.groupId()) //
.withSavingsId(savingsId) // .withSavingsId(savingsId) //
.with(changes) //
.build(); .build();
} }
@@ -285,7 +112,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
this.savingsAccountTransactionDataValidator.validate(command); this.savingsAccountTransactionDataValidator.validate(command);
final SavingsAccount account = this.savingAccountRepository.findOneWithNotFoundDetection(savingsId); final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
final Locale locale = command.extractLocale(); final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); 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>(); final Map<String, Object> changes = new LinkedHashMap<String, Object>();
PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN);
final SavingsAccountTransaction deposit = account.deposit(fmt, transactionDate, transactionAmount, existingTransactionIds, final SavingsAccountTransaction deposit = account.deposit(fmt, transactionDate, transactionAmount, existingTransactionIds,
existingReversedTransactionIds, paymentDetail); 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); final Long transactionId = saveTransactionToGenerateTransactionId(deposit);
this.savingAccountRepository.save(account); this.savingAccountRepository.save(account);
@@ -329,7 +166,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
this.savingsAccountTransactionDataValidator.validate(command); 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 LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate");
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount");
@@ -342,8 +179,18 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final Map<String, Object> changes = new LinkedHashMap<String, Object>(); final Map<String, Object> changes = new LinkedHashMap<String, Object>();
PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN);
final SavingsAccountTransaction withdrawal = account.withdraw(fmt, transactionDate, transactionAmount, existingTransactionIds, final SavingsAccountTransaction withdrawal = account.withdraw(fmt, transactionDate, transactionAmount, existingTransactionIds,
existingReversedTransactionIds, paymentDetail); 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); final Long transactionId = saveTransactionToGenerateTransactionId(withdrawal);
this.savingAccountRepository.save(account); this.savingAccountRepository.save(account);
@@ -363,7 +210,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Override @Override
public CommandProcessingResult applyAnnualFee(final Long savingsId) { 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(); final LocalDate transactionDate = LocalDate.now();
@@ -372,8 +219,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final List<Long> existingTransactionIds = new ArrayList<Long>(); final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>(); final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final SavingsAccountTransaction annualFee = account.addAnnualFee(fmt, transactionDate, existingTransactionIds, final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN);
existingReversedTransactionIds); final SavingsAccountTransaction annualFee = account.addAnnualFee(this.savingsHelper, mc, fmt, transactionDate,
existingTransactionIds, existingReversedTransactionIds);
final Long transactionId = saveTransactionToGenerateTransactionId(annualFee); final Long transactionId = saveTransactionToGenerateTransactionId(annualFee);
this.savingAccountRepository.save(account); this.savingAccountRepository.save(account);
@@ -388,14 +236,19 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
.build(); .build();
} }
@Transactional
@Override @Override
public CommandProcessingResult calculateInterest(final Long savingsId, final JsonCommand command) { public CommandProcessingResult calculateInterest(final Long savingsId) {
this.context.authenticatedUser(); this.context.authenticatedUser();
final SavingsAccount account = this.savingAccountRepository.findOneWithNotFoundDetection(savingsId); final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
final LocalDate today = DateUtils.getLocalDateOfTenant(); 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); this.savingAccountRepository.save(account);
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
@@ -409,15 +262,17 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Transactional @Transactional
@Override @Override
public CommandProcessingResult postInterest(final Long savingsId, final JsonCommand command) { public CommandProcessingResult postInterest(final Long savingsId) {
this.context.authenticatedUser(); this.context.authenticatedUser();
final List<Long> existingTransactionIds = new ArrayList<Long>(); final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = 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(); 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); this.savingAccountRepository.save(account);
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); 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 java.util.List;
import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.portfolio.savings.domain.SavingsCompoundingInterestPeriodType; import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationDaysInYearType; import org.mifosplatform.portfolio.savings.SavingsInterestCalculationDaysInYearType;
import org.mifosplatform.portfolio.savings.domain.SavingsInterestCalculationType; import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType;
import org.mifosplatform.portfolio.savings.domain.SavingsInterestPostingPeriodType; import org.mifosplatform.portfolio.savings.SavingsPeriodFrequencyType;
import org.mifosplatform.portfolio.savings.domain.SavingsPeriodFrequencyType; import org.mifosplatform.portfolio.savings.SavingsPostingInterestPeriodType;
import org.mifosplatform.portfolio.savings.domain.SavingsWithdrawalFeesType; import org.mifosplatform.portfolio.savings.SavingsWithdrawalFeesType;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service @Service
@@ -47,13 +47,13 @@ public class SavingsDropdownReadPlatformServiceImpl implements SavingsDropdownRe
public Collection<EnumOptionData> retrieveCompoundingInterestPeriodTypeOptions() { public Collection<EnumOptionData> retrieveCompoundingInterestPeriodTypeOptions() {
List<EnumOptionData> allowedOptions = Arrays.asList( // List<EnumOptionData> allowedOptions = Arrays.asList( //
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.DAILY), // SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.DAILY), //
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.WEEKLY), // // SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.WEEKLY), //
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.BIWEEKLY), // // SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.BIWEEKLY), //
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.MONTHLY), // SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.MONTHLY)
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.QUATERLY), // // SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.QUATERLY), //
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.BI_ANNUAL), // // SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.BI_ANNUAL), //
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.ANNUAL), // // SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.ANNUAL), //
SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.NO_COMPOUNDING_SIMPLE_INTEREST) // // SavingsEnumerations.compoundingInterestPeriodType(SavingsCompoundingInterestPeriodType.NO_COMPOUNDING_SIMPLE_INTEREST) //
); );
return allowedOptions; return allowedOptions;
@@ -62,10 +62,9 @@ public class SavingsDropdownReadPlatformServiceImpl implements SavingsDropdownRe
@Override @Override
public Collection<EnumOptionData> retrieveInterestPostingPeriodTypeOptions() { public Collection<EnumOptionData> retrieveInterestPostingPeriodTypeOptions() {
List<EnumOptionData> allowedOptions = Arrays.asList( // List<EnumOptionData> allowedOptions = Arrays.asList( //
SavingsEnumerations.interestPostingPeriodType(SavingsInterestPostingPeriodType.MONTHLY), // SavingsEnumerations.interestPostingPeriodType(SavingsPostingInterestPeriodType.MONTHLY), //
SavingsEnumerations.interestPostingPeriodType(SavingsInterestPostingPeriodType.QUATERLY), // SavingsEnumerations.interestPostingPeriodType(SavingsPostingInterestPeriodType.QUATERLY), //
SavingsEnumerations.interestPostingPeriodType(SavingsInterestPostingPeriodType.BI_ANNUAL), // SavingsEnumerations.interestPostingPeriodType(SavingsPostingInterestPeriodType.ANNUAL) //
SavingsEnumerations.interestPostingPeriodType(SavingsInterestPostingPeriodType.ANNUAL) //
); );
return allowedOptions; return allowedOptions;
@@ -6,16 +6,16 @@
package org.mifosplatform.portfolio.savings.service; package org.mifosplatform.portfolio.savings.service;
import org.mifosplatform.infrastructure.core.data.EnumOptionData; 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.SavingsAccountStatusEnumData;
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionEnumData; import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionEnumData;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountStatusType; 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 { public class SavingsEnumerations {
@@ -94,59 +94,81 @@ public class SavingsEnumerations {
public static SavingsAccountStatusEnumData status(final SavingsAccountStatusType type) { 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(), 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) { switch (type) {
case INVALID: case INVALID:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.INVALID.getValue().longValue(), 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; break;
case UNACTIVATED: case SUBMITTED_AND_PENDING_APPROVAL:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.UNACTIVATED.getValue().longValue(), optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.getValue()
SavingsAccountStatusType.UNACTIVATED.getCode(), "Unactivated", type.isUnactivated(), type.isActive(), .longValue(), SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.getCode(), "Submitted and pending approval",
type.isClosed()); 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; break;
case ACTIVE: case ACTIVE:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.ACTIVE.getValue().longValue(), 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; break;
case CLOSED: case CLOSED:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.CLOSED.getValue().longValue(), 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; break;
} }
return optionData; return optionData;
} }
public static EnumOptionData interestPostingPeriodType(final Integer type) { 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."; final String codePrefix = "savings.interest.posting.period.";
EnumOptionData optionData = new EnumOptionData(SavingsInterestPostingPeriodType.INVALID.getValue().longValue(), EnumOptionData optionData = new EnumOptionData(SavingsPostingInterestPeriodType.INVALID.getValue().longValue(),
SavingsInterestPostingPeriodType.INVALID.getCode(), "Invalid"); SavingsPostingInterestPeriodType.INVALID.getCode(), "Invalid");
switch (type) { switch (type) {
case INVALID: case INVALID:
break; break;
case MONTHLY: case MONTHLY:
optionData = new EnumOptionData(SavingsInterestPostingPeriodType.MONTHLY.getValue().longValue(), codePrefix optionData = new EnumOptionData(SavingsPostingInterestPeriodType.MONTHLY.getValue().longValue(), codePrefix
+ SavingsInterestPostingPeriodType.MONTHLY.getCode(), "Monthly"); + SavingsPostingInterestPeriodType.MONTHLY.getCode(), "Monthly");
break; break;
case QUATERLY: case QUATERLY:
optionData = new EnumOptionData(SavingsInterestPostingPeriodType.QUATERLY.getValue().longValue(), codePrefix optionData = new EnumOptionData(SavingsPostingInterestPeriodType.QUATERLY.getValue().longValue(), codePrefix
+ SavingsInterestPostingPeriodType.QUATERLY.getCode(), "Quarterly"); + SavingsPostingInterestPeriodType.QUATERLY.getCode(), "Quarterly");
break;
case BI_ANNUAL:
optionData = new EnumOptionData(SavingsInterestPostingPeriodType.BI_ANNUAL.getValue().longValue(), codePrefix
+ SavingsInterestPostingPeriodType.BI_ANNUAL.getCode(), "Semi-Annual");
break; break;
case ANNUAL: case ANNUAL:
optionData = new EnumOptionData(SavingsInterestPostingPeriodType.ANNUAL.getValue().longValue(), codePrefix optionData = new EnumOptionData(SavingsPostingInterestPeriodType.ANNUAL.getValue().longValue(), codePrefix
+ SavingsInterestPostingPeriodType.ANNUAL.getCode(), "Annually"); + SavingsPostingInterestPeriodType.ANNUAL.getCode(), "Annually");
break; break;
} }
@@ -170,35 +192,51 @@ public class SavingsEnumerations {
optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.DAILY.getValue().longValue(), codePrefix optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.DAILY.getValue().longValue(), codePrefix
+ SavingsCompoundingInterestPeriodType.DAILY.getCode(), "Daily"); + SavingsCompoundingInterestPeriodType.DAILY.getCode(), "Daily");
break; break;
case WEEKLY: // case WEEKLY:
optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.WEEKLY.getValue().longValue(), codePrefix // optionData = new
+ SavingsCompoundingInterestPeriodType.WEEKLY.getCode(), "Weekly"); // EnumOptionData(SavingsCompoundingInterestPeriodType.WEEKLY.getValue().longValue(),
break; // codePrefix
case BIWEEKLY: // + SavingsCompoundingInterestPeriodType.WEEKLY.getCode(),
optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.BIWEEKLY.getValue().longValue(), codePrefix // "Weekly");
+ SavingsCompoundingInterestPeriodType.BIWEEKLY.getCode(), "Bi-Weekly"); // break;
break; // case BIWEEKLY:
// optionData = new
// EnumOptionData(SavingsCompoundingInterestPeriodType.BIWEEKLY.getValue().longValue(),
// codePrefix
// + SavingsCompoundingInterestPeriodType.BIWEEKLY.getCode(),
// "Bi-Weekly");
// break;
case MONTHLY: case MONTHLY:
optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.MONTHLY.getValue().longValue(), codePrefix optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.MONTHLY.getValue().longValue(), codePrefix
+ SavingsCompoundingInterestPeriodType.MONTHLY.getCode(), "Monthly"); + SavingsCompoundingInterestPeriodType.MONTHLY.getCode(), "Monthly");
break; break;
case QUATERLY: // case QUATERLY:
optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.QUATERLY.getValue().longValue(), codePrefix // optionData = new
+ SavingsCompoundingInterestPeriodType.QUATERLY.getCode(), "Quarterly"); // EnumOptionData(SavingsCompoundingInterestPeriodType.QUATERLY.getValue().longValue(),
break; // codePrefix
case BI_ANNUAL: // + SavingsCompoundingInterestPeriodType.QUATERLY.getCode(),
optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.BI_ANNUAL.getValue().longValue(), codePrefix // "Quarterly");
+ SavingsCompoundingInterestPeriodType.BI_ANNUAL.getCode(), "Semi-Annual"); // break;
break; // case BI_ANNUAL:
case ANNUAL: // optionData = new
optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.ANNUAL.getValue().longValue(), codePrefix // EnumOptionData(SavingsCompoundingInterestPeriodType.BI_ANNUAL.getValue().longValue(),
+ SavingsCompoundingInterestPeriodType.ANNUAL.getCode(), "Annually"); // codePrefix
break; // + SavingsCompoundingInterestPeriodType.BI_ANNUAL.getCode(),
case NO_COMPOUNDING_SIMPLE_INTEREST: // "Semi-Annual");
optionData = new EnumOptionData(SavingsCompoundingInterestPeriodType.NO_COMPOUNDING_SIMPLE_INTEREST.getValue().longValue(), // break;
codePrefix + SavingsCompoundingInterestPeriodType.NO_COMPOUNDING_SIMPLE_INTEREST.getCode(), // case ANNUAL:
"No Compounding - Simple Interest"); // optionData = new
break; // 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; return optionData;
@@ -24,7 +24,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.accountingRuleParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
@Service @Service
public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements SavingsProductWritePlatformService { 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()); 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 { public class MonetaryCurrencyBuilder {
private String code = "XOF"; private String code = "XOF";
private int digitsAfterDecimal = 0; private int digitsAfterDecimal = 0;
public MonetaryCurrency build() { public MonetaryCurrency build() {
return new MonetaryCurrency(code, digitsAfterDecimal); return new MonetaryCurrency(code, digitsAfterDecimal);
} }
public MonetaryCurrencyBuilder withCode(String withCode) { public MonetaryCurrencyBuilder withCode(final String withCode) {
this.code = withCode; this.code = withCode;
return this; return this;
} }
public MonetaryCurrencyBuilder withDigitsAfterDecimal(int withDigitsAfterDecimal) { public MonetaryCurrencyBuilder withDigitsAfterDecimal(final int withDigitsAfterDecimal) {
this.digitsAfterDecimal = withDigitsAfterDecimal; this.digitsAfterDecimal = withDigitsAfterDecimal;
return this; return this;
} }
} }
@@ -7,20 +7,20 @@ import org.mifosplatform.organisation.monetary.domain.Money;
public class MoneyBuilder { public class MoneyBuilder {
private MonetaryCurrency currencyDetail = new MonetaryCurrencyBuilder().build(); private MonetaryCurrency currencyDetail = new MonetaryCurrencyBuilder().build();
private BigDecimal newAmount = BigDecimal.ZERO; private BigDecimal newAmount = BigDecimal.ZERO;
public Money build() { public Money build() {
return Money.of(currencyDetail, newAmount); return Money.of(currencyDetail, newAmount);
} }
public MoneyBuilder with(MonetaryCurrency withDetail) { public MoneyBuilder with(final MonetaryCurrency withDetail) {
this.currencyDetail = withDetail; this.currencyDetail = withDetail;
return this; return this;
} }
public MoneyBuilder with(String withAmount) { public MoneyBuilder with(final String withAmount) {
this.newAmount = BigDecimal.valueOf(Double.valueOf(withAmount)); this.newAmount = BigDecimal.valueOf(Double.valueOf(withAmount));
return this; return this;
} }
} }