diff --git a/mifosng-db/migrations/core_db/V106__more_permissions_for_transfers.sql b/mifosng-db/migrations/core_db/V106__more_permissions_for_transfers.sql
new file mode 100644
index 000000000..3b99dea4e
--- /dev/null
+++ b/mifosng-db/migrations/core_db/V106__more_permissions_for_transfers.sql
@@ -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);
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/common/AccountingConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/common/AccountingConstants.java
index 35de2586a..c6afeccab 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/common/AccountingConstants.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/common/AccountingConstants.java
@@ -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;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/service/GLAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/service/GLAccountWritePlatformServiceJpaRepositoryImpl.java
index 6b975c89b..fddfb937c 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/service/GLAccountWritePlatformServiceJpaRepositoryImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/service/GLAccountWritePlatformServiceJpaRepositoryImpl.java
@@ -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;
}
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java
index 0eca865cd..e2f69f4df 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java
@@ -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
+ *
+ * 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);
+ }
+ }
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/LoanProductToGLAccountMappingHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/LoanProductToGLAccountMappingHelper.java
index 1dcb5ca36..e3b12c28f 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/LoanProductToGLAccountMappingHelper.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/LoanProductToGLAccountMappingHelper.java
@@ -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);
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java
index c4bf1d4aa..392538403 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java
@@ -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)) {
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformServiceImpl.java
index 2feeb8706..c95e4133a 100755
--- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformServiceImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformServiceImpl.java
@@ -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());
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java
index 90ff1c732..effa71a75 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java
@@ -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() {
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java
index 49d7838ad..a2ea424f6 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java
@@ -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";
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java
index b8822b31c..2dae7c32a 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java
@@ -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());
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientsApiResource.java
index 4fc537a27..beaa7792b 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientsApiResource.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientsApiResource.java
@@ -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);
}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java
index aaf9fca4b..d033c0dca 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java
@@ -252,6 +252,10 @@ public final class Client extends AbstractPersistable {
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 {
public Integer getStatus() {
return this.status;
}
+
+ public void setStatus(Integer status) {
+ this.status = status;
+ }
public boolean isActivatedAfter(final LocalDate submittedOn) {
return getActivationLocalDate().isAfter(submittedOn);
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientEnumerations.java
index 40aa1fb38..bbffdfcfa 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientEnumerations.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientEnumerations.java
@@ -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;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientStatus.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientStatus.java
index 47088c1a5..327db48b7 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientStatus.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientStatus.java
@@ -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();
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/exception/ClientNotAwaitingTransferApprovalException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/exception/ClientNotAwaitingTransferApprovalException.java
new file mode 100644
index 000000000..998ad1eb6
--- /dev/null
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/exception/ClientNotAwaitingTransferApprovalException.java
@@ -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);
+ }
+
+}
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/Group.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/Group.java
index 65a9915de..26e70aafe 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/Group.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/Group.java
@@ -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 {
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) {
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/GroupingTypeStatus.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/GroupingTypeStatus.java
index e0add459d..a52b1d1be 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/GroupingTypeStatus.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/GroupingTypeStatus.java
@@ -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();
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionEnumData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionEnumData.java
index b1cf8e3e2..dfec6baa5 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionEnumData.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionEnumData.java
@@ -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;
+ }
+
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java
index f1b3b9b04..3b57cb70e 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java
@@ -1281,7 +1281,7 @@ public class Loan extends AbstractPersistable {
return actualChanges;
}
- private Collection findExistingTransactionIds() {
+ public Collection findExistingTransactionIds() {
final Collection ids = new ArrayList();
@@ -1292,7 +1292,7 @@ public class Loan extends AbstractPersistable {
return ids;
}
- private Collection findExistingReversedTransactionIds() {
+ public Collection findExistingReversedTransactionIds() {
final Collection ids = new ArrayList();
@@ -2011,7 +2011,7 @@ public class Loan extends AbstractPersistable {
return hasDisbursementTransaction();
}
- private boolean isClosed() {
+ public boolean isClosed() {
return status().isClosed() || isCancelled();
}
@@ -2578,6 +2578,14 @@ public class Loan extends AbstractPersistable {
public Integer getTermPeriodFrequencyType() {
return this.termPeriodFrequencyType;
}
+
+ public List getLoanTransactions() {
+ return this.loanTransactions;
+ }
+
+ public void setLoanStatus(Integer loanStatus) {
+ this.loanStatus = loanStatus;
+ }
public void validateExpectedDisbursementForHolidayAndNonWorkingDay(final WorkingDays workingDays,
final boolean allowTransactionsOnHoliday, final List holidays, final boolean allowTransactionsOnNonWorkingDay) {
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepository.java
index 07854f45b..e868efaee 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepository.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepository.java
@@ -52,7 +52,7 @@ public interface LoanRepository extends JpaRepository, 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 getGroupLoansDisbursedAfter(@Param("disbursementDate") Date disbursementDate, @Param("groupId") Long groupId,
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanStatus.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanStatus.java
index 958a551f2..d1d8bed8d 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanStatus.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanStatus.java
@@ -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();
+ }
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummary.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummary.java
index 98ffcc58a..72812237c 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummary.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummary.java
@@ -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 totalFeeChargesDueAtDisbursement should be
* reset.
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java
index 32302df4f..b83f622fe 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java
@@ -123,6 +123,20 @@ public final class LoanTransaction extends AbstractPersistable {
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,
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransactionType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransactionType.java
index 5d2f6f2d0..f691ccda2 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransactionType.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransactionType.java
@@ -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;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java
index e410610ed..c43b665de 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java
@@ -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 bulkLoanDisbursal(JsonCommand command, CollectionSheetBulkDisbursalCommand bulkDisbursalCommand);
CommandProcessingResult undoLoanDisbursal(Long loanId, JsonCommand command);
CommandProcessingResult makeLoanRepayment(Long loanId, JsonCommand command);
-
+
Map 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 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);
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index 79791fc3e..3035d3aaf 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -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 existingTransactionIds = new ArrayList(loan.findExistingTransactionIds());
+ final List existingReversedTransactionIds = new ArrayList(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 existingTransactionIds = new ArrayList(loan.findExistingTransactionIds());
+ final List existingReversedTransactionIds = new ArrayList(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()); }
+ }
+ }
+
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java
index f195673cb..4822a87dd 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java
@@ -38,17 +38,17 @@ public final class LoanProductDataValidator {
/**
* The parameters supported for this command.
*/
- private final Set supportedParameters = new HashSet(Arrays.asList("locale", "dateFormat", "name", "description", "fundId",
- "currencyCode", "digitsAfterDecimal","inMultiplesOf", "principal", "minPrincipal", "maxPrincipal", "repaymentEvery", "numberOfRepayments",
- "minNumberOfRepayments", "maxNumberOfRepayments", "repaymentFrequencyType", "interestRatePerPeriod",
+ private final Set supportedParameters = new HashSet(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)
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java
index 6ebdea8e9..0aeecb9bb 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java
@@ -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;
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/api/TransferApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/api/TransferApiConstants.java
index 4920de273..fb0857097 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/api/TransferApiConstants.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/api/TransferApiConstants.java
@@ -28,7 +28,7 @@ public class TransferApiConstants {
dateFormatParamName, destinationGroupIdParamName, clients, inheritDestinationGroupLoanOfficer, newStaffIdParamName,
transferActiveLoans));
- public static final Set TRANSFER_CLIENT_BETWEEN_BRANCHES_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName,
- dateFormatParamName, destinationOfficeIdParamName, newStaffIdParamName, transferActiveLoans));
+ public static final Set PROPOSE_CLIENT_TRANSFER_BETWEEN_BRANCHES_DATA_PARAMETERS = new HashSet(Arrays.asList(
+ localeParamName, dateFormatParamName, destinationOfficeIdParamName, newStaffIdParamName, transferActiveLoans));
}
\ No newline at end of file
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/data/TransfersDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/data/TransfersDataValidator.java
index a71646c74..b6f7cd3d2 100644
--- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/data/TransfersDataValidator.java
+++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/data/TransfersDataValidator.java
@@ -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