MIFOSX-1252 Squashing all previous commits.Savings Officer changes

This commit is contained in:
Chandrika
2014-09-24 17:39:11 -04:00
parent 114f17e300
commit 253c444a45
20 changed files with 466 additions and 113 deletions
+19 -13
View File
@@ -427,7 +427,7 @@
</tr>
<tr>
<td></td>
<td>savingsaccounts/{accountId}?command=assignFieldOfficer</td>
<td>savingsaccounts/{accountId}?command=assignSavingsOfficer</td>
<td><a href="#savingsaccounts_assignSavingsOfficer">Assign Savings Officer</a></td>
<td></td>
<td></td>
@@ -435,7 +435,7 @@
</tr>
<tr>
<td></td>
<td>savingsaccounts/{accountId}?command=unassignFieldOfficer</td>
<td>savingsaccounts/{accountId}?command=unassignSavingsOfficer</td>
<td><a href="#savingsaccounts_unassignSavingsOfficer">Unassign Savings Officer</a></td>
<td></td>
<td></td>
@@ -19167,20 +19167,24 @@ Request Body:
<p>Allows you to assign <i>Savings Officer</i> for existing Savings Account.</p>
</div>
<div class="method-example">
<code class="method-declaration">POST https://Domain Name/api/v1/savingsaccounts/{savingsId}?command=assignFieldOfficer</code>
<code class="method-request">POST savingsaccounts/1?command=assignFieldOfficer
<code class="method-declaration">POST https://Domain Name/api/v1/savingsaccounts/{savingsId}?command=assignSavingsOfficer</code>
<code class="method-request">POST savingsaccounts/1?command=assignSavingsOfficer
Content-Type: application/json
Request Body:
{
"staffId":1
assignmentDate : "04 September 2014"
dateFormat : "dd MMMM yyyy"
fromSavingsOfficerId : ""
locale : "en"
toSavingsOfficerId : 2
}
</code>
<code class="method-response">
{
"officeId":2,
"clientId":8,
"savingsId":8,
"resourceId":8,
"changes":{"staffId":1}
"changes":{"savingsOfficerId":2}
}
</code>
</div>
@@ -19193,12 +19197,14 @@ Request Body:
<p>Allows you to unassign the <i>Savings Officer</i>.</p>
</div>
<div class="method-example">
<code class="method-declaration">POST https://Domain Name/api/v1/savingsaccounts/{savingsId}?command=unassignFieldOfficer</code>
<code class="method-request">POST savingsaccounts/1?command=unassignFieldOfficer
<code class="method-declaration">POST https://Domain Name/api/v1/savingsaccounts/{savingsId}?command=unassignSavingsOfficer</code>
<code class="method-request">POST savingsaccounts/1?command=unassignSavingsOfficer
Content-Type: application/json
Request Body:
{
"staffId":1
dateFormat : "dd MMMM yyyy"
locale : "en"
unassignedDate : "05 September 2014"
}
</code>
<code class="method-response">
@@ -19206,7 +19212,7 @@ Request Body:
"officeId":2,
"clientId":8,
"resourceId":8,
"changes":{"staffId":1}
"changes":{}
}
</code>
</div>
@@ -33777,7 +33783,7 @@ No Request Body:
</tr>
<tr>
<td></td>
<td>savingsaccounts/{accountId}?command=assignFieldOfficer</td>
<td>savingsaccounts/{accountId}?command=assignSavingsOfficer</td>
<td><a href="#savingsaccounts_assignSavingsOfficer">Assign Savings Officer</a></td>
<td></td>
<td></td>
@@ -33785,7 +33791,7 @@ No Request Body:
</tr>
<tr>
<td></td>
<td>savingsaccounts/{accountId}?command=unassignFieldOfficer</td>
<td>savingsaccounts/{accountId}?command=unassignSavingsOfficer</td>
<td><a href="#savingsaccounts_unassignSavingsOfficer">Unassign Savings Officer</a></td>
<td></td>
<td></td>
@@ -0,0 +1,20 @@
CREATE TABLE `m_savings_officer_assignment_history` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`account_id` bigint(20) NOT NULL,
`savings_officer_id` bigint(20) DEFAULT NULL,
`start_date` date NOT NULL,
`end_date` date DEFAULT NULL,
`createdby_id` bigint(20) DEFAULT NULL,
`created_date` datetime DEFAULT NULL,
`lastmodified_date` datetime DEFAULT NULL,
`lastmodifiedby_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fk_m_savings_officer_assignment_history_0001` (`account_id`),
KEY `fk_m_savings_officer_assignment_history_0002` (`savings_officer_id`),
CONSTRAINT `fk_m_savings_officer_assignment_history_0001` FOREIGN KEY (`account_id`) REFERENCES `m_savings_account` (`id`),
CONSTRAINT `fk_m_savings_officer_assignment_history_0002` FOREIGN KEY (`savings_officer_id`) REFERENCES `m_staff` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into m_permission (grouping,code,entity_name,action_name) values ('portfolio','REMOVESAVINGSOFFICER_SAVINGSACCOUNT','SAVINGSACCOUNT','REMOVESAVINGSOFFICER');
insert into m_permission (grouping,code,entity_name,action_name) values ('portfolio','UPDATESAVINGSOFFICER_SAVINGSACCOUNT','SAVINGSACCOUNT','UPDATESAVINGSOFFICER');
@@ -589,12 +589,12 @@ public class CommandWrapper {
return this.actionName.equalsIgnoreCase("ADJUSTTRANSACTION") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
}
public boolean isAssignStaffSavingsAccount(){
return this.actionName.equalsIgnoreCase("ASSIGNFIELDOFFICER") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
public boolean isUpdateSavingsOfficer(){
return this.actionName.equalsIgnoreCase("UPDATESAVINGSOFFICER") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
}
public boolean isUnAssignStaffSavingsAccount(){
return this.actionName.equalsIgnoreCase("UNASSIGNFIELDOFFICER") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
public boolean isRemoveSavingsOfficer(){
return this.actionName.equalsIgnoreCase("REMOVESAVINGSOFFICER") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
}
public boolean isSavingsAccountChargeResource() {
@@ -2073,19 +2073,19 @@ public class CommandWrapperBuilder {
this.entityId = entityId;
return this;
}
public CommandWrapperBuilder assignFieldOfficer(final Long accountId) {
this.actionName = "ASSIGNFIELDOFFICER";
public CommandWrapperBuilder assignSavingsOfficer(final Long accountId) {
this.actionName = "UPDATESAVINGSOFFICER";
this.entityName = "SAVINGSACCOUNT";
this.entityId = accountId;
this.href = "/savingsaccounts/" + accountId + "?command=assignFieldOfficer";
this.href = "/savingsaccounts/" + accountId + "?command=assignSavingsOfficer";
return this;
}
public CommandWrapperBuilder unassignFieldOfficer(final Long accountId) {
this.actionName = "UNASSIGNFIELDOFFICER";
public CommandWrapperBuilder unassignSavingsOfficer(final Long accountId) {
this.actionName = "REMOVESAVINGSOFFICER";
this.entityName = "SAVINGSACCOUNT";
this.entityId = accountId;
this.href = "/savingsaccounts/" + accountId + "?command=unassignFieldOfficer";
this.href = "/savingsaccounts/" + accountId + "?commad=unassignSavingsOfficer";
return this;
}
@@ -512,10 +512,10 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
handler = this.applicationContext.getBean("savingsTransactionAdjustmentCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isSavingsAccountClose()) {
handler = this.applicationContext.getBean("closeSavingsAccountCommandHandler", NewCommandSourceHandler.class);
}else if(wrapper.isAssignStaffSavingsAccount()) {
handler = this.applicationContext.getBean("assignFieldOfficerSavingsAccountCommandHandler", NewCommandSourceHandler.class);
}else if(wrapper.isUnAssignStaffSavingsAccount()) {
handler = this.applicationContext.getBean("unassignFieldOfficerSavingsAccountCommandHandler", NewCommandSourceHandler.class);
}else if(wrapper.isUpdateSavingsOfficer()) {
handler = this.applicationContext.getBean("updateSavingsOfficerCommandHandler", NewCommandSourceHandler.class);
}else if(wrapper.isRemoveSavingsOfficer()) {
handler = this.applicationContext.getBean("removeSavingsOfficerCommandHandler", NewCommandSourceHandler.class);
}
else {
throw new UnsupportedCommandException(wrapper.commandName());
@@ -13,7 +13,7 @@ import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceN
public class StaffRoleException extends AbstractPlatformResourceNotFoundException {
public static enum STAFF_ROLE {
LOAN_OFFICER, BRANCH_MANAGER;
LOAN_OFFICER, BRANCH_MANAGER,SAVINGS_OFFICER;
@Override
public String toString() {
@@ -48,7 +48,7 @@ public class SavingsApiConstants {
public static final String localeParamName = "locale";
public static final String dateFormatParamName = "dateFormat";
public static final String monthDayFormatParamName = "monthDayFormat";
public static final String staffIdParamName = "staffId";
public static final String staffIdParamName = "savingsOfficerId";
// savings product and account parameters
public static final String idParamName = "id";
@@ -248,12 +248,12 @@ public class SavingsAccountsApiResource {
} else if (is(commandParam, "close")) {
final CommandWrapper commandRequest = builder.closeSavingsAccountApplication(accountId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (is(commandParam, "assignFieldOfficer")) {
final CommandWrapper commandRequest = builder.assignFieldOfficer(accountId).build();
} else if (is(commandParam, "assignSavingsOfficer")) {
final CommandWrapper commandRequest = builder.assignSavingsOfficer(accountId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
} else if (is(commandParam, "unassignFieldOfficer")) {
final CommandWrapper commandRequest = builder.unassignFieldOfficer(accountId).build();
} else if (is(commandParam, "unassignSavingsOfficer")) {
final CommandWrapper commandRequest = builder.unassignSavingsOfficer(accountId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
@@ -262,7 +262,7 @@ public class SavingsAccountsApiResource {
//
throw new UnrecognizedQueryParamException("command", commandParam,
new Object[] { "reject", "withdrawnByApplicant", "approve", "undoapproval", "activate", "calculateInterest",
"postInterest", "close", "assignFieldOfficer", "unassignFieldOfficer" });
"postInterest", "close", "assignSavingsOfficer", "unassignSavingsOfficer" });
}
return this.toApiJsonSerializer.serialize(result);
@@ -391,50 +391,56 @@ public class SavingsAccountDataValidator {
}
public void validateForAssignFieldOfficer(final String json) {
public void validateForAssignSavingsOfficer(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Set<String> supportedParameters = new HashSet<>(Arrays.asList("fromSavingsOfficerId","toSavingsOfficerId","assignmentDate","locale","dateFormat"));
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
final Set<String> supportedParametersUnassignStaff = new HashSet<>(Arrays.asList(SavingsApiConstants.staffIdParamName));
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParametersUnassignStaff);
final JsonElement element = this.fromApiJsonHelper.parse(json);
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final String staffIdParameterName = SavingsApiConstants.staffIdParamName;
final Long staffId = this.fromApiJsonHelper.extractLongNamed(staffIdParameterName, element);
baseDataValidator.reset().parameter(staffIdParameterName).value(staffId).notNull().longGreaterThanZero();
final Long toSavingsOfficerId = this.fromApiJsonHelper.extractLongNamed("toSavingsOfficerId", element);
baseDataValidator.reset().parameter("toSavingsOfficerId").value(toSavingsOfficerId).notNull().integerGreaterThanZero();
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
final String assignmentDateStr = this.fromApiJsonHelper.extractStringNamed("assignmentDate", element);
baseDataValidator.reset().parameter("assignmentDate").value(assignmentDateStr).notBlank();
if (!StringUtils.isBlank(assignmentDateStr)) {
final LocalDate assignmentDate = this.fromApiJsonHelper.extractLocalDateNamed("assignmentDate", element);
baseDataValidator.reset().parameter("assignmentDate").value(assignmentDate).notNull();
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
public void validateForUnAssignFieldOfficer(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
public void validateForUnAssignSavingsOfficer(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Set<String> supportedParameters = new HashSet<>(Arrays.asList("unassignedDate","locale","dateFormat"));
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
final Set<String> supportedParametersUnassignStaff = new HashSet<>(Arrays.asList(SavingsApiConstants.staffIdParamName));
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParametersUnassignStaff);
final JsonElement element = this.fromApiJsonHelper.parse(json);
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final String staffIdParameterName = SavingsApiConstants.staffIdParamName;
final Long staffId = this.fromApiJsonHelper.extractLongNamed(staffIdParameterName, element);
baseDataValidator.reset().parameter(staffIdParameterName).value(staffId).notNull().longGreaterThanZero();
final String unassignedDateStr = this.fromApiJsonHelper.extractStringNamed("unassignedDate", element);
baseDataValidator.reset().parameter("unassignedDate").value(unassignedDateStr).notBlank();
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
if (!StringUtils.isBlank(unassignedDateStr)) {
final LocalDate unassignedDate = this.fromApiJsonHelper.extractLocalDateNamed("unassignedDate", element);
baseDataValidator.reset().parameter("unassignedDate").value(unassignedDate).notNull();
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
}
@@ -728,7 +728,7 @@ public class FixedDepositAccount extends SavingsAccount {
final boolean withdrawalFeeApplicableForTransfer = false;
final String accountNumber = null;
final FixedDepositAccount reInvestedAccount = FixedDepositAccount
.createNewApplicationForSubmittal(client, group, product, fieldOfficer, accountNumber, externalId, accountType,
.createNewApplicationForSubmittal(client, group, product, savingsOfficer, accountNumber, externalId, accountType,
getClosedOnDate(), closedBy, interestRate, compoundingPeriodType, postingPeriodType, interestCalculationType,
daysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
withdrawalFeeApplicableForTransfer, savingsAccountCharges, newAccountTermAndPreClosure, newChart);
@@ -1006,7 +1006,7 @@ public class RecurringDepositAccount extends SavingsAccount {
newAccountTermAndPreClosure.updateExpectedFirstDepositDate(now);
RecurringDepositAccount rdAccount = RecurringDepositAccount.createNewActivatedAccount(client, group, product, fieldOfficer,
RecurringDepositAccount rdAccount = RecurringDepositAccount.createNewActivatedAccount(client, group, product, savingsOfficer,
accountNumber, externalId, accountType, getClosedOnDate(), closedBy, interestRate, compoundingPeriodType,
postingPeriodType, interestCalculationType, daysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, savingsAccountCharges, newAccountTermAndPreClosure,
@@ -75,6 +75,8 @@ import org.mifosplatform.portfolio.charge.exception.SavingsAccountChargeNotFound
import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
import org.mifosplatform.portfolio.group.domain.Group;
import org.mifosplatform.portfolio.savings.exception.SavingsOfficerAssignmentDateException;
import org.mifosplatform.portfolio.savings.domain.SavingsOfficerAssignmentHistory;
import org.mifosplatform.portfolio.savings.DepositAccountType;
import org.mifosplatform.portfolio.savings.SavingsApiConstants;
import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType;
@@ -87,6 +89,7 @@ import org.mifosplatform.portfolio.savings.domain.interest.PostingPeriod;
import org.mifosplatform.portfolio.savings.exception.InsufficientAccountBalanceException;
import org.mifosplatform.portfolio.savings.exception.SavingsAccountTransactionNotFoundException;
import org.mifosplatform.portfolio.savings.exception.SavingsActivityPriorToClientTransferException;
import org.mifosplatform.portfolio.savings.exception.SavingsOfficerUnassignmentDateException;
import org.mifosplatform.portfolio.savings.exception.SavingsTransferTransactionsCannotBeUndoneException;
import org.mifosplatform.portfolio.savings.service.SavingsEnumerations;
import org.mifosplatform.useradministration.domain.AppUser;
@@ -123,7 +126,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
@ManyToOne
@JoinColumn(name = "field_officer_id", nullable = true)
protected Staff fieldOfficer;
protected Staff savingsOfficer;
@Column(name = "status_enum", nullable = false)
protected Integer status;
@@ -263,6 +266,10 @@ public class SavingsAccount extends AbstractPersistable<Long> {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "savingsAccount", orphanRemoval = true)
protected Set<SavingsAccountCharge> charges = new HashSet<>();
@LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(cascade = CascadeType.ALL, mappedBy = "savingsAccount", orphanRemoval = true)
private Set<SavingsOfficerAssignmentHistory> savingsOfficerHistory;
@Transient
protected boolean accountNumberRequiresAutoGeneration = false;
@Transient
@@ -279,7 +286,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
protected SavingsAccount() {
//
}
public static SavingsAccount createNewApplicationForSubmittal(final Client client, final Group group, final SavingsProduct product,
final Staff fieldOfficer, final String accountNo, final String externalId, final AccountType accountType,
final LocalDate submittedOnDate, final AppUser submittedBy, final BigDecimal interestRate,
@@ -314,7 +321,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
withdrawalFeeApplicableForTransfer, savingsAccountCharges, allowOverdraft, overdraftLimit, false, null);
}
protected SavingsAccount(final Client client, final Group group, final SavingsProduct product, final Staff fieldOfficer,
protected SavingsAccount(final Client client, final Group group, final SavingsProduct product, final Staff savingsOfficer,
final String accountNo, final String externalId, final SavingsAccountStatusType status, final AccountType accountType,
final LocalDate submittedOnDate, final AppUser submittedBy, final BigDecimal nominalAnnualInterestRate,
final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
@@ -327,7 +334,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
this.client = client;
this.group = group;
this.product = product;
this.fieldOfficer = fieldOfficer;
this.savingsOfficer = savingsOfficer;
if (StringUtils.isBlank(accountNo)) {
this.accountNumber = new RandomPasswordGenerator(19).generate();
this.accountNumberRequiresAutoGeneration = true;
@@ -364,6 +371,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
this.enforceMinRequiredBalance = enforceMinRequiredBalance;
this.minRequiredBalance = minRequiredBalance;
this.minBalanceForInterestCalculation = product.minBalanceForInterestCalculation();
this.savingsOfficerHistory = null;
}
/**
@@ -991,7 +999,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
actualChanges.put(SavingsApiConstants.productIdParamName, newValue);
}
if (command.isChangeInLongParameterNamed(SavingsApiConstants.fieldOfficerIdParamName, fieldOfficerId())) {
if (command.isChangeInLongParameterNamed(SavingsApiConstants.fieldOfficerIdParamName, hasSavingsOfficerId())) {
final Long newValue = command.longValueOfParameterNamed(SavingsApiConstants.fieldOfficerIdParamName);
actualChanges.put(SavingsApiConstants.fieldOfficerIdParamName, newValue);
}
@@ -1196,8 +1204,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
this.minBalanceForInterestCalculation = product.minBalanceForInterestCalculation();
}
public void update(final Staff fieldOfficer) {
this.fieldOfficer = fieldOfficer;
public void update(final Staff savingsOfficer) {
this.savingsOfficer = savingsOfficer;
}
public void updateAccountNo(final String newAccountNo) {
@@ -1241,16 +1249,16 @@ public class SavingsAccount extends AbstractPersistable<Long> {
return office;
}
public Staff getFieldOfficer() {
return this.fieldOfficer;
public Staff getSavingsOfficer() {
return this.savingsOfficer;
}
public void unassignFieldOfficer() {
this.fieldOfficer = null;
public void unassignSavingsOfficer() {
this.savingsOfficer = null;
}
public void assignFieldOfficer(final Staff fieldOfficer) {
this.fieldOfficer = fieldOfficer;
public void assignSavingsOfficer(final Staff fieldOfficer) {
this.savingsOfficer = fieldOfficer;
}
public Long clientId() {
@@ -1269,14 +1277,149 @@ public class SavingsAccount extends AbstractPersistable<Long> {
return id;
}
public Long fieldOfficerId() {
public Long hasSavingsOfficerId() {
Long id = null;
if (this.fieldOfficer != null) {
id = this.fieldOfficer.getId();
if (this.savingsOfficer != null) {
id = this.savingsOfficer.getId();
}
return id;
}
public boolean hasSavingsOfficer(final Staff fromSavingsOfficer) {
boolean matchesCurrentSavingsOfficer = false;
if (this.savingsOfficer != null) {
matchesCurrentSavingsOfficer = this.savingsOfficer.identifiedBy(fromSavingsOfficer);
} else {
matchesCurrentSavingsOfficer = fromSavingsOfficer == null;
}
return matchesCurrentSavingsOfficer;
}
public void reassignSavingsOfficer(final Staff newSavingsOfficer,final LocalDate assignmentDate) {
final SavingsOfficerAssignmentHistory latestHistoryRecord = findLatestIncompleteHistoryRecord();
final SavingsOfficerAssignmentHistory lastAssignmentRecord = findLastAssignmentHistoryRecord(newSavingsOfficer);
// assignment date should not be less than savings account submitted date
if (isSubmittedOnDateAfter(assignmentDate)) {
final String errorMessage = "The Savings Officer assignment date (" + assignmentDate.toString()
+ ") cannot be before savings submitted date (" + getSubmittedOnDate().toString() + ").";
throw new SavingsOfficerAssignmentDateException("cannot.be.before.savings.submitted.date", errorMessage, assignmentDate,
getSubmittedOnDate());
} else if (lastAssignmentRecord != null && lastAssignmentRecord.isEndDateAfter(assignmentDate)) {
final String errorMessage = "The Savings Officer assignment date (" + assignmentDate
+ ") cannot be before previous Savings Officer unassigned date (" + lastAssignmentRecord.getEndDate() + ").";
throw new SavingsOfficerAssignmentDateException("cannot.be.before.previous.unassignement.date", errorMessage, assignmentDate,
lastAssignmentRecord.getEndDate());
} else if (DateUtils.getLocalDateOfTenant().isBefore(assignmentDate)) {
final String errorMessage = "The Savings Officer assignment date (" + assignmentDate + ") cannot be in the future.";
throw new SavingsOfficerAssignmentDateException("cannot.be.a.future.date", errorMessage, assignmentDate);
} else if (latestHistoryRecord != null && this.savingsOfficer.identifiedBy(newSavingsOfficer)) {
latestHistoryRecord.updateStartDate(assignmentDate);
} else if (latestHistoryRecord != null && latestHistoryRecord.matchesStartDateOf(assignmentDate)) {
latestHistoryRecord.updateSavingsOfficer(newSavingsOfficer);
this.savingsOfficer = newSavingsOfficer;
} else if (latestHistoryRecord != null && latestHistoryRecord.hasStartDateBefore(assignmentDate)) {
final String errorMessage = "Savings account with identifier " + getId() + " was already assigned before date " + assignmentDate;
throw new SavingsOfficerAssignmentDateException("is.before.last.assignment.date", errorMessage, getId(), assignmentDate);
} else {
if (latestHistoryRecord != null) {
// savings officer correctly changed from previous savings officer to
// new savings officer
latestHistoryRecord.updateEndDate(assignmentDate);
}
this.savingsOfficer = newSavingsOfficer;
if (isNotSubmittedAndPendingApproval()) {
final SavingsOfficerAssignmentHistory savingsOfficerAssignmentHistory =
SavingsOfficerAssignmentHistory.createNew(this,this.savingsOfficer,assignmentDate);
this.savingsOfficerHistory.add(savingsOfficerAssignmentHistory);
}
}
}
private SavingsOfficerAssignmentHistory findLastAssignmentHistoryRecord(final Staff newSavingsOfficer) {
SavingsOfficerAssignmentHistory lastAssignmentRecordLatestEndDate = null;
for (final SavingsOfficerAssignmentHistory historyRecord : this.savingsOfficerHistory) {
if (historyRecord.isCurrentRecord() && !historyRecord.isSameSavingsOfficer(newSavingsOfficer)) {
lastAssignmentRecordLatestEndDate = historyRecord;
break;
}
if (lastAssignmentRecordLatestEndDate == null) {
lastAssignmentRecordLatestEndDate = historyRecord;
} else if (historyRecord.isEndDateAfter(lastAssignmentRecordLatestEndDate.getEndDate())
&& !historyRecord.isSameSavingsOfficer(newSavingsOfficer)) {
lastAssignmentRecordLatestEndDate = historyRecord;
}
}
return lastAssignmentRecordLatestEndDate;
}
public boolean isSubmittedOnDateAfter(final LocalDate compareDate) {
return this.submittedOnDate == null ? false : new LocalDate(this.submittedOnDate).isAfter(compareDate);
}
public LocalDate getSubmittedOnDate() {
return (LocalDate) ObjectUtils.defaultIfNull(new LocalDate(this.submittedOnDate), null);
}
public void removeSavingsOfficer(final LocalDate unassignDate) {
final SavingsOfficerAssignmentHistory latestHistoryRecord = findLatestIncompleteHistoryRecord();
if (latestHistoryRecord != null) {
validateUnassignDate(latestHistoryRecord, unassignDate);
latestHistoryRecord.updateEndDate(unassignDate);
}
this.savingsOfficer = null;
}
private SavingsOfficerAssignmentHistory findLatestIncompleteHistoryRecord() {
SavingsOfficerAssignmentHistory latestRecordWithNoEndDate = null;
for (final SavingsOfficerAssignmentHistory historyRecord : this.savingsOfficerHistory) {
if (historyRecord.isCurrentRecord()) {
latestRecordWithNoEndDate = historyRecord;
break;
}
}
return latestRecordWithNoEndDate;
}
private void validateUnassignDate(final SavingsOfficerAssignmentHistory latestHistoryRecord, final LocalDate unassignDate) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
if (latestHistoryRecord.getStartDate().isAfter(unassignDate)) {
final String errorMessage = "The Savings officer Unassign date(" + unassignDate + ") cannot be before its assignment date ("
+ latestHistoryRecord.getStartDate() + ").";
throw new SavingsOfficerUnassignmentDateException("cannot.be.before.assignment.date", errorMessage, getId(), getSavingsOfficer()
.getId(), latestHistoryRecord.getStartDate(), unassignDate);
} else if (unassignDate.isAfter(today)) {
final String errorMessage = "The Savings Officer Unassign date (" + unassignDate + ") cannot be in the future.";
throw new SavingsOfficerUnassignmentDateException("cannot.be.a.future.date", errorMessage, unassignDate);
}
}
public MonetaryCurrency getCurrency() {
return this.currency;
}
@@ -0,0 +1,106 @@
/**
* 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.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.apache.commons.lang.ObjectUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom;
import org.mifosplatform.organisation.staff.domain.Staff;
import org.mifosplatform.useradministration.domain.AppUser;
@Entity
@Table(name = "m_savings_officer_assignment_history")
public class SavingsOfficerAssignmentHistory extends AbstractAuditableCustom<AppUser, Long> {
@ManyToOne
@JoinColumn(name = "account_id", nullable = false)
private SavingsAccount savingsAccount;
@ManyToOne
@JoinColumn(name = "savings_officer_id", nullable = true)
private Staff savingsOfficer;
@Temporal(TemporalType.DATE)
@Column(name = "start_date")
private Date startDate;
@Temporal(TemporalType.DATE)
@Column(name = "end_date")
private Date endDate;
public static SavingsOfficerAssignmentHistory createNew(final SavingsAccount account, final Staff savingsOfficer,final LocalDate assignmentDate) {
return new SavingsOfficerAssignmentHistory(account, savingsOfficer,assignmentDate.toDate(),null);
}
protected SavingsOfficerAssignmentHistory() {
//
}
private SavingsOfficerAssignmentHistory(final SavingsAccount account, final Staff savingsOfficer,final Date startDate, final Date endDate) {
this.savingsAccount = account;
this.savingsOfficer = savingsOfficer;
this.startDate = startDate;
this.endDate = endDate;
}
public void updateSavingsOfficer(final Staff savingsOfficer) {
this.savingsOfficer = savingsOfficer;
}
public boolean isSameSavingsOfficer(final Staff staff) {
return this.savingsOfficer.identifiedBy(staff);
}
public void updateStartDate(final LocalDate startDate) {
this.startDate = startDate.toDate();
}
public void updateEndDate(final LocalDate endDate) {
this.endDate = endDate.toDate();
}
public boolean matchesStartDateOf(final LocalDate matchingDate) {
return getStartDate().isEqual(matchingDate);
}
public LocalDate getStartDate() {
return new LocalDate(this.startDate);
}
public boolean hasStartDateBefore(final LocalDate matchingDate) {
return matchingDate.isBefore(getStartDate());
}
public boolean isCurrentRecord() {
return this.endDate == null;
}
/**
* If endDate is null then return false.
*
* @param compareDate
* @return
*/
public boolean isEndDateAfter(final LocalDate compareDate) {
return this.endDate == null ? false : new LocalDate(this.endDate).isAfter(compareDate);
}
public LocalDate getEndDate() {
return (LocalDate) ObjectUtils.defaultIfNull(new LocalDate(this.endDate), null);
}
}
@@ -0,0 +1,16 @@
/**
* 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.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class SavingsOfficerAssignmentDateException extends AbstractPlatformDomainRuleException {
public SavingsOfficerAssignmentDateException(final String postFix, final String defaultUserMessage, final Object... defaultUserMessageArgs) {
super("error.msg.savings.assignment.date." + postFix, defaultUserMessage, defaultUserMessageArgs);
}
}
@@ -0,0 +1,16 @@
/**
* 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.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class SavingsOfficerAssignmentException extends AbstractPlatformDomainRuleException {
public SavingsOfficerAssignmentException(final Long accountId, final Long fromSavingsOfficerId) {
super("error.msg.savings.account.not.assigned.to.savings.officer", "Savings Account Identifier " + accountId
+ " is not assigned to Savings Officer with identifier " + fromSavingsOfficerId + ".", accountId);
}
}
@@ -0,0 +1,17 @@
/**
* 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.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class SavingsOfficerUnassignmentDateException extends AbstractPlatformDomainRuleException {
public SavingsOfficerUnassignmentDateException(final String postFix, final String defaultUserMessage,
final Object... defaultUserMessageArgs) {
super("error.msg.savings.savingsofficer.unassign.date." + postFix, defaultUserMessage, defaultUserMessageArgs);
}
}
@@ -0,0 +1,15 @@
/**
* 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.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class SavingsOfficerUnassignmentException extends AbstractPlatformDomainRuleException {
public SavingsOfficerUnassignmentException(final Long accountId) {
super("error.msg.savings.account.not.assigned.to.savings.officer", "Savings Account Identifier" + accountId + " is not assigned to any savings officer.");
}
}
@@ -14,12 +14,12 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UnassignFieldOfficerSavingsAccountCommandHandler implements NewCommandSourceHandler {
public class RemoveSavingsOfficerCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService savingsWritePlatformService;
@Autowired
public UnassignFieldOfficerSavingsAccountCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) {
public RemoveSavingsOfficerCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) {
this.savingsWritePlatformService = savingAccountWritePlatformService;
}
@@ -14,12 +14,12 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class AssignFieldOfficerSavingsAccountCommandHandler implements NewCommandSourceHandler {
public class UpdateSavingsOfficerCommandHandler implements NewCommandSourceHandler {
private final SavingsAccountWritePlatformService savingsWritePlatformService;
@Autowired
public AssignFieldOfficerSavingsAccountCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) {
public UpdateSavingsOfficerCommandHandler(final SavingsAccountWritePlatformService savingAccountWritePlatformService) {
this.savingsWritePlatformService = savingAccountWritePlatformService;
}
@@ -81,6 +81,8 @@ import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransactionRepository;
import org.mifosplatform.portfolio.savings.exception.SavingsAccountClosingNotAllowedException;
import org.mifosplatform.portfolio.savings.exception.SavingsAccountTransactionNotFoundException;
import org.mifosplatform.portfolio.savings.exception.SavingsOfficerAssignmentException;
import org.mifosplatform.portfolio.savings.exception.SavingsOfficerUnassignmentException;
import org.mifosplatform.portfolio.savings.exception.TransactionUpdateNotAllowedException;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
@@ -1021,66 +1023,72 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
return null;
}
@Transactional
@Override
public CommandProcessingResult assignFieldOfficer(Long savingsAccountId, JsonCommand command) {
this.context.authenticatedUser();
final Map<String, Object> actualChanges = new LinkedHashMap<>(5);
this.fromApiJsonDeserializer.validateForAssignFieldOfficer(command.json());
Staff fromSavingsOfficer = null;
Staff toSavingsOfficer = null;
this.fromApiJsonDeserializer.validateForAssignSavingsOfficer(command.json());
final SavingsAccount savingsForUpdate = this.savingsRepository.findOneWithNotFoundDetection(savingsAccountId);
Staff staff = null;
final Long staffId = command.longValueOfParameterNamed(SavingsApiConstants.staffIdParamName);
if (staffId != null) {
staff = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(staffId, savingsForUpdate.office().getHierarchy());
savingsForUpdate.assignFieldOfficer(staff);
final Long fromSavingsOfficerId = command.longValueOfParameterNamed("fromSavingsOfficerId");
final Long toSavingsOfficerId = command.longValueOfParameterNamed("toSavingsOfficerId");
final LocalDate dateOfSavingsOfficerAssignment = command.localDateValueOfParameterNamed("assignmentDate");
if(fromSavingsOfficerId != null)
{
fromSavingsOfficer = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(fromSavingsOfficerId, savingsForUpdate.office().getHierarchy());
}
if(toSavingsOfficerId != null)
{
toSavingsOfficer = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(toSavingsOfficerId, savingsForUpdate.office().getHierarchy());
}
if (!savingsForUpdate.hasSavingsOfficer(fromSavingsOfficer)) { throw new SavingsOfficerAssignmentException(savingsAccountId, fromSavingsOfficerId); }
savingsForUpdate.reassignSavingsOfficer(toSavingsOfficer,dateOfSavingsOfficerAssignment);
this.savingsRepository.saveAndFlush(savingsForUpdate);
actualChanges.put(SavingsApiConstants.staffIdParamName, staffId);
actualChanges.put("toSavingsOfficerId", toSavingsOfficer.getId());
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withOfficeId(savingsForUpdate.officeId()) //
.withEntityId(savingsForUpdate.getId()) //
.withClientId(savingsAccountId) //
.withSavingsId(savingsAccountId) //
.with(actualChanges) //
.build();
}
@Transactional
@Override
public CommandProcessingResult unassignFieldOfficer(Long savingsAccountId, JsonCommand command) {
this.context.authenticatedUser();
this.context.authenticatedUser();
final Map<String, Object> actualChanges = new LinkedHashMap<>(5);
final Map<String, Object> actualChanges = new LinkedHashMap<>(5);
this.fromApiJsonDeserializer.validateForUnAssignSavingsOfficer(command.json());
final SavingsAccount savingsForUpdate = this.savingsRepository.findOneWithNotFoundDetection(savingsAccountId);
if (savingsForUpdate.getSavingsOfficer() == null) { throw new SavingsOfficerUnassignmentException(savingsAccountId); }
this.fromApiJsonDeserializer.validateForUnAssignFieldOfficer(command.json());
final LocalDate dateOfSavingsOfficerUnassigned = command.localDateValueOfParameterNamed("unassignedDate");
savingsForUpdate.removeSavingsOfficer(dateOfSavingsOfficerUnassigned);
final SavingsAccount savingsForUpdate = this.savingsRepository.findOneWithNotFoundDetection(savingsAccountId);
final Staff presentStaff = savingsForUpdate.getFieldOfficer();
Long presentStaffId = null;
if (presentStaff == null) { throw new ClientHasNoStaffException(savingsAccountId); }
presentStaffId = presentStaff.getId();
final String staffIdParamName = SavingsApiConstants.staffIdParamName;
if (!command.isChangeInLongParameterNamed(staffIdParamName, presentStaffId)) {
savingsForUpdate.unassignFieldOfficer();
}
this.savingsRepository.saveAndFlush(savingsForUpdate);
actualChanges.put(staffIdParamName, presentStaffId);
actualChanges.put("toSavingsOfficerId", null);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withOfficeId(savingsForUpdate.officeId()) //
.withEntityId(savingsForUpdate.getId()) //
.withClientId(savingsAccountId) //
.withSavingsId(savingsAccountId) //
.with(actualChanges) //
.build();
}
.build();
}
}