adding an api workflow for transfers

This commit is contained in:
Vishwas Babu A J
2013-08-26 23:25:33 +05:30
parent b1d2b42da1
commit 22fee03e0a
37 changed files with 764 additions and 103 deletions
@@ -0,0 +1,30 @@
DELETE FROM `m_permission` WHERE `code`="TRANSFER_CLIENT";
DELETE FROM `m_permission` WHERE `code`="TRANSFER_CLIENT_CHECKER";
/**Permissions for proposing a transfer**/
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('portfolio', 'PROPOSETRANSFER_CLIENT', 'CLIENT', 'PROPOSETRANSFER', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('portfolio', 'PROPOSETRANSFER_CLIENT_CHECKER', 'CLIENT', 'PROPOSETRANSFER', 0);
/**Permissions for accepting a transfer**/
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('portfolio', 'ACCEPTTRANSFER_CLIENT', 'CLIENT', 'ACCEPTTRANSFER', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('portfolio', 'ACCEPTTRANSFER_CLIENT_CHECKER', 'CLIENT', 'ACCEPTTRANSFER', 0);
/**Permissions for rejecting a transfer**/
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('portfolio', 'REJECTTRANSFER_CLIENT', 'CLIENT', 'REJECTTRANSFER', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('portfolio', 'REJECTTRANSFER_CLIENT_CHECKER', 'CLIENT', 'REJECTTRANSFER', 0);
/**Permissions for withdrawing a transfer proposal**/
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('portfolio', 'WITHDRAWTRANSFER_CLIENT', 'CLIENT', 'WITHDRAWTRANSFER', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('portfolio', 'WITHDRAWTRANSFER_CLIENT_CHECKER', 'CLIENT', 'WITHDRAWTRANSFER', 0);
@@ -12,7 +12,8 @@ public class AccountingConstants {
/*** Accounting placeholders for cash based accounting for loan products ***/
public static enum CASH_ACCOUNTS_FOR_LOAN {
FUND_SOURCE(1), LOAN_PORTFOLIO(2), INTEREST_ON_LOANS(3), INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), LOSSES_WRITTEN_OFF(6);
FUND_SOURCE(1), LOAN_PORTFOLIO(2), INTEREST_ON_LOANS(3), INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), LOSSES_WRITTEN_OFF(6), TRANSFERS_SUSPENSE(
10);
private final Integer value;
@@ -45,7 +46,7 @@ public class AccountingConstants {
/*** Accounting placeholders for accrual based accounting for loan products ***/
public static enum ACCRUAL_ACCOUNTS_FOR_LOAN {
FUND_SOURCE(1), LOAN_PORTFOLIO(2), INTEREST_ON_LOANS(3), INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), LOSSES_WRITTEN_OFF(6), INTEREST_RECEIVABLE(
7), FEES_RECEIVABLE(8), PENALTIES_RECEIVABLE(9);
7), FEES_RECEIVABLE(8), PENALTIES_RECEIVABLE(9), TRANSFERS_SUSPENSE(10);
private final Integer value;
@@ -84,9 +85,10 @@ public class AccountingConstants {
FUND_SOURCE("fundSourceAccountId"), LOAN_PORTFOLIO("loanPortfolioAccountId"), INTEREST_ON_LOANS("interestOnLoanAccountId"), INCOME_FROM_FEES(
"incomeFromFeeAccountId"), INCOME_FROM_PENALTIES("incomeFromPenaltyAccountId"), LOSSES_WRITTEN_OFF("writeOffAccountId"), INTEREST_RECEIVABLE(
"receivableInterestAccountId"), FEES_RECEIVABLE("receivableFeeAccountId"), PENALTIES_RECEIVABLE(
"receivablePenaltyAccountId"), PAYMENT_CHANNEL_FUND_SOURCE_MAPPING("paymentChannelToFundSourceMappings"), PAYMENT_TYPE(
"paymentTypeId"), FEE_INCOME_ACCOUNT_MAPPING("feeToIncomeAccountMappings"), PENALTY_INCOME_ACCOUNT_MAPPING(
"penaltyToIncomeAccountMappings"), CHARGE_ID("chargeId"), INCOME_ACCOUNT_ID("incomeAccountId");
"receivablePenaltyAccountId"), TRANSFERS_SUSPENSE("transfersInSuspenseAccountId"), PAYMENT_CHANNEL_FUND_SOURCE_MAPPING(
"paymentChannelToFundSourceMappings"), PAYMENT_TYPE("paymentTypeId"), FEE_INCOME_ACCOUNT_MAPPING(
"feeToIncomeAccountMappings"), PENALTY_INCOME_ACCOUNT_MAPPING("penaltyToIncomeAccountMappings"), CHARGE_ID("chargeId"), INCOME_ACCOUNT_ID(
"incomeAccountId");
private final String value;
@@ -76,11 +76,11 @@ public class GLAccountWritePlatformServiceJpaRepositoryImpl implements GLAccount
final Long tagId = command.longValueOfParameterNamed(GLAccountJsonInputParams.TAGID.getValue());
final Long type = command.longValueOfParameterNamed(GLAccountJsonInputParams.TYPE.getValue());
GLAccountType accountType = GLAccountType.fromInt(type.intValue());
if(tagId != null) {
glAccountTagType = retrieveTagId(tagId, accountType);
}
if (tagId != null) {
glAccountTagType = retrieveTagId(tagId, accountType);
}
final GLAccount glAccount = GLAccount.fromJson(parentGLAccount, command, glAccountTagType);
this.glAccountRepository.saveAndFlush(glAccount);
@@ -104,9 +104,7 @@ public class GLAccountWritePlatformServiceJpaRepositoryImpl implements GLAccount
accountCommand.validateForUpdate();
final Long parentId = command.longValueOfParameterNamed(GLAccountJsonInputParams.PARENT_ID.getValue());
if (glAccountId.equals(parentId)) {
throw new InvalidParentGLAccountHeadException(glAccountId,parentId);
}
if (glAccountId.equals(parentId)) { throw new InvalidParentGLAccountHeadException(glAccountId, parentId); }
// is the glAccount valid
final GLAccount glAccount = this.glAccountRepository.findOne(glAccountId);
if (glAccount == null) { throw new GLAccountNotFoundException(glAccountId); }
@@ -118,15 +116,15 @@ public class GLAccountWritePlatformServiceJpaRepositoryImpl implements GLAccount
GLAccount parentAccount = validateParentGLAccount(parentId);
glAccount.updateParentAccount(parentAccount);
}
if (changesOnly.containsKey(GLAccountJsonInputParams.TAGID.getValue())) {
final Long tagIdLongValue = command.longValueOfParameterNamed(GLAccountJsonInputParams.TAGID.getValue());
final GLAccountType accountType = GLAccountType.fromInt(glAccount.getType());
CodeValue tagID = null;
if (tagIdLongValue != null) {
tagID = retrieveTagId(tagIdLongValue, accountType);
}
glAccount.updateTagId(tagID);
final Long tagIdLongValue = command.longValueOfParameterNamed(GLAccountJsonInputParams.TAGID.getValue());
final GLAccountType accountType = GLAccountType.fromInt(glAccount.getType());
CodeValue tagID = null;
if (tagIdLongValue != null) {
tagID = retrieveTagId(tagIdLongValue, accountType);
}
glAccount.updateTagId(tagID);
}
/**
@@ -180,11 +178,11 @@ public class GLAccountWritePlatformServiceJpaRepositoryImpl implements GLAccount
*/
private GLAccount validateParentGLAccount(final Long parentAccountId) {
GLAccount parentGLAccount = null;
if (parentAccountId != null){
parentGLAccount = this.glAccountRepository.findOne(parentAccountId);
if (parentGLAccount == null) { throw new GLAccountNotFoundException(parentAccountId); }
// ensure parent is not a detail account
if (parentGLAccount.isDetailAccount()) { throw new GLAccountInvalidParentException(parentAccountId); }
if (parentAccountId != null) {
parentGLAccount = this.glAccountRepository.findOne(parentAccountId);
if (parentGLAccount == null) { throw new GLAccountNotFoundException(parentAccountId); }
// ensure parent is not a detail account
if (parentGLAccount.isDetailAccount()) { throw new GLAccountInvalidParentException(parentAccountId); }
}
return parentGLAccount;
}
@@ -204,26 +202,26 @@ public class GLAccountWritePlatformServiceJpaRepositoryImpl implements GLAccount
throw new PlatformDataIntegrityException("error.msg.glAccount.unknown.data.integrity.issue",
"Unknown data integrity issue with resource GL Account: " + realCause.getMessage());
}
private CodeValue retrieveTagId(Long tagId, GLAccountType accountType) {
CodeValue glAccountTagType = null;
if (accountType.isAssetType()) {
glAccountTagType = this.codeValueRepositoryWrapper.findOneByCodeNameAndIdWithNotFoundDetection(
AccountingConstants.ASSESTS_TAG_OPTION_CODE_NAME, tagId);
} else if (accountType.isLiabilityType()) {
glAccountTagType = this.codeValueRepositoryWrapper.findOneByCodeNameAndIdWithNotFoundDetection(
AccountingConstants.LIABILITIES_TAG_OPTION_CODE_NAME, tagId);
} else if (accountType.isEquityType()) {
glAccountTagType = this.codeValueRepositoryWrapper.findOneByCodeNameAndIdWithNotFoundDetection(
AccountingConstants.EQUITY_TAG_OPTION_CODE_NAME, tagId);
} else if (accountType.isIncomeType()) {
glAccountTagType = this.codeValueRepositoryWrapper.findOneByCodeNameAndIdWithNotFoundDetection(
AccountingConstants.INCOME_TAG_OPTION_CODE_NAME, tagId);
} else if (accountType.isExpenseType()) {
glAccountTagType = this.codeValueRepositoryWrapper.findOneByCodeNameAndIdWithNotFoundDetection(
AccountingConstants.EXPENSES_TAG_OPTION_CODE_NAME, tagId);
}
return glAccountTagType;
CodeValue glAccountTagType = null;
if (accountType.isAssetType()) {
glAccountTagType = this.codeValueRepositoryWrapper.findOneByCodeNameAndIdWithNotFoundDetection(
AccountingConstants.ASSESTS_TAG_OPTION_CODE_NAME, tagId);
} else if (accountType.isLiabilityType()) {
glAccountTagType = this.codeValueRepositoryWrapper.findOneByCodeNameAndIdWithNotFoundDetection(
AccountingConstants.LIABILITIES_TAG_OPTION_CODE_NAME, tagId);
} else if (accountType.isEquityType()) {
glAccountTagType = this.codeValueRepositoryWrapper.findOneByCodeNameAndIdWithNotFoundDetection(
AccountingConstants.EQUITY_TAG_OPTION_CODE_NAME, tagId);
} else if (accountType.isIncomeType()) {
glAccountTagType = this.codeValueRepositoryWrapper.findOneByCodeNameAndIdWithNotFoundDetection(
AccountingConstants.INCOME_TAG_OPTION_CODE_NAME, tagId);
} else if (accountType.isExpenseType()) {
glAccountTagType = this.codeValueRepositoryWrapper.findOneByCodeNameAndIdWithNotFoundDetection(
AccountingConstants.EXPENSES_TAG_OPTION_CODE_NAME, tagId);
}
return glAccountTagType;
}
}
@@ -63,6 +63,9 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
helper.createCashBasedJournalEntriesAndReversalsForLoan(office, CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF,
CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
principalAmount, false);
} else if (loanTransactionDTO.getTransactionType().isInitiateTransfer()
|| loanTransactionDTO.getTransactionType().isApproveTransfer()) {
createJournalEntriesForTransfers(loanDTO, loanTransactionDTO, office);
}
}
}
@@ -151,4 +154,40 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
helper.createDebitJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, paymentTypeId, loanId,
transactionId, transactionDate, totalDebitAmount, isReversal);
}
/**
* Credit loan Portfolio and Debit Suspense Account for a Transfer
* Initiation. A Transfer acceptance would be treated the opposite i.e Debit
* Loan Portfolio and Credit Suspense Account <br/>
*
* All debits are turned into credits and vice versa in case of Transfer
* Initiation disbursals
*
*
* @param loanDTO
* @param loanTransactionDTO
* @param office
*/
private void createJournalEntriesForTransfers(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO, final Office office) {
// loan properties
final Long loanProductId = loanDTO.getLoanProductId();
final Long loanId = loanDTO.getLoanId();
// transaction properties
final String transactionId = loanTransactionDTO.getTransactionId();
final Date transactionDate = loanTransactionDTO.getTransactionDate();
final BigDecimal principalAmount = loanTransactionDTO.getPrincipal();
final boolean isReversal = loanTransactionDTO.isReversed();
// final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
if (loanTransactionDTO.getTransactionType().isInitiateTransfer()) {
helper.createCashBasedJournalEntriesAndReversalsForLoan(office, CASH_ACCOUNTS_FOR_LOAN.TRANSFERS_SUSPENSE,
CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId, null, loanId, transactionId, transactionDate, principalAmount,
isReversal);
} else if (loanTransactionDTO.getTransactionType().isApproveTransfer()) {
helper.createCashBasedJournalEntriesAndReversalsForLoan(office, CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO,
CASH_ACCOUNTS_FOR_LOAN.TRANSFERS_SUSPENSE, loanProductId, null, loanId, transactionId, transactionDate,
principalAmount, isReversal);
}
}
}
@@ -167,6 +167,8 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi
CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE.getValue(), CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE.toString(), changes);
mergeLoanToAssetAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue(), CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.toString(), changes);
mergeLoanToAssetAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.TRANSFERS_SUSPENSE.getValue(), CASH_ACCOUNTS_FOR_LOAN.TRANSFERS_SUSPENSE.toString(), changes);
// income
mergeLoanToIncomeAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), loanProductId,
@@ -188,6 +190,9 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi
ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE.getValue(), ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE.toString(), changes);
mergeLoanToAssetAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue(), ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.toString(), changes);
mergeLoanToAssetAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.TRANSFERS_SUSPENSE.getValue(), ACCRUAL_ACCOUNTS_FOR_LOAN.TRANSFERS_SUSPENSE.toString(),
changes);
mergeLoanToAssetAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(),
loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE.getValue(),
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE.toString(), changes);
@@ -102,6 +102,8 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), glAccountId);
} else if (glAccountForLoan.equals(CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO)) {
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), glAccountId);
} else if (glAccountForLoan.equals(CASH_ACCOUNTS_FOR_LOAN.TRANSFERS_SUSPENSE)) {
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), glAccountId);
} else if (glAccountForLoan.equals(CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF)) {
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), glAccountId);
}
@@ -124,6 +126,8 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), glAccountId);
} else if (glAccountForLoan.equals(ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO)) {
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), glAccountId);
} else if (glAccountForLoan.equals(ACCRUAL_ACCOUNTS_FOR_LOAN.TRANSFERS_SUSPENSE)) {
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), glAccountId);
} else if (glAccountForLoan.equals(ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF)) {
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), glAccountId);
} else if (glAccountForLoan.equals(ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE)) {
@@ -63,6 +63,9 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
loanProductToGLAccountMappingHelper.saveLoanToAssetAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue());
loanProductToGLAccountMappingHelper.saveLoanToAssetAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.TRANSFERS_SUSPENSE.getValue());
// income
loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
@@ -93,6 +96,9 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
loanProductToGLAccountMappingHelper.saveLoanToAssetAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue());
loanProductToGLAccountMappingHelper.saveLoanToAssetAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.TRANSFERS_SUSPENSE.getValue());
loanProductToGLAccountMappingHelper.saveLoanToAssetAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE.getValue());
@@ -564,8 +564,20 @@ public class CommandWrapper {
return this.actionName.equalsIgnoreCase("CLOSE") && this.entityName.equalsIgnoreCase("CLIENT");
}
public boolean isClientTransfer() {
return this.actionName.equalsIgnoreCase("TRANSFER") && this.entityName.equalsIgnoreCase("CLIENT");
public boolean isProposeClientTransfer() {
return this.actionName.equalsIgnoreCase("PROPOSETRANSFER") && this.entityName.equalsIgnoreCase("CLIENT");
}
public boolean isWithdrawClientTransfer() {
return this.actionName.equalsIgnoreCase("WITHDRAWTRANSFER") && this.entityName.equalsIgnoreCase("CLIENT");
}
public boolean isAcceptClientTransfer() {
return this.actionName.equalsIgnoreCase("ACCEPTTRANSFER") && this.entityName.equalsIgnoreCase("CLIENT");
}
public boolean isRejectClientTransfer() {
return this.actionName.equalsIgnoreCase("REJECTTRANSFER") && this.entityName.equalsIgnoreCase("CLIENT");
}
public boolean isProductMixResource() {
@@ -351,13 +351,40 @@ public class CommandWrapperBuilder {
this.href = "/clients/" + clientId + "?command=close&template=true";
return this;
}
public CommandWrapperBuilder transferClient(final Long clientId) {
this.actionName = "TRANSFER";
public CommandWrapperBuilder proposeClientTransfer(final Long clientId) {
this.actionName = "PROPOSETRANSFER";
this.entityName = "CLIENT";
this.entityId = clientId;
this.clientId = clientId;
this.href = "/clientId/" + clientId + "?command=transfer";
this.href = "/clientId/" + clientId + "?command=proposeTransfer";
return this;
}
public CommandWrapperBuilder withdrawClientTransferRequest(final Long clientId) {
this.actionName = "PROPOSETRANSFER";
this.entityName = "CLIENT";
this.entityId = clientId;
this.clientId = clientId;
this.href = "/clientId/" + clientId + "?command=withdrawTransfer";
return this;
}
public CommandWrapperBuilder acceptClientTransfer(final Long clientId) {
this.actionName = "ACCEPTTRANSFER";
this.entityName = "CLIENT";
this.entityId = clientId;
this.clientId = clientId;
this.href = "/clientId/" + clientId + "?command=acceptTransfer";
return this;
}
public CommandWrapperBuilder rejectClientTransfer(final Long clientId) {
this.actionName = "REJECTTRANSFER";
this.entityName = "CLIENT";
this.entityId = clientId;
this.clientId = clientId;
this.href = "/clientId/" + clientId + "?command=rejectTransfer";
return this;
}
@@ -1009,7 +1036,7 @@ public class CommandWrapperBuilder {
this.href = "/groups/" + groupId + "?command=disassociateClients";
return this;
}
public CommandWrapperBuilder transferClientsBetweenGroups(final Long sourceGroupId) {
this.actionName = "TRANSFERCLIENTS";
this.entityName = "GROUP";
@@ -1253,7 +1280,7 @@ public class CommandWrapperBuilder {
this.href = "/" + supportedEntityType + "/" + supportedEntityId + "/meetings/" + meetingId;
return this;
}
public CommandWrapperBuilder saveOrUpdateAttendance(final Long entityId, final String supportedEntityType, final Long supportedEntityId) {
this.actionName = "SAVEORUPDATEATTENDANCE";
this.entityName = "MEETING";
@@ -170,8 +170,14 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
handler = this.applicationContext.getBean("assignClientStaffCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isClientClose()) {
handler = this.applicationContext.getBean("closeClientCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isClientTransfer()) {
handler = this.applicationContext.getBean("transferClientBetweenBranchesCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isProposeClientTransfer()) {
handler = this.applicationContext.getBean("proposeClientTransferCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isWithdrawClientTransfer()) {
handler = this.applicationContext.getBean("withdrawClientTransferCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isAcceptClientTransfer()) {
handler = this.applicationContext.getBean("acceptClientTransferCommandHandler", NewCommandSourceHandler.class);
} else if (wrapper.isRejectClientTransfer()) {
handler = this.applicationContext.getBean("rejectClientTransferCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
@@ -207,13 +207,22 @@ public class ClientsApiResource {
} else if (is(commandParam, "close")) {
commandRequest = builder.closeClient(clientId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
}else if (is(commandParam, "transfer")) {
commandRequest = builder.transferClient(clientId).build();
} else if (is(commandParam, "proposeTransfer")) {
commandRequest = builder.proposeClientTransfer(clientId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (is(commandParam, "withdrawTransfer")) {
commandRequest = builder.withdrawClientTransferRequest(clientId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (is(commandParam, "acceptTransfer")) {
commandRequest = builder.acceptClientTransfer(clientId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (is(commandParam, "rejectTransfer")) {
commandRequest = builder.rejectClientTransfer(clientId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
}
if (result == null) { throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate",
"unassignStaff", "assignStaff" }); }
"unassignStaff", "assignStaff", "close", "proposeTransfer", "withdrawTransfer", "acceptTransfer", "rejectTransfer" }); }
return this.toApiJsonSerializer.serialize(result);
}
@@ -252,6 +252,10 @@ public final class Client extends AbstractPersistable<Long> {
public boolean isActive() {
return ClientStatus.fromInt(this.status).isActive();
}
public boolean isTransferInProgress () {
return ClientStatus.fromInt(this.status).isTransferInProgress();
}
public boolean isNotPending() {
return !isPending();
@@ -472,6 +476,10 @@ public final class Client extends AbstractPersistable<Long> {
public Integer getStatus() {
return this.status;
}
public void setStatus(Integer status) {
this.status = status;
}
public boolean isActivatedAfter(final LocalDate submittedOn) {
return getActivationLocalDate().isAfter(submittedOn);
@@ -29,6 +29,16 @@ public class ClientEnumerations {
case CLOSED:
optionData = new EnumOptionData(ClientStatus.CLOSED.getValue().longValue(), ClientStatus.CLOSED.getCode(), "Closed");
break;
case TRANSFER_IN_PROGRESS:
optionData = new EnumOptionData(ClientStatus.TRANSFER_IN_PROGRESS.getValue().longValue(),
ClientStatus.TRANSFER_IN_PROGRESS.getCode(), "Transfer in progress");
break;
case TRANSFER_ON_HOLD:
optionData = new EnumOptionData(ClientStatus.TRANSFER_ON_HOLD.getValue().longValue(),
ClientStatus.TRANSFER_ON_HOLD.getCode(), "Transfer on hold");
break;
default:
break;
}
return optionData;
@@ -13,6 +13,8 @@ public enum ClientStatus {
INVALID(0, "clientStatusType.invalid"), //
PENDING(100, "clientStatusType.pending"), //
ACTIVE(300, "clientStatusType.active"), //
TRANSFER_IN_PROGRESS(303, "clientStatusType.transfer.in.progress"), //
TRANSFER_ON_HOLD(304, "clientStatusType.transfer.on.hold"), //
CLOSED(600, "clientStatusType.closed");
private final Integer value;
@@ -27,6 +29,12 @@ public enum ClientStatus {
break;
case 300:
enumeration = ClientStatus.ACTIVE;
break;
case 303:
enumeration = ClientStatus.TRANSFER_IN_PROGRESS;
break;
case 304:
enumeration = ClientStatus.TRANSFER_ON_HOLD;
break;
case 600:
enumeration = ClientStatus.CLOSED;
@@ -63,4 +71,16 @@ public enum ClientStatus {
public boolean isClosed() {
return this.value.equals(ClientStatus.CLOSED.getValue());
}
public boolean isTransferInProgress() {
return this.value.equals(ClientStatus.TRANSFER_IN_PROGRESS.getValue());
}
public boolean isTransferOnHold() {
return this.value.equals(ClientStatus.TRANSFER_ON_HOLD.getValue());
}
public boolean isUnderTransfer() {
return isTransferInProgress() || isTransferOnHold();
}
}
@@ -0,0 +1,12 @@
package org.mifosplatform.portfolio.client.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class ClientNotAwaitingTransferApprovalException extends AbstractPlatformDomainRuleException {
public ClientNotAwaitingTransferApprovalException(final Long clientId) {
super("error.msg.client.not.awaiting.transfer.approval.exception",
"The Client with id `" + clientId + "` is not awaiting transfer", clientId);
}
}
@@ -38,6 +38,7 @@ import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.staff.domain.Staff;
import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.client.domain.ClientStatus;
import org.mifosplatform.portfolio.group.api.GroupingTypesApiConstants;
import org.mifosplatform.portfolio.group.exception.ClientExistInGroupException;
import org.mifosplatform.portfolio.group.exception.ClientNotInGroupException;
@@ -385,6 +386,10 @@ public final class Group extends AbstractPersistable<Long> {
return this.groupLevel.isCenter();
}
public boolean isTransferInProgress () {
return GroupingTypeStatus.fromInt(this.status).isTransferInProgress();
}
public boolean isChildClient(final Long clientId){
if (clientId != null && this.clientMembers != null && !this.clientMembers.isEmpty()) {
for (Client client : this.clientMembers) {
@@ -13,6 +13,8 @@ public enum GroupingTypeStatus {
INVALID(0, "groupingStatusType.invalid"), //
PENDING(100, "groupingStatusType.pending"), //
ACTIVE(300, "groupingStatusType.active"), //
TRANSFER_IN_PROGRESS(303, "clientStatusType.transfer.in.progress"), //
TRANSFER_ON_HOLD(304, "clientStatusType.transfer.on.hold"), //
CLOSED(600, "groupingStatusType.closed");
private final Integer value;
@@ -63,4 +65,16 @@ public enum GroupingTypeStatus {
public boolean isClosed() {
return this.value.equals(GroupingTypeStatus.CLOSED.getValue());
}
public boolean isTransferInProgress() {
return this.isTransferInProgress();
}
public boolean isTransferOnHold() {
return this.isTransferOnHold();
}
public boolean isUnderTransfer() {
return isTransferInProgress() || isTransferOnHold();
}
}
@@ -25,6 +25,10 @@ public class LoanTransactionEnumData {
private final boolean applyCharges;
private final boolean writeOff;
private final boolean recoveryRepayment;
private final boolean initiateTransfer;
private final boolean approveTransfer;
private final boolean withdrawTransfer;
private final boolean rejectTransfer;
public LoanTransactionEnumData(final Long id, final String code, final String value) {
this.id = id;
@@ -40,6 +44,10 @@ public class LoanTransactionEnumData {
this.applyInterest = Long.valueOf(11).equals(this.id);
this.writeOff = Long.valueOf(6).equals(this.id);
this.recoveryRepayment = Long.valueOf(8).equals(this.id);
this.initiateTransfer = Long.valueOf(12).equals(this.id);
this.approveTransfer = Long.valueOf(13).equals(this.id);
this.withdrawTransfer = Long.valueOf(14).equals(this.id);
this.rejectTransfer = Long.valueOf(15).equals(this.id);
}
public Long id() {
@@ -93,4 +101,20 @@ public class LoanTransactionEnumData {
return this.applyInterest;
}
public boolean isInitiateTransfer() {
return this.initiateTransfer;
}
public boolean isApproveTransfer() {
return this.approveTransfer;
}
public boolean isWithdrawTransfer() {
return this.withdrawTransfer;
}
public boolean isRejectTransfer() {
return this.rejectTransfer;
}
}
@@ -1281,7 +1281,7 @@ public class Loan extends AbstractPersistable<Long> {
return actualChanges;
}
private Collection<Long> findExistingTransactionIds() {
public Collection<Long> findExistingTransactionIds() {
final Collection<Long> ids = new ArrayList<Long>();
@@ -1292,7 +1292,7 @@ public class Loan extends AbstractPersistable<Long> {
return ids;
}
private Collection<Long> findExistingReversedTransactionIds() {
public Collection<Long> findExistingReversedTransactionIds() {
final Collection<Long> ids = new ArrayList<Long>();
@@ -2011,7 +2011,7 @@ public class Loan extends AbstractPersistable<Long> {
return hasDisbursementTransaction();
}
private boolean isClosed() {
public boolean isClosed() {
return status().isClosed() || isCancelled();
}
@@ -2578,6 +2578,14 @@ public class Loan extends AbstractPersistable<Long> {
public Integer getTermPeriodFrequencyType() {
return this.termPeriodFrequencyType;
}
public List<LoanTransaction> getLoanTransactions() {
return this.loanTransactions;
}
public void setLoanStatus(Integer loanStatus) {
this.loanStatus = loanStatus;
}
public void validateExpectedDisbursementForHolidayAndNonWorkingDay(final WorkingDays workingDays,
final boolean allowTransactionsOnHoliday, final List<Holiday> holidays, final boolean allowTransactionsOnNonWorkingDay) {
@@ -52,7 +52,7 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
public static final String FIND_ACTIVE_LOANS_PRODUCT_IDS_BY_GROUP = "Select loan.loanProduct.id from Loan loan where "
+ "loan.group.id = :groupId and loan.loanStatus = :loanStatus and loan.client.id is NULL group by loan.loanProduct.id";
public static final String DOES_CLIENT_HAVE_ACTIVE_LOANS = "select case when (count (loan) > 0) then true else false end from Loan loan where loan.client.id = :clientId and loan.loanStatus in (100,200,300)";
public static final String DOES_CLIENT_HAVE_ACTIVE_LOANS = "select case when (count (loan) > 0) then true else false end from Loan loan where loan.client.id = :clientId and loan.loanStatus in (100,200,300,303,304)";
@Query(FIND_GROUP_LOANS_DISBURSED_AFTER)
List<Loan> getGroupLoansDisbursedAfter(@Param("disbursementDate") Date disbursementDate, @Param("groupId") Long groupId,
@@ -14,6 +14,8 @@ public enum LoanStatus {
SUBMITTED_AND_PENDING_APPROVAL(100, "loanStatusType.submitted.and.pending.approval"), //
APPROVED(200, "loanStatusType.approved"), //
ACTIVE(300, "loanStatusType.active"), //
TRANSFER_IN_PROGRESS(303, "loanStatusType.transfer.in.progress"), //
TRANSFER_ON_HOLD(304, "loanStatusType.transfer.on.hold"), //
WITHDRAWN_BY_CLIENT(400, "loanStatusType.withdrawn.by.client"), //
REJECTED(500, "loanStatusType.rejected"), //
CLOSED_OBLIGATIONS_MET(600, "loanStatusType.closed.obligations.met"), //
@@ -37,6 +39,12 @@ public enum LoanStatus {
case 300:
enumeration = LoanStatus.ACTIVE;
break;
case 303:
enumeration = LoanStatus.TRANSFER_IN_PROGRESS;
break;
case 304:
enumeration = LoanStatus.TRANSFER_ON_HOLD;
break;
case 400:
enumeration = LoanStatus.WITHDRAWN_BY_CLIENT;
break;
@@ -115,4 +123,16 @@ public enum LoanStatus {
public boolean isActiveOrAwaitingApprovalOrDisbursal() {
return this.isApproved() || this.isSubmittedAndPendingApproval() || this.isActive();
}
public boolean isTransferInProgress() {
return this.isTransferInProgress();
}
public boolean isTransferOnHold() {
return this.isTransferOnHold();
}
public boolean isUnderTransfer() {
return isTransferInProgress() || isTransferOnHold();
}
}
@@ -137,6 +137,32 @@ public final class LoanSummary {
return this.totalInterestCharged;
}
public BigDecimal getTotalPrincipalOutstanding() {
return this.totalPrincipalOutstanding;
}
public BigDecimal getTotalInterestOutstanding() {
return this.totalInterestOutstanding;
}
public BigDecimal getTotalFeeChargesOutstanding() {
return this.totalFeeChargesOutstanding;
}
public BigDecimal getTotalPenaltyChargesOutstanding() {
return this.totalPenaltyChargesOutstanding;
}
public BigDecimal getTotalOutstanding() {
return this.totalOutstanding;
}
/**
* All fields but <code>totalFeeChargesDueAtDisbursement</code> should be
* reset.
@@ -123,6 +123,20 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
return new LoanTransaction(loan, office, LoanTransactionType.APPLY_INTEREST, amount.getAmount(), interestAppliedDate, null);
}
public static LoanTransaction initiateTransfer(final Office office, final Loan loan, final LocalDate transferDate) {
return new LoanTransaction(loan, office, LoanTransactionType.INITIATE_TRANSFER.getValue(), transferDate.toDateMidnight().toDate(),
loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(), loan.getSummary()
.getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(), loan.getSummary()
.getTotalPenaltyChargesOutstanding(), false, null);
}
public static LoanTransaction approveTransfer(final Office office, final Loan loan, final LocalDate transferDate) {
return new LoanTransaction(loan, office, LoanTransactionType.APPROVE_TRANSFER.getValue(), transferDate.toDateMidnight().toDate(),
loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(), loan.getSummary()
.getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(), loan.getSummary()
.getTotalPenaltyChargesOutstanding(), false, null);
}
public static LoanTransaction copyTransactionProperties(LoanTransaction loanTransaction) {
return new LoanTransaction(loanTransaction.loan, loanTransaction.office, loanTransaction.typeOf, loanTransaction.dateOf,
loanTransaction.amount, loanTransaction.principalPortion, loanTransaction.interestPortion,
@@ -22,7 +22,17 @@ public enum LoanTransactionType {
RECOVERY_REPAYMENT(8, "loanTransactionType.recoveryRepayment"), //
WAIVE_CHARGES(9, "loanTransactionType.waiveCharges"), //
APPLY_CHARGES(10, "loanTransactionType.applyCharges"), //
APPLY_INTEREST(11, "loanTransactionType.applyInterest");
APPLY_INTEREST(11, "loanTransactionType.applyInterest"),
/***
* A Loan Transfer involves two steps, first a "initiate" Loan transfer
* transaction done by the Source branch followed by a "complete" loan
* transaction initiated by the destination branch
**/
INITIATE_TRANSFER(12, "loanTransactionType.initiateTransfer"), //
APPROVE_TRANSFER(13, "loanTransactionType.approveTransfer"), //
WITHDRAW_TRANSFER(14, "loanTransactionType.withdrawTransfer"), //
REJECT_TRANSFER(15, "loanTransactionType.rejectTransfer");
private final Integer value;
private final String code;
@@ -79,6 +89,18 @@ public enum LoanTransactionType {
case 11:
loanTransactionType = LoanTransactionType.APPLY_INTEREST;
break;
case 12:
loanTransactionType = LoanTransactionType.INITIATE_TRANSFER;
break;
case 13:
loanTransactionType = LoanTransactionType.APPROVE_TRANSFER;
break;
case 14:
loanTransactionType = LoanTransactionType.WITHDRAW_TRANSFER;
break;
case 15:
loanTransactionType = LoanTransactionType.REJECT_TRANSFER;
break;
default:
loanTransactionType = LoanTransactionType.INVALID;
break;
@@ -8,23 +8,25 @@ package org.mifosplatform.portfolio.loanaccount.service;
import java.util.Collection;
import java.util.Map;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.portfolio.calendar.domain.Calendar;
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
import org.mifosplatform.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand;
import org.mifosplatform.portfolio.collectionsheet.command.CollectionSheetBulkRepaymentCommand;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
public interface LoanWritePlatformService {
CommandProcessingResult disburseLoan(Long loanId, JsonCommand command);
Map<String, Object> bulkLoanDisbursal(JsonCommand command, CollectionSheetBulkDisbursalCommand bulkDisbursalCommand);
CommandProcessingResult undoLoanDisbursal(Long loanId, JsonCommand command);
CommandProcessingResult makeLoanRepayment(Long loanId, JsonCommand command);
Map<String, Object> makeLoanBulkRepayment(CollectionSheetBulkRepaymentCommand bulkRepaymentCommand);
CommandProcessingResult adjustLoanTransaction(Long loanId, Long transactionId, JsonCommand command);
@@ -50,8 +52,16 @@ public interface LoanWritePlatformService {
CommandProcessingResult bulkLoanReassignment(JsonCommand command);
CommandProcessingResult removeLoanOfficer(Long loanId, JsonCommand command);
void applyMeetingDateChanges(Calendar calendar, Collection<CalendarInstance> loanCalendarInstances);
void applyHolidaysToLoans();
LoanTransaction initiateLoanTransfer(Long accountId, LocalDate TransferDate);
LoanTransaction acceptLoanTransfer(Long accountId, LocalDate TransferDate);
LoanTransaction withdrawLoanTransfer(Long accountId, LocalDate TransferDate);
LoanTransaction rejectLoanTransfer(Long accountId, LocalDate TransferDate);
}
@@ -51,6 +51,7 @@ import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeWaivedExce
import org.mifosplatform.portfolio.charge.exception.LoanChargeNotFoundException;
import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.client.exception.ClientNotActiveException;
import org.mifosplatform.portfolio.client.exception.ClientNotAwaitingTransferApprovalException;
import org.mifosplatform.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand;
import org.mifosplatform.portfolio.collectionsheet.command.CollectionSheetBulkRepaymentCommand;
import org.mifosplatform.portfolio.collectionsheet.command.SingleDisbursalCommand;
@@ -892,6 +893,64 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
return loanCharge;
}
@Transactional
@Override
public LoanTransaction initiateLoanTransfer(final Long accountId, final LocalDate TransferDate) {
final Loan loan = this.loanAssembler.assembleFrom(accountId);
checkClientOrGroupActive(loan);
final List<Long> existingTransactionIds = new ArrayList<Long>(loan.findExistingTransactionIds());
final List<Long> existingReversedTransactionIds = new ArrayList<Long>(loan.findExistingReversedTransactionIds());
final LoanTransaction newTransferTransaction = LoanTransaction.initiateTransfer(loan.getOffice(), loan, TransferDate);
loan.getLoanTransactions().add(newTransferTransaction);
loan.setLoanStatus(LoanStatus.TRANSFER_IN_PROGRESS.getValue());
this.loanTransactionRepository.save(newTransferTransaction);
this.loanRepository.save(loan);
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
return newTransferTransaction;
}
@Transactional
@Override
public LoanTransaction acceptLoanTransfer(final Long accountId, final LocalDate TransferDate) {
final Loan loan = this.loanAssembler.assembleFrom(accountId);
validateClientOrGroupAwaitingTransferAcceptance(loan);
final List<Long> existingTransactionIds = new ArrayList<Long>(loan.findExistingTransactionIds());
final List<Long> existingReversedTransactionIds = new ArrayList<Long>(loan.findExistingReversedTransactionIds());
final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.approveTransfer(loan.getOffice(), loan, TransferDate);
loan.getLoanTransactions().add(newTransferAcceptanceTransaction);
loan.setLoanStatus(LoanStatus.ACTIVE.getValue());
this.loanTransactionRepository.save(newTransferAcceptanceTransaction);
this.loanRepository.save(loan);
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
return newTransferAcceptanceTransaction;
}
@Transactional
@Override
public LoanTransaction withdrawLoanTransfer(Long accountId, LocalDate TransferDate) {
// TODO Auto-generated method stub
return null;
}
@Transactional
@Override
public LoanTransaction rejectLoanTransfer(Long accountId, LocalDate TransferDate) {
// TODO Auto-generated method stub
return null;
}
@Transactional
@Override
public CommandProcessingResult loanReassignment(final Long loanId, final JsonCommand command) {
@@ -1256,4 +1315,16 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (group.isNotActive()) { throw new GroupNotActiveException(group.getId()); }
}
}
private void validateClientOrGroupAwaitingTransferAcceptance(final Loan loan) {
final Client client = loan.client();
if (client != null) {
if (!client.isTransferInProgress()) { throw new ClientNotAwaitingTransferApprovalException(client.getId()); }
}
final Group group = loan.group();
if (group != null) {
if (!group.isTransferInProgress()) { throw new ClientNotAwaitingTransferApprovalException(group.getId()); }
}
}
}
@@ -38,17 +38,17 @@ public final class LoanProductDataValidator {
/**
* The parameters supported for this command.
*/
private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("locale", "dateFormat", "name", "description", "fundId",
"currencyCode", "digitsAfterDecimal","inMultiplesOf", "principal", "minPrincipal", "maxPrincipal", "repaymentEvery", "numberOfRepayments",
"minNumberOfRepayments", "maxNumberOfRepayments", "repaymentFrequencyType", "interestRatePerPeriod",
private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("locale", "dateFormat", "name", "description",
"fundId", "currencyCode", "digitsAfterDecimal", "inMultiplesOf", "principal", "minPrincipal", "maxPrincipal", "repaymentEvery",
"numberOfRepayments", "minNumberOfRepayments", "maxNumberOfRepayments", "repaymentFrequencyType", "interestRatePerPeriod",
"minInterestRatePerPeriod", "maxInterestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType",
"interestCalculationPeriodType", "inArrearsTolerance", "transactionProcessingStrategyId", "graceOnPrincipalPayment",
"graceOnInterestPayment", "graceOnInterestCharged", "charges", "accountingRule","includeInBorrowerCycle", "startDate", "closeDate", "externalId",
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(),
"graceOnInterestPayment", "graceOnInterestCharged", "charges", "accountingRule", "includeInBorrowerCycle", "startDate",
"closeDate", "externalId", LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEE_INCOME_ACCOUNT_MAPPING.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTY_INCOME_ACCOUNT_MAPPING.getValue()));
@@ -81,9 +81,10 @@ public final class LoanProductDataValidator {
final Long fundId = fromApiJsonHelper.extractLongNamed("fundId", element);
baseDataValidator.reset().parameter("fundId").value(fundId).ignoreIfNull().integerGreaterThanZero();
}
final Boolean includeInBorrowerCycle = fromApiJsonHelper.extractBooleanNamed("includeInBorrowerCycle", element);
baseDataValidator.reset().parameter("includeInBorrowerCycle").value(includeInBorrowerCycle).ignoreIfNull().validateForBooleanValue();
baseDataValidator.reset().parameter("includeInBorrowerCycle").value(includeInBorrowerCycle).ignoreIfNull()
.validateForBooleanValue();
// terms
final String currencyCode = fromApiJsonHelper.extractStringNamed("currencyCode", element);
@@ -91,7 +92,7 @@ public final class LoanProductDataValidator {
final Integer digitsAfterDecimal = fromApiJsonHelper.extractIntegerNamed("digitsAfterDecimal", element, Locale.getDefault());
baseDataValidator.reset().parameter("digitsAfterDecimal").value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6);
final Integer inMultiplesOf = fromApiJsonHelper.extractIntegerNamed("inMultiplesOf", element, Locale.getDefault());
baseDataValidator.reset().parameter("inMultiplesOf").value(inMultiplesOf).ignoreIfNull().integerZeroOrGreater();
@@ -252,6 +253,11 @@ public final class LoanProductDataValidator {
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue()).value(loanPortfolioAccountId)
.notNull().integerGreaterThanZero();
final Long transfersInSuspenseAccountId = fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue())
.value(transfersInSuspenseAccountId).notNull().integerGreaterThanZero();
final Long incomeFromInterestId = fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue()).value(incomeFromInterestId)
@@ -322,10 +328,11 @@ public final class LoanProductDataValidator {
final Long fundId = fromApiJsonHelper.extractLongNamed("fundId", element);
baseDataValidator.reset().parameter("fundId").value(fundId).ignoreIfNull().integerGreaterThanZero();
}
if (fromApiJsonHelper.parameterExists("includeInBorrowerCycle", element)) {
final Boolean includeInBorrowerCycle = fromApiJsonHelper.extractBooleanNamed("includeInBorrowerCycle", element);
baseDataValidator.reset().parameter("includeInBorrowerCycle").value(includeInBorrowerCycle).ignoreIfNull().validateForBooleanValue();
baseDataValidator.reset().parameter("includeInBorrowerCycle").value(includeInBorrowerCycle).ignoreIfNull()
.validateForBooleanValue();
}
if (fromApiJsonHelper.parameterExists("currencyCode", element)) {
@@ -337,7 +344,7 @@ public final class LoanProductDataValidator {
final Integer digitsAfterDecimal = fromApiJsonHelper.extractIntegerNamed("digitsAfterDecimal", element, Locale.getDefault());
baseDataValidator.reset().parameter("digitsAfterDecimal").value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6);
}
if (fromApiJsonHelper.parameterExists("inMultiplesOf", element)) {
final Integer inMultiplesOf = fromApiJsonHelper.extractIntegerNamed("inMultiplesOf", element, Locale.getDefault());
baseDataValidator.reset().parameter("inMultiplesOf").value(inMultiplesOf).ignoreIfNull().integerZeroOrGreater();
@@ -478,6 +485,11 @@ public final class LoanProductDataValidator {
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue()).value(loanPortfolioAccountId)
.ignoreIfNull().integerGreaterThanZero();
final Long transfersInSuspenseAccountId = fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue())
.value(transfersInSuspenseAccountId).ignoreIfNull().integerGreaterThanZero();
final Long incomeFromInterestId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(),
element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue()).value(incomeFromInterestId)
@@ -16,7 +16,7 @@ import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodM
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
public class LoanEnumerations{
public class LoanEnumerations {
public static final String LOAN_TERM_FREQUENCY_TYPE = "loanTermFrequencyType";
public static final String TERM_FREQUENCY_TYPE = "termFrequencyType";
@@ -282,6 +282,24 @@ public class LoanEnumerations{
optionData = new LoanTransactionEnumData(LoanTransactionType.APPLY_INTEREST.getValue().longValue(),
LoanTransactionType.APPLY_INTEREST.getCode(), "Apply Interest");
break;
case APPROVE_TRANSFER:
optionData = new LoanTransactionEnumData(LoanTransactionType.APPROVE_TRANSFER.getValue().longValue(),
LoanTransactionType.APPROVE_TRANSFER.getCode(), "Transfer approved");
break;
case INITIATE_TRANSFER:
optionData = new LoanTransactionEnumData(LoanTransactionType.INITIATE_TRANSFER.getValue().longValue(),
LoanTransactionType.INITIATE_TRANSFER.getCode(), "Transfer initiated");
break;
case WITHDRAW_TRANSFER:
optionData = new LoanTransactionEnumData(LoanTransactionType.WITHDRAW_TRANSFER.getValue().longValue(),
LoanTransactionType.WITHDRAW_TRANSFER.getCode(), "Transfer Withdrawn");
break;
case REJECT_TRANSFER:
optionData = new LoanTransactionEnumData(LoanTransactionType.REJECT_TRANSFER.getValue().longValue(),
LoanTransactionType.REJECT_TRANSFER.getCode(), "Transfer Rejected");
break;
default:
break;
}
return optionData;
}
@@ -329,6 +347,16 @@ public class LoanEnumerations{
case OVERPAID:
optionData = new LoanStatusEnumData(LoanStatus.OVERPAID.getValue().longValue(), LoanStatus.OVERPAID.getCode(), "Overpaid");
break;
case TRANSFER_IN_PROGRESS:
optionData = new LoanStatusEnumData(LoanStatus.TRANSFER_IN_PROGRESS.getValue().longValue(),
LoanStatus.TRANSFER_IN_PROGRESS.getCode(), "Transfer in progress");
break;
case TRANSFER_ON_HOLD:
optionData = new LoanStatusEnumData(LoanStatus.TRANSFER_ON_HOLD.getValue().longValue(),
LoanStatus.TRANSFER_ON_HOLD.getCode(), "Transfer in hold");
break;
default:
break;
}
return optionData;
@@ -28,7 +28,7 @@ public class TransferApiConstants {
dateFormatParamName, destinationGroupIdParamName, clients, inheritDestinationGroupLoanOfficer, newStaffIdParamName,
transferActiveLoans));
public static final Set<String> TRANSFER_CLIENT_BETWEEN_BRANCHES_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
dateFormatParamName, destinationOfficeIdParamName, newStaffIdParamName, transferActiveLoans));
public static final Set<String> PROPOSE_CLIENT_TRANSFER_BETWEEN_BRANCHES_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(
localeParamName, dateFormatParamName, destinationOfficeIdParamName, newStaffIdParamName, transferActiveLoans));
}
@@ -80,13 +80,13 @@ public final class TransfersDataValidator {
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateForClientTransferBetweenBranches(final String json) {
public void validateForProposeClientTransferBetweenBranches(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
TransferApiConstants.TRANSFER_CLIENT_BETWEEN_BRANCHES_DATA_PARAMETERS);
TransferApiConstants.PROPOSE_CLIENT_TRANSFER_BETWEEN_BRANCHES_DATA_PARAMETERS);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
@@ -14,12 +14,12 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class TransferClientBetweenBranchesCommandHandler implements NewCommandSourceHandler {
public class AcceptClientTransferCommandHandler implements NewCommandSourceHandler {
private final TransferWritePlatformService writePlatformService;
@Autowired
public TransferClientBetweenBranchesCommandHandler(final TransferWritePlatformService writePlatformService) {
public AcceptClientTransferCommandHandler(final TransferWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@@ -27,6 +27,6 @@ public class TransferClientBetweenBranchesCommandHandler implements NewCommandSo
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.transferClientBetweenBranches(command.entityId(), command);
return this.writePlatformService.acceptClientTransfer(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.transfer.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.transfer.service.TransferWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ProposeClientTransferCommandHandler implements NewCommandSourceHandler {
private final TransferWritePlatformService writePlatformService;
@Autowired
public ProposeClientTransferCommandHandler(final TransferWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.proposeClientTransfer(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.transfer.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.transfer.service.TransferWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class RejectClientTransferCommandHandler implements NewCommandSourceHandler {
private final TransferWritePlatformService writePlatformService;
@Autowired
public RejectClientTransferCommandHandler(final TransferWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.rejectClientTransfer(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.transfer.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.transfer.service.TransferWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class WithdrawClientTransferCommandHandler implements NewCommandSourceHandler {
private final TransferWritePlatformService writePlatformService;
@Autowired
public WithdrawClientTransferCommandHandler(final TransferWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.withdrawClientTransfer(command.entityId(), command);
}
}
@@ -0,0 +1,29 @@
/**
* 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.transfer.service;
public enum TransferEventType {
PROPOSAL(1, "transferEvent.proposal"), ACCEPTANCE(2, "transferEvent.acceptance"), WITHDRAWAL(3, "transferEvent.withdrawal"), REJECTION(
4, "transferEvent.rejection");
private final Integer value;
private final String code;
private TransferEventType(final Integer value, final String code) {
this.value = value;
this.code = code;
}
public Integer getValue() {
return this.value;
}
public String getCode() {
return this.code;
}
}
@@ -12,6 +12,12 @@ public interface TransferWritePlatformService {
CommandProcessingResult transferClientsBetweenGroups(final Long sourceGroupId, final JsonCommand jsonCommand);
CommandProcessingResult transferClientBetweenBranches(final Long clientId, final JsonCommand jsonCommand);
CommandProcessingResult proposeClientTransfer(final Long clientId, final JsonCommand jsonCommand);
CommandProcessingResult withdrawClientTransfer(final Long clientId, final JsonCommand jsonCommand);
CommandProcessingResult acceptClientTransfer(final Long clientId, final JsonCommand jsonCommand);
CommandProcessingResult rejectClientTransfer(final Long clientId, final JsonCommand jsonCommand);
}
@@ -23,6 +23,7 @@ import org.mifosplatform.portfolio.calendar.domain.CalendarType;
import org.mifosplatform.portfolio.calendar.service.CalendarUtils;
import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper;
import org.mifosplatform.portfolio.client.domain.ClientStatus;
import org.mifosplatform.portfolio.client.exception.ClientHasBeenClosedException;
import org.mifosplatform.portfolio.group.domain.Group;
import org.mifosplatform.portfolio.group.domain.GroupRepositoryWrapper;
@@ -160,7 +161,8 @@ public class TransferWritePlatformServiceJpaRepositoryImpl implements TransferWr
} else {
Office destinationOffice = destinationGroup.getOffice();
transferClientToNewBranch(client, destinationOffice);
// handleClientTransfer(client, destinationOffice);
// TODO: Fill in the blanks
}
/**
@@ -208,16 +210,16 @@ public class TransferWritePlatformServiceJpaRepositoryImpl implements TransferWr
**/
@Transactional
@Override
public CommandProcessingResult transferClientBetweenBranches(final Long clientId, final JsonCommand jsonCommand) {
public CommandProcessingResult proposeClientTransfer(final Long clientId, final JsonCommand jsonCommand) {
// validation
this.transfersDataValidator.validateForClientTransferBetweenBranches(jsonCommand.json());
this.transfersDataValidator.validateForProposeClientTransferBetweenBranches(jsonCommand.json());
final Long destinationOfficeId = jsonCommand.longValueOfParameterNamed(TransferApiConstants.destinationOfficeIdParamName);
final Office office = this.officeRepository.findOneWithNotFoundDetection(destinationOfficeId);
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
/*** Remove all group associations for this client ***/
client.getGroups().clear();
transferClientToNewBranch(client, office);
handleClientTransfer(client, office, TransferEventType.PROPOSAL);
this.clientRepository.save(client);
return new CommandProcessingResultBuilder() //
@@ -226,14 +228,100 @@ public class TransferWritePlatformServiceJpaRepositoryImpl implements TransferWr
.build();
}
private void transferClientToNewBranch(final Client client, final Office destinationOffice) {
/**
* For now, do not allow Clients with active loans to be transferred
* between branches
**/
//if (loanRepository.doesClientHaveActiveLoans(client.getId())) { throw new TransferNotSupportedException(); }
// set the new office for the client
client.updateOffice(destinationOffice);
/**
* This API is meant for transferring clients between branches mainly by
* Organizations following an Individual lending Model <br/>
* If the Client is linked to any Groups, we can optionally choose to have
* all the linkages broken and all JLG Loans are converted into Individual
* Loans
*
* @param clientId
* @param destinationOfficeId
* @param jsonCommand
* @return
**/
@Transactional
@Override
public CommandProcessingResult acceptClientTransfer(final Long clientId, final JsonCommand jsonCommand) {
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
/*** Remove all group associations for this client ***/
client.getGroups().clear();
handleClientTransfer(client, client.getOffice(), TransferEventType.ACCEPTANCE);
this.clientRepository.save(client);
return new CommandProcessingResultBuilder() //
.withClientId(clientId) //
.withEntityId(clientId) //
.build();
}
@Override
@Transactional
public CommandProcessingResult withdrawClientTransfer(Long clientId, JsonCommand jsonCommand) {
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
/*** Remove all group associations for this client ***/
client.getGroups().clear();
handleClientTransfer(client, client.getOffice(), TransferEventType.WITHDRAWAL);
this.clientRepository.save(client);
return new CommandProcessingResultBuilder() //
.withClientId(clientId) //
.withEntityId(clientId) //
.build();
}
@Override
@Transactional
public CommandProcessingResult rejectClientTransfer(Long clientId, JsonCommand jsonCommand) {
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
/*** Remove all group associations for this client ***/
client.getGroups().clear();
handleClientTransfer(client, client.getOffice(), TransferEventType.REJECTION);
this.clientRepository.save(client);
return new CommandProcessingResultBuilder() //
.withClientId(clientId) //
.withEntityId(clientId) //
.build();
}
private void handleClientTransfer(final Client client, final Office destinationOffice, TransferEventType transferEventType) {
if (loanRepository.doesClientHaveActiveLoans(client.getId())) {
// get each individual loan for the client
for (Loan loan : loanRepository.findLoanByClientId(client.getId())) {
if (!loan.isClosed()) {
switch (transferEventType) {
case ACCEPTANCE:
this.loanWritePlatformService.acceptLoanTransfer(loan.getId(), DateUtils.getLocalDateOfTenant());
break;
case PROPOSAL:
this.loanWritePlatformService.initiateLoanTransfer(loan.getId(), DateUtils.getLocalDateOfTenant());
break;
case REJECTION:
this.loanWritePlatformService.rejectLoanTransfer(loan.getId(), DateUtils.getLocalDateOfTenant());
break;
case WITHDRAWAL:
this.loanWritePlatformService.withdrawLoanTransfer(loan.getId(), DateUtils.getLocalDateOfTenant());
}
}
}
}
switch (transferEventType) {
case ACCEPTANCE:
client.setStatus(ClientStatus.ACTIVE.getValue());
break;
case PROPOSAL:
client.setStatus(ClientStatus.TRANSFER_IN_PROGRESS.getValue());
client.updateOffice(destinationOffice);
break;
case REJECTION:
client.setStatus(ClientStatus.TRANSFER_ON_HOLD.getValue());
break;
// TODO update client with previous office
case WITHDRAWAL:
client.setStatus(ClientStatus.ACTIVE.getValue());
}
}
private List<Client> assembleListOfClients(final JsonCommand command) {