adding an api workflow for transfers
This commit is contained in:
@@ -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);
|
||||
+7
-5
@@ -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;
|
||||
|
||||
|
||||
+38
-40
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+39
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -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);
|
||||
|
||||
+4
@@ -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)) {
|
||||
|
||||
+6
@@ -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());
|
||||
|
||||
+14
-2
@@ -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() {
|
||||
|
||||
+33
-6
@@ -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";
|
||||
|
||||
+8
-2
@@ -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());
|
||||
}
|
||||
|
||||
+12
-3
@@ -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);
|
||||
|
||||
+10
@@ -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;
|
||||
|
||||
+20
@@ -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();
|
||||
}
|
||||
}
|
||||
+12
@@ -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) {
|
||||
|
||||
+14
@@ -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();
|
||||
}
|
||||
}
|
||||
+24
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+11
-3
@@ -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) {
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
+20
@@ -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();
|
||||
}
|
||||
}
|
||||
+26
@@ -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.
|
||||
|
||||
+14
@@ -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,
|
||||
|
||||
+23
-1
@@ -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;
|
||||
|
||||
+14
-4
@@ -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);
|
||||
}
|
||||
+71
@@ -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()); }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+27
-15
@@ -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)
|
||||
|
||||
+29
-1
@@ -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;
|
||||
|
||||
+2
-2
@@ -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));
|
||||
|
||||
}
|
||||
+2
-2
@@ -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>();
|
||||
|
||||
+3
-3
@@ -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);
|
||||
}
|
||||
}
|
||||
+32
@@ -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);
|
||||
}
|
||||
}
|
||||
+32
@@ -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);
|
||||
}
|
||||
}
|
||||
+32
@@ -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);
|
||||
}
|
||||
}
|
||||
+29
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+7
-1
@@ -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);
|
||||
|
||||
}
|
||||
+100
-12
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user