update loan products api-docs and validation. some refactor at point where loan product and GLAccount mappings meet.

This commit is contained in:
Keith Woodlock
2013-01-30 16:38:41 +00:00
parent dd9179dadd
commit 07a4d7c476
14 changed files with 654 additions and 770 deletions
+17 -11
View File
@@ -1956,9 +1956,6 @@ Request Body:
"numberOfRepayments": 12,
"loanTermFrequency": 12,
"repaymentEvery": 1,
"interestRatePerPeriod": 1.75,
"annualInterestRate": 21,
"accountingType": 1,
"loanTermFrequencyType": {
"id": 2,
"code": "loanTermFrequency.periodFrequencyType.months",
@@ -1969,11 +1966,15 @@ Request Body:
"code": "repaymentFrequency.periodFrequencyType.months",
"value": "Months"
},
"transactionProcessingStrategyId": 1,
"transactionProcessingStrategyName": "Mifos style",
"interestRatePerPeriod": 1.75,
"interestRateFrequencyType": {
"id": 2,
"code": "interestRateFrequency.periodFrequencyType.months",
"value": "Per month"
},
"annualInterestRate": 21,
"amortizationType": {
"id": 0,
"code": "amortizationType.equal.principal",
@@ -1988,7 +1989,8 @@ Request Body:
"id": 1,
"code": "interestCalculationPeriodType.same.as.repayment.period",
"value": "Same as repayment period"
}
},
"accountingType": 1
}
]
</code>
@@ -2019,7 +2021,6 @@ Request Body:
"nameCode": "currency.USD",
"displayLabel": "US Dollar ($)"
},
"accountingType": 1,
"loanTermFrequencyType": {
"id": 2,
"code": "loanTermFrequency.periodFrequencyType.months",
@@ -2050,6 +2051,7 @@ Request Body:
"code": "interestCalculationPeriodType.same.as.repayment.period",
"value": "Same as repayment period"
},
"accountingType": 1,
"currencyOptions": [
{
"code": "USD",
@@ -2168,7 +2170,8 @@ Request Body:
"code": "loanTermFrequency.periodFrequencyType.years",
"value": "Years"
}
]
],
"accountingMappings": {}
}
</code>
</div>
@@ -2202,9 +2205,6 @@ Request Body:
"numberOfRepayments": 12,
"loanTermFrequency": 12,
"repaymentEvery": 1,
"interestRatePerPeriod": 1.75,
"annualInterestRate": 21,
"accountingType": 1,
"loanTermFrequencyType": {
"id": 2,
"code": "loanTermFrequency.periodFrequencyType.months",
@@ -2215,11 +2215,15 @@ Request Body:
"code": "repaymentFrequency.periodFrequencyType.months",
"value": "Months"
},
"transactionProcessingStrategyId": 1,
"transactionProcessingStrategyName": "Mifos style",
"interestRatePerPeriod": 1.75,
"interestRateFrequencyType": {
"id": 2,
"code": "interestRateFrequency.periodFrequencyType.months",
"value": "Per month"
},
"annualInterestRate": 21,
"amortizationType": {
"id": 0,
"code": "amortizationType.equal.principal",
@@ -2235,6 +2239,7 @@ Request Body:
"code": "interestCalculationPeriodType.same.as.repayment.period",
"value": "Same as repayment period"
},
"accountingType": 1,
"charges": []
}
</code>
@@ -2263,9 +2268,10 @@ Request Body:
"locale": "en_GB",
"digitsAfterDecimal": "2",
"principal": "100,000.00",
"repaymentFrequencyType": 2,
"repaymentEvery": "1",
"numberOfRepayments": "12",
"repaymentEvery": "1",
"repaymentFrequencyType": 2,
"transactionProcessingStrategyId": 1,
"interestRatePerPeriod": "1.75",
"interestRateFrequencyType": 2,
"amortizationType": 1,
@@ -293,6 +293,10 @@ INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `c
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'READ_GUARANTOR', 'GUARANTOR', 'READ', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'UPDATE_GUARANTOR', 'GUARANTOR', 'UPDATE', 1);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('organisation', 'DELETE_OFFICETRANSACTION', 'OFFICETRANSACTION', 'DELETE', 1);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('organisation', 'DELETE_OFFICETRANSACTION_CHECKER', 'OFFICETRANSACTION', 'DELETE', 0);
INSERT INTO `m_permission`
(
`grouping`,
@@ -1 +1,4 @@
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('organisation', 'DELETE_OFFICETRANSACTION', 'OFFICETRANSACTION', 'DELETE', 1);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('organisation', 'DELETE_OFFICETRANSACTION', 'OFFICETRANSACTION', 'DELETE', 1);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('organisation', 'DELETE_OFFICETRANSACTION_CHECKER',
'OFFICETRANSACTION', 'DELETE', 0);
@@ -1,40 +0,0 @@
package org.mifosplatform.accounting.api.data;
public class ProductToGLAccountMappingData {
private final Long id;
private final Long glAccountId;
private final Long productId;
private final Integer productType;
private final Integer financialAccountType;
public ProductToGLAccountMappingData(Long id, Long glAccountId, Long productId, Integer productType, Integer financialAccountType) {
super();
this.id = id;
this.glAccountId = glAccountId;
this.productId = productId;
this.productType = productType;
this.financialAccountType = financialAccountType;
}
public Long getId() {
return this.id;
}
public Long getGlAccountId() {
return this.glAccountId;
}
public Long getProductId() {
return this.productId;
}
public Integer getProductType() {
return this.productType;
}
public Integer getFinancialAccountType() {
return this.financialAccountType;
}
}
@@ -1,9 +1,8 @@
package org.mifosplatform.accounting.service;
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
import java.util.Map;
public interface ProductToGLAccountMappingReadPlatformService {
LoanProductData fetchAccountMappingDetailsForLoanProduct(LoanProductData loanProductData);
}
Map<String, Object> fetchAccountMappingDetailsForLoanProduct(final Long loanProductId, final Integer accountingType);
}
@@ -2,14 +2,13 @@ package org.mifosplatform.accounting.service;
import java.util.Map;
import org.mifosplatform.portfolio.loanproduct.command.LoanProductCommand;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
public interface ProductToGLAccountMappingWritePlatformService {
void createLoanProductToGLAccountMapping(Long loanProductId, LoanProductCommand command);
void createLoanProductToGLAccountMapping(Long loanProductId, JsonCommand command);
Map<String, Object> updateLoanProductToGLAccountMapping(Long loanProductId, LoanProductCommand command, boolean accountingRuleChanged);
Map<String, Object> updateLoanProductToGLAccountMapping(Long loanProductId, JsonCommand command, boolean accountingRuleChanged);
void deleteLoanProductToGLAccountMapping(Long loanProductId);
}
}
@@ -2,14 +2,15 @@ package org.mifosplatform.accounting.service.impl;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.mifosplatform.accounting.AccountingConstants.ACCRUAL_ACCOUNTS_FOR_LOAN;
import org.mifosplatform.accounting.AccountingConstants.CASH_ACCOUNTS_FOR_LOAN;
import org.mifosplatform.accounting.api.data.ProductToGLAccountMappingData;
import org.mifosplatform.accounting.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
import org.mifosplatform.accounting.service.ProductToGLAccountMappingReadPlatformService;
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
import org.mifosplatform.portfolio.loanproduct.domain.AccountingRuleType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -26,7 +27,7 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
private static final class LoanProductToGLAccountMappingMapper implements RowMapper<ProductToGLAccountMappingData> {
private static final class LoanProductToGLAccountMappingMapper implements RowMapper<Map<String, Object>> {
public String schema() {
return " id as id, gl_account_id as glAccountId,product_id as productId,product_type as productType,financial_account_type as financialAccountType"
@@ -34,71 +35,89 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
}
@Override
public ProductToGLAccountMappingData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
public Map<String, Object> mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
Long id = rs.getLong("id");
Long glAccountId = rs.getLong("glAccountId");
Long productId = rs.getLong("productId");
Integer productType = rs.getInt("productType");
Integer financialAccountType = rs.getInt("financialAccountType");
final Long id = rs.getLong("id");
final Long glAccountId = rs.getLong("glAccountId");
final Long productId = rs.getLong("productId");
final Integer productType = rs.getInt("productType");
final Integer financialAccountType = rs.getInt("financialAccountType");
return new ProductToGLAccountMappingData(id, glAccountId, productId, productType, financialAccountType);
final Map<String, Object> loanProductToGLAccountMap = new LinkedHashMap<String, Object>(5);
loanProductToGLAccountMap.put("id", id);
loanProductToGLAccountMap.put("glAccountId", glAccountId);
loanProductToGLAccountMap.put("productId", productId);
loanProductToGLAccountMap.put("productType", productType);
loanProductToGLAccountMap.put("financialAccountType", financialAccountType);
return loanProductToGLAccountMap;
}
}
@Override
public LoanProductData fetchAccountMappingDetailsForLoanProduct(LoanProductData loanProductData) {
LoanProductToGLAccountMappingMapper rm = new LoanProductToGLAccountMappingMapper();
String sql = "select " + rm.schema() + " and product_id = ?";
List<ProductToGLAccountMappingData> productToGLAccountMappingDatas = this.jdbcTemplate.query(sql, rm,
new Object[] { loanProductData.getId() });
public Map<String, Object> fetchAccountMappingDetailsForLoanProduct(final Long loanProductId, final Integer accountingType) {
final Map<String, Object> accountMappingDetails = new LinkedHashMap<String, Object>(8);
final LoanProductToGLAccountMappingMapper rm = new LoanProductToGLAccountMappingMapper();
final String sql = "select " + rm.schema() + " and product_id = ?";
final List<Map<String, Object>> listOfProductToGLAccountMaps = this.jdbcTemplate.query(sql, rm, new Object[] { loanProductId });
if (AccountingRuleType.CASH_BASED.getValue().equals(accountingType)) {
for (Map<String, Object> productToGLAccountMap : listOfProductToGLAccountMaps) {
final Integer financialAccountType = (Integer) productToGLAccountMap.get("financialAccountType");
CASH_ACCOUNTS_FOR_LOAN glAccountForLoan = CASH_ACCOUNTS_FOR_LOAN.fromInt(financialAccountType);
final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId");
if (loanProductData.getAccountingType() == AccountingRuleType.CASH_BASED.getValue()) {
for (ProductToGLAccountMappingData productToGLAccountMappingData : productToGLAccountMappingDatas) {
CASH_ACCOUNTS_FOR_LOAN glAccountForLoan = CASH_ACCOUNTS_FOR_LOAN.fromInt(productToGLAccountMappingData
.getFinancialAccountType());
if (glAccountForLoan.equals(CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE)) {
loanProductData.setFundSourceAccountId(productToGLAccountMappingData.getGlAccountId());
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), glAccountId);
} else if (glAccountForLoan.equals(CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES)) {
loanProductData.setIncomeFromFeeAccountId(productToGLAccountMappingData.getGlAccountId());
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), glAccountId);
} else if (glAccountForLoan.equals(CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES)) {
loanProductData.setIncomeFromPenaltyAccountId(productToGLAccountMappingData.getGlAccountId());
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), glAccountId);
} else if (glAccountForLoan.equals(CASH_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS)) {
loanProductData.setInterestOnLoanAccountId(productToGLAccountMappingData.getGlAccountId());
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), glAccountId);
} else if (glAccountForLoan.equals(CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO)) {
loanProductData.setLoanPortfolioAccountId(productToGLAccountMappingData.getGlAccountId());
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), glAccountId);
} else if (glAccountForLoan.equals(CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF)) {
loanProductData.setWriteOffAccountId(productToGLAccountMappingData.getGlAccountId());
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), glAccountId);
}
}
} else if (loanProductData.getAccountingType() == AccountingRuleType.ACCRUAL_BASED.getValue()) {
for (ProductToGLAccountMappingData productToGLAccountMappingData : productToGLAccountMappingDatas) {
ACCRUAL_ACCOUNTS_FOR_LOAN glAccountForLoan = ACCRUAL_ACCOUNTS_FOR_LOAN.fromInt(productToGLAccountMappingData
.getFinancialAccountType());
} else if (AccountingRuleType.ACCRUAL_BASED.getValue().equals(accountingType)) {
for (Map<String, Object> productToGLAccountMap : listOfProductToGLAccountMaps) {
final Integer financialAccountType = (Integer) productToGLAccountMap.get("financialAccountType");
ACCRUAL_ACCOUNTS_FOR_LOAN glAccountForLoan = ACCRUAL_ACCOUNTS_FOR_LOAN.fromInt(financialAccountType);
final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId");
if (glAccountForLoan.equals(ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE)) {
loanProductData.setFundSourceAccountId(productToGLAccountMappingData.getGlAccountId());
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), glAccountId);
} else if (glAccountForLoan.equals(ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES)) {
loanProductData.setIncomeFromFeeAccountId(productToGLAccountMappingData.getGlAccountId());
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), glAccountId);
} else if (glAccountForLoan.equals(ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES)) {
loanProductData.setIncomeFromPenaltyAccountId(productToGLAccountMappingData.getGlAccountId());
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), glAccountId);
} else if (glAccountForLoan.equals(ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS)) {
loanProductData.setInterestOnLoanAccountId(productToGLAccountMappingData.getGlAccountId());
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), glAccountId);
} else if (glAccountForLoan.equals(ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO)) {
loanProductData.setLoanPortfolioAccountId(productToGLAccountMappingData.getGlAccountId());
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), glAccountId);
} else if (glAccountForLoan.equals(ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF)) {
loanProductData.setWriteOffAccountId(productToGLAccountMappingData.getGlAccountId());
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), glAccountId);
} else if (glAccountForLoan.equals(ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE)) {
loanProductData.setReceivableInterestAccountId(productToGLAccountMappingData.getGlAccountId());
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), glAccountId);
} else if (glAccountForLoan.equals(ACCRUAL_ACCOUNTS_FOR_LOAN.FEES_RECEIVABLE)) {
loanProductData.setReceivableFeeAccountId(productToGLAccountMappingData.getGlAccountId());
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), glAccountId);
} else if (glAccountForLoan.equals(ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE)) {
loanProductData.setReceivablePenaltyAccountId(productToGLAccountMappingData.getGlAccountId());
accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), glAccountId);
}
}
}
return loanProductData;
return accountMappingDetails;
}
}
}
@@ -2,6 +2,7 @@ package org.mifosplatform.accounting.service.impl;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.mifosplatform.accounting.AccountingConstants.ACCRUAL_ACCOUNTS_FOR_LOAN;
@@ -15,168 +16,224 @@ import org.mifosplatform.accounting.domain.ProductToGLAccountMappingRepository;
import org.mifosplatform.accounting.exceptions.GLAccountNotFoundException;
import org.mifosplatform.accounting.exceptions.ProductToGLAccountMappingNotFoundException;
import org.mifosplatform.accounting.service.ProductToGLAccountMappingWritePlatformService;
import org.mifosplatform.portfolio.loanproduct.command.LoanProductCommand;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.loanproduct.domain.AccountingRuleType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.google.gson.JsonElement;
@Service
public class ProductToGLAccountMappingWritePlatformServiceImpl implements ProductToGLAccountMappingWritePlatformService {
private final GLAccountRepository accountRepository;
private final ProductToGLAccountMappingRepository accountMappingRepository;
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public ProductToGLAccountMappingWritePlatformServiceImpl(final GLAccountRepository glAccountRepository,
final ProductToGLAccountMappingRepository glAccountMappingRepository) {
final ProductToGLAccountMappingRepository glAccountMappingRepository, final FromJsonHelper fromApiJsonHelper) {
this.accountRepository = glAccountRepository;
this.accountMappingRepository = glAccountMappingRepository;
this.fromApiJsonHelper = fromApiJsonHelper;
}
@Override
@Transactional
public void createLoanProductToGLAccountMapping(Long loanProductId, LoanProductCommand command) {
AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(command.getAccountingType());
public void createLoanProductToGLAccountMapping(final Long loanProductId, final JsonCommand command) {
final JsonElement element = fromApiJsonHelper.parse(command.json());
final Integer accountingRuleTypeId = fromApiJsonHelper.extractIntegerNamed("accountingType", element, Locale.getDefault());
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(accountingRuleTypeId);
if (accountingRuleType.equals(AccountingRuleType.NONE)) {
return;
} else if (accountingRuleType.equals(AccountingRuleType.CASH_BASED)) {
// asset
saveLoanToAccountMapping(command.getFundAccountId(), loanProductId, CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE.getValue());
saveLoanToAccountMapping(command.getLoanPortfolioAccountId(), loanProductId, CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue());
saveLoanToAccountMapping(command.getInterestOnLoanId(), loanProductId, CASH_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS.getValue());
final Long loanPortfolioAccountId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(),
element);
final Long fundAccountId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), element);
final Long incomeFromInterestId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(),
element);
final Long incomeFromFeeId = fromApiJsonHelper
.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), element);
final Long incomeFromPenaltyId = fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), element);
final Long writeOffAccountId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(),
element);
// income
saveLoanToAccountMapping(command.getIncomeFromFeeId(), loanProductId, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue());
saveLoanToAccountMapping(command.getIncomeFromPenaltyId(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue());
switch (accountingRuleType) {
case NONE:
break;
case CASH_BASED:
// asset
saveLoanToAccountMapping(fundAccountId, loanProductId, CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE.getValue());
saveLoanToAccountMapping(loanPortfolioAccountId, loanProductId, CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue());
saveLoanToAccountMapping(incomeFromInterestId, loanProductId, CASH_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS.getValue());
// expenses
saveLoanToAccountMapping(command.getWriteOffAccountId(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue());
} else if (accountingRuleType.equals(AccountingRuleType.ACCRUAL_BASED)) {
// assets (including receivables)
saveLoanToAccountMapping(command.getFundAccountId(), loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE.getValue());
saveLoanToAccountMapping(command.getLoanPortfolioAccountId(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue());
saveLoanToAccountMapping(command.getInterestOnLoanId(), loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS.getValue());
// income
saveLoanToAccountMapping(incomeFromFeeId, loanProductId, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue());
saveLoanToAccountMapping(incomeFromPenaltyId, loanProductId, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue());
saveLoanToAccountMapping(command.getReceivableInterestAccountId(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE.getValue());
saveLoanToAccountMapping(command.getReceivableFeeAccountId(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.FEES_RECEIVABLE.getValue());
saveLoanToAccountMapping(command.getReceivablePenaltyAccountId(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE.getValue());
// expenses
saveLoanToAccountMapping(writeOffAccountId, loanProductId, CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue());
break;
case ACCRUAL_BASED:
final Long receivableInterestAccountId = fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), element);
final Long receivableFeeAccountId = fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), element);
final Long receivablePenaltyAccountId = fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), element);
// income
saveLoanToAccountMapping(command.getIncomeFromFeeId(), loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue());
saveLoanToAccountMapping(command.getIncomeFromPenaltyId(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue());
// assets (including receivables)
saveLoanToAccountMapping(fundAccountId, loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE.getValue());
saveLoanToAccountMapping(loanPortfolioAccountId, loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue());
saveLoanToAccountMapping(incomeFromInterestId, loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS.getValue());
// expenses
saveLoanToAccountMapping(command.getWriteOffAccountId(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue());
saveLoanToAccountMapping(receivableInterestAccountId, loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE.getValue());
saveLoanToAccountMapping(receivableFeeAccountId, loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.FEES_RECEIVABLE.getValue());
saveLoanToAccountMapping(receivablePenaltyAccountId, loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE.getValue());
// income
saveLoanToAccountMapping(incomeFromFeeId, loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue());
saveLoanToAccountMapping(incomeFromPenaltyId, loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue());
// expenses
saveLoanToAccountMapping(writeOffAccountId, loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue());
break;
}
}
@Override
@Transactional
public Map<String, Object> updateLoanProductToGLAccountMapping(Long loanProductId, LoanProductCommand command,
public Map<String, Object> updateLoanProductToGLAccountMapping(final Long loanProductId, final JsonCommand command,
boolean accountingRuleChanged) {
Map<String, Object> changes = new HashMap<String, Object>();
AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(command.getAccountingType());
final Map<String, Object> changes = new HashMap<String, Object>();
final JsonElement element = fromApiJsonHelper.parse(command.json());
final Integer accountingRuleTypeId = fromApiJsonHelper.extractIntegerNamed("accountingType", element, Locale.getDefault());
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(accountingRuleTypeId);
final Long fundAccountId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), element);
final Long loanPortfolioAccountId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(),
element);
final Long incomeFromInterestId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(),
element);
final Long incomeFromFeeId = fromApiJsonHelper
.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), element);
final Long incomeFromPenaltyId = fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), element);
final Long writeOffAccountId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(),
element);
final Long receivableInterestAccountId = fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), element);
final Long receivableFeeAccountId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(),
element);
final Long receivablePenaltyAccountId = fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), element);
if (accountingRuleChanged) {
deleteLoanProductToGLAccountMapping(loanProductId);
createLoanProductToGLAccountMapping(loanProductId, command);
if (accountingRuleType.equals(AccountingRuleType.CASH_BASED)) {
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), command.getFundAccountId());
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), command.getLoanPortfolioAccountId());
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), command.getInterestOnLoanId());
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), command.getIncomeFromFeeId());
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), command.getIncomeFromPenaltyId());
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), command.getWriteOffAccountId());
} else if (accountingRuleType.equals(AccountingRuleType.ACCRUAL_BASED)) {
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), command.getFundAccountId());
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), command.getLoanPortfolioAccountId());
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), command.getInterestOnLoanId());
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), command.getIncomeFromFeeId());
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), command.getIncomeFromPenaltyId());
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), command.getWriteOffAccountId());
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), command.getReceivableInterestAccountId());
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), command.getReceivableFeeAccountId());
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), command.getReceivablePenaltyAccountId());
switch (accountingRuleType) {
case NONE:
break;
case CASH_BASED:
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), fundAccountId);
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), loanPortfolioAccountId);
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), incomeFromInterestId);
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), incomeFromFeeId);
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), incomeFromPenaltyId);
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), writeOffAccountId);
break;
case ACCRUAL_BASED:
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), fundAccountId);
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), loanPortfolioAccountId);
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), incomeFromInterestId);
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), incomeFromFeeId);
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), incomeFromPenaltyId);
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), writeOffAccountId);
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), receivableInterestAccountId);
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), receivableFeeAccountId);
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), receivablePenaltyAccountId);
break;
}
} else {
switch (accountingRuleType) {
case NONE:
break;
case CASH_BASED:
// asset
updateLoanToAccountMapping(fundAccountId, loanProductId, CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE.getValue(),
CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE.toString(), LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), changes);
updateLoanToAccountMapping(loanPortfolioAccountId, loanProductId, CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue(),
CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.toString(), LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(),
changes);
updateLoanToAccountMapping(incomeFromInterestId, loanProductId, CASH_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS.getValue(),
CASH_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), changes);
if (accountingRuleType.equals(AccountingRuleType.NONE)) {
// do nothing
} else if (accountingRuleType.equals(AccountingRuleType.CASH_BASED)) {
// asset
updateLoanToAccountMapping(command.getFundAccountId(), loanProductId, CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE.getValue(),
CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE.toString(), LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), changes);
updateLoanToAccountMapping(command.getLoanPortfolioAccountId(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue(), CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), changes);
updateLoanToAccountMapping(command.getInterestOnLoanId(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS.getValue(), CASH_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), changes);
// income
updateLoanToAccountMapping(incomeFromFeeId, loanProductId, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue(),
CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.toString(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(),
changes);
updateLoanToAccountMapping(incomeFromPenaltyId, loanProductId, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue(),
CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), changes);
// income
updateLoanToAccountMapping(command.getIncomeFromFeeId(), loanProductId, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue(),
CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.toString(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(),
changes);
updateLoanToAccountMapping(command.getIncomeFromPenaltyId(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue(), CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), changes);
// expenses
updateLoanToAccountMapping(writeOffAccountId, loanProductId, CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue(),
CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), changes);
break;
case ACCRUAL_BASED:
// assets (including receivables)
updateLoanToAccountMapping(fundAccountId, loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE.getValue(),
ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE.toString(), LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(),
changes);
updateLoanToAccountMapping(loanPortfolioAccountId, loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue(),
ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.toString(), LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(),
changes);
updateLoanToAccountMapping(incomeFromInterestId, loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS.getValue(),
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), changes);
// expenses
updateLoanToAccountMapping(command.getWriteOffAccountId(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue(), CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), changes);
} else if (accountingRuleType.equals(AccountingRuleType.ACCRUAL_BASED)) {
// assets (including receivables)
updateLoanToAccountMapping(command.getFundAccountId(), loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE.getValue(),
ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE.toString(), LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), changes);
updateLoanToAccountMapping(command.getLoanPortfolioAccountId(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue(), ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), changes);
updateLoanToAccountMapping(command.getInterestOnLoanId(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS.getValue(), ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), changes);
updateLoanToAccountMapping(receivableInterestAccountId, loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE.getValue(),
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), changes);
updateLoanToAccountMapping(command.getReceivableInterestAccountId(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE.getValue(), ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), changes);
updateLoanToAccountMapping(command.getReceivableFeeAccountId(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.FEES_RECEIVABLE.getValue(), ACCRUAL_ACCOUNTS_FOR_LOAN.FEES_RECEIVABLE.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), changes);
updateLoanToAccountMapping(command.getReceivablePenaltyAccountId(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE.getValue(),
ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), changes);
updateLoanToAccountMapping(receivableFeeAccountId, loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.FEES_RECEIVABLE.getValue(),
ACCRUAL_ACCOUNTS_FOR_LOAN.FEES_RECEIVABLE.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), changes);
// income
updateLoanToAccountMapping(command.getIncomeFromFeeId(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue(), ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), changes);
updateLoanToAccountMapping(command.getIncomeFromPenaltyId(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue(),
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), changes);
updateLoanToAccountMapping(receivablePenaltyAccountId, loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE.getValue(),
ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), changes);
// expenses
updateLoanToAccountMapping(command.getWriteOffAccountId(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue(), ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), changes);
// income
updateLoanToAccountMapping(incomeFromFeeId, loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue(),
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), changes);
updateLoanToAccountMapping(incomeFromPenaltyId, loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue(),
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), changes);
// expenses
updateLoanToAccountMapping(writeOffAccountId, loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue(),
ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), changes);
break;
}
}
return changes;
}
private void saveLoanToAccountMapping(Long glAccountId, Long productId, int accountTypeId) {
private void saveLoanToAccountMapping(final Long glAccountId, final Long productId, final int accountTypeId) {
GLAccount glAccount = accountRepository.findOne(glAccountId);
if (glAccount == null) { throw new GLAccountNotFoundException(glAccountId); }
ProductToGLAccountMapping accountMapping = new ProductToGLAccountMapping(glAccount, productId,
@@ -184,8 +241,8 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
accountMappingRepository.save(accountMapping);
}
private void updateLoanToAccountMapping(Long glAccountId, Long productId, int accountTypeId, String accountTypeName,
String accountParamValue, Map<String, Object> changes) {
private void updateLoanToAccountMapping(final Long glAccountId, final Long productId, final int accountTypeId,
final String accountTypeName, final String accountParamValue, final Map<String, Object> changes) {
// get the existing product
ProductToGLAccountMapping accountMapping = accountMappingRepository.findByProductIdAndProductTypeAndFinancialAccountType(productId,
PortfolioProductType.LOAN.getValue(), accountTypeId);
@@ -201,7 +258,7 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
}
@Override
public void deleteLoanProductToGLAccountMapping(Long loanProductId) {
public void deleteLoanProductToGLAccountMapping(final Long loanProductId) {
List<ProductToGLAccountMapping> productToGLAccountMappings = accountMappingRepository.findByProductIdAndProductType(loanProductId,
PortfolioProductType.LOAN.getValue());
if (productToGLAccountMappings != null && productToGLAccountMappings.size() > 0) {
@@ -2,8 +2,10 @@ package org.mifosplatform.portfolio.loanproduct.api;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.Consumes;
@@ -56,10 +58,12 @@ public class LoanProductsApiResource {
"interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "charges", "createdOn",
"lastModifedOn", "currencyOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
"repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "transactionProcessingStrategyOptions",
"chargeOptions", "assetAccountOptions", "incomeAccountOptions", "expenseAccountOptions", "accountingType" , 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()));
"chargeOptions", "assetAccountOptions", "incomeAccountOptions", "expenseAccountOptions", "accountingType",
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()));
private final String resourceNameForPermissions = "LOANPRODUCT";
@@ -131,7 +135,7 @@ public class LoanProductsApiResource {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
LoanProductData loanProduct = this.loanProductReadPlatformService.retrieveNewLoanProductDetails();
loanProduct = handleTemplate(loanProduct);
loanProduct = handleTemplate(loanProduct, new HashMap<String, Object>());
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, loanProduct, LOAN_PRODUCT_DATA_PARAMETERS);
@@ -148,13 +152,17 @@ public class LoanProductsApiResource {
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
LoanProductData loanProduct = this.loanProductReadPlatformService.retrieveLoanProduct(productId);
if (settings.isTemplate()) {
loanProduct = handleTemplate(loanProduct);
}
LoanProductData loanProductDataUpdatedWithAccountingMapping=accountMappingReadPlatformService.fetchAccountMappingDetailsForLoanProduct(loanProduct);
return this.toApiJsonSerializer.serialize(settings, loanProductDataUpdatedWithAccountingMapping, LOAN_PRODUCT_DATA_PARAMETERS);
Map<String, Object> accountingMappings = null;
if (loanProduct.hasAccountingEnabled()) {
accountingMappings = accountMappingReadPlatformService.fetchAccountMappingDetailsForLoanProduct(productId, loanProduct.accountingRuleType());
loanProduct = LoanProductData.withAccountingMappings(loanProduct, accountingMappings);
}
if (settings.isTemplate()) {
loanProduct = handleTemplate(loanProduct, accountingMappings);
}
return this.toApiJsonSerializer.serialize(settings, loanProduct, LOAN_PRODUCT_DATA_PARAMETERS);
}
@PUT
@@ -171,7 +179,7 @@ public class LoanProductsApiResource {
return this.toApiJsonSerializer.serialize(result);
}
private LoanProductData handleTemplate(final LoanProductData productData) {
private LoanProductData handleTemplate(final LoanProductData productData, final Map<String, Object> accountingMappings) {
final boolean feeChargesOnly = true;
Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
@@ -196,7 +204,7 @@ public class LoanProductsApiResource {
}
final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions = this.dropdownReadPlatformService
.retreiveTransactionProcessingStrategies();
List<GLAccountData> assetAccountOptions = accountReadPlatformService.retrieveAllEnabledDetailGLAccounts(GLAccountType.ASSET);
if (assetAccountOptions.isEmpty()) {
assetAccountOptions = null;
@@ -209,10 +217,10 @@ public class LoanProductsApiResource {
if (expenseAccountOptions.isEmpty()) {
expenseAccountOptions = null;
}
return new LoanProductData(productData, chargeOptions, currencyOptions, amortizationTypeOptions, interestTypeOptions,
interestCalculationPeriodTypeOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions,
assetAccountOptions, incomeAccountOptions, expenseAccountOptions);
interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions, assetAccountOptions,
incomeAccountOptions, expenseAccountOptions, accountingMappings);
}
}
@@ -1,345 +0,0 @@
package org.mifosplatform.portfolio.loanproduct.command;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import org.mifosplatform.accounting.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.portfolio.loanproduct.domain.AccountingRuleType;
/**
* Immutable command for creating or updating details of a loan product.
*/
public class LoanProductCommand {
private final String name;
private final String description;
private final Long fundId;
private final Long transactionProcessingStrategyId;
private final String currencyCode;
private final Integer digitsAfterDecimal;
private final BigDecimal principal;
private final BigDecimal inArrearsTolerance;
private final Integer numberOfRepayments;
private final Integer repaymentEvery;
private final BigDecimal interestRatePerPeriod;
private final Integer repaymentFrequencyType;
private final Integer interestRateFrequencyType;
private final Integer amortizationType;
private final Integer interestType;
private final Integer interestCalculationPeriodType;
private final String[] charges;
// accounting fields
private final Integer accountingType;
// accounting heads used for cash/accrual based accounting
private final Long loanPortfolioAccountId;
private final Long fundAccountId;
private final Long interestOnLoanId;
private final Long incomeFromFeeId;
private final Long incomeFromPenaltyId;
private final Long writeOffAccountId;
// accounting heads used only for accrual based accounting
private final Long receivableInterestAccountId;
private final Long receivableFeeAccountId;
private final Long receivablePenaltyAccountId;
public LoanProductCommand(final String name, final String description, final Long fundId, final Long transactionProcessingStrategyId,
final String currencyCode, final Integer digitsAfterDecimal, final BigDecimal principal, final BigDecimal inArrearsTolerance,
final Integer numberOfRepayments, final Integer repaymentEvery, final BigDecimal interestRatePerPeriod,
final Integer repaymentFrequencyType, final Integer interestRateFrequencyType, final Integer amortizationType,
final Integer interestType, final Integer interestCalculationPeriodType, final String[] charges, final Integer accountingType,
final Long loanPortfolioAccountId, final Long fundAccountId, final Long interestOnLoanId, final Long incomeFromFeeId,
final Long incomeFromPenaltyId, final Long writeOffAccountId, final Long receivableInterestAccountId,
final Long receivableFeeAccountId, final Long receivablePenaltyAccountId) {
this.name = name;
this.description = description;
this.fundId = fundId;
this.transactionProcessingStrategyId = transactionProcessingStrategyId;
this.currencyCode = currencyCode;
this.digitsAfterDecimal = digitsAfterDecimal;
this.principal = principal;
if (inArrearsTolerance != null && BigDecimal.ZERO.compareTo(inArrearsTolerance) == 0) {
this.inArrearsTolerance = null;
} else {
this.inArrearsTolerance = inArrearsTolerance;
}
this.numberOfRepayments = numberOfRepayments;
this.repaymentEvery = repaymentEvery;
this.interestRatePerPeriod = interestRatePerPeriod;
this.repaymentFrequencyType = repaymentFrequencyType;
this.interestRateFrequencyType = interestRateFrequencyType;
this.amortizationType = amortizationType;
this.interestType = interestType;
this.interestCalculationPeriodType = interestCalculationPeriodType;
this.charges = charges;
this.accountingType = accountingType;
// accounting heads used for cash/accrual based accounting
this.loanPortfolioAccountId = loanPortfolioAccountId;
this.fundAccountId = fundAccountId;
this.interestOnLoanId = interestOnLoanId;
this.incomeFromFeeId = incomeFromFeeId;
this.incomeFromPenaltyId = incomeFromPenaltyId;
this.writeOffAccountId = writeOffAccountId;
// accounting heads used only for accrual based accounting
this.receivableInterestAccountId = receivableInterestAccountId;
this.receivableFeeAccountId = receivableFeeAccountId;
this.receivablePenaltyAccountId = receivablePenaltyAccountId;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public Long getFundId() {
return fundId;
}
public Long getTransactionProcessingStrategyId() {
return transactionProcessingStrategyId;
}
public String getCurrencyCode() {
return currencyCode;
}
public Integer getDigitsAfterDecimal() {
return digitsAfterDecimal;
}
public BigDecimal getPrincipal() {
return principal;
}
public BigDecimal getInArrearsTolerance() {
return inArrearsTolerance;
}
public Integer getNumberOfRepayments() {
return numberOfRepayments;
}
public Integer getRepaymentEvery() {
return repaymentEvery;
}
public BigDecimal getInterestRatePerPeriod() {
return interestRatePerPeriod;
}
public Integer getRepaymentFrequencyType() {
return repaymentFrequencyType;
}
public Integer getInterestRateFrequencyType() {
return interestRateFrequencyType;
}
public Integer getAmortizationType() {
return amortizationType;
}
public Integer getInterestType() {
return interestType;
}
public Integer getInterestCalculationPeriodType() {
return interestCalculationPeriodType;
}
public String[] getCharges() {
return charges;
}
public Integer getAccountingType() {
return this.accountingType;
}
public Long getLoanPortfolioAccountId() {
return this.loanPortfolioAccountId;
}
public Long getFundAccountId() {
return this.fundAccountId;
}
public Long getInterestOnLoanId() {
return this.interestOnLoanId;
}
public Long getIncomeFromFeeId() {
return this.incomeFromFeeId;
}
public Long getIncomeFromPenaltyId() {
return this.incomeFromPenaltyId;
}
public Long getWriteOffAccountId() {
return this.writeOffAccountId;
}
public Long getReceivableInterestAccountId() {
return this.receivableInterestAccountId;
}
public Long getReceivableFeeAccountId() {
return this.receivableFeeAccountId;
}
public Long getReceivablePenaltyAccountId() {
return this.receivablePenaltyAccountId;
}
public void validateForCreate() {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("product");
baseDataValidator.reset().parameter("name").value(this.name).notBlank();
baseDataValidator.reset().parameter("description").value(this.description).notExceedingLengthOf(500);
baseDataValidator.reset().parameter("currencyCode").value(this.currencyCode).notBlank();
baseDataValidator.reset().parameter("digitsAfterDecimal").value(this.digitsAfterDecimal).notNull().inMinMaxRange(0, 6);
baseDataValidator.reset().parameter("principal").value(this.principal).notNull().positiveAmount();
baseDataValidator.reset().parameter("inArrearsTolerance").value(this.inArrearsTolerance).ignoreIfNull().zeroOrPositiveAmount();
baseDataValidator.reset().parameter("repaymentFrequencyType").value(this.repaymentFrequencyType).notNull().inMinMaxRange(0, 3);
baseDataValidator.reset().parameter("repaymentEvery").value(this.repaymentEvery).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter("numberOfRepayments").value(this.numberOfRepayments).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter("interestRatePerPeriod").value(this.interestRatePerPeriod).notNull();
baseDataValidator.reset().parameter("interestRateFrequencyType").value(this.interestRateFrequencyType).notNull()
.inMinMaxRange(0, 3);
baseDataValidator.reset().parameter("amortizationType").value(this.amortizationType).notNull().inMinMaxRange(0, 1);
baseDataValidator.reset().parameter("interestType").value(this.interestType).notNull().inMinMaxRange(0, 1);
baseDataValidator.reset().parameter("interestCalculationPeriodType").value(this.interestCalculationPeriodType).notNull()
.inMinMaxRange(0, 1);
// validation for accounting rules
baseDataValidator.reset().parameter("accountingType").value(this.accountingType).notNull().inMinMaxRange(1,3);
if (this.getAccountingType() != null) {
/**
* validation for heads applicable for both cash and accrual based
* accounting
**/
if (this.getAccountingType().equals(AccountingRuleType.CASH_BASED.getValue())
|| this.getAccountingType().equals(AccountingRuleType.ACCRUAL_BASED.getValue())) {
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue())
.value(this.loanPortfolioAccountId).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue()).value(this.fundAccountId)
.notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue())
.value(this.interestOnLoanId).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue()).value(this.incomeFromFeeId)
.notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue())
.value(this.incomeFromPenaltyId).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue())
.value(this.writeOffAccountId).notNull().integerGreaterThanZero();
}// validation for accrual based accounting specific heads
if (this.getAccountingType().equals(AccountingRuleType.ACCRUAL_BASED.getValue())) {
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue())
.value(this.receivableInterestAccountId).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue())
.value(this.receivableFeeAccountId).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue())
.value(this.receivablePenaltyAccountId).notNull().integerGreaterThanZero();
}
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
public void validateForUpdate() {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("product");
baseDataValidator.reset().parameter("name").value(this.name).ignoreIfNull().notBlank();
baseDataValidator.reset().parameter("description").value(this.description).ignoreIfNull().notExceedingLengthOf(500);
baseDataValidator.reset().parameter("currencyCode").value(this.currencyCode).ignoreIfNull().notBlank();
baseDataValidator.reset().parameter("digitsAfterDecimal").value(this.digitsAfterDecimal).ignoreIfNull().notNull()
.inMinMaxRange(0, 6);
baseDataValidator.reset().parameter("principal").value(this.principal).ignoreIfNull().notNull().positiveAmount();
baseDataValidator.reset().parameter("inArrearsTolerance").value(this.inArrearsTolerance).ignoreIfNull().notNull()
.zeroOrPositiveAmount();
baseDataValidator.reset().parameter("repaymentFrequencyType").value(this.repaymentFrequencyType).ignoreIfNull().notNull()
.inMinMaxRange(0, 3);
baseDataValidator.reset().parameter("repaymentEvery").value(this.repaymentEvery).ignoreIfNull().notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter("numberOfRepayments").value(this.numberOfRepayments).ignoreIfNull().notNull()
.integerGreaterThanZero();
baseDataValidator.reset().parameter("interestRatePerPeriod").value(this.interestRatePerPeriod).ignoreIfNull().notBlank();
baseDataValidator.reset().parameter("interestRateFrequencyType").value(this.interestRateFrequencyType).ignoreIfNull().notNull()
.inMinMaxRange(0, 3);
baseDataValidator.reset().parameter("amortizationType").value(this.amortizationType).ignoreIfNull().notNull().inMinMaxRange(0, 1);
baseDataValidator.reset().parameter("interestType").value(this.interestType).ignoreIfNull().notNull().inMinMaxRange(0, 1);
baseDataValidator.reset().parameter("interestCalculationPeriodType").value(this.interestCalculationPeriodType).ignoreIfNull()
.notNull().inMinMaxRange(0, 1);
// validation for accounting rules
baseDataValidator.reset().parameter("accountingType").value(this.accountingType).ignoreIfNull().inMinMaxRange(1, 3);
if (this.getAccountingType() != null) {
/**
* validation for heads applicable for both cash and accrual based
* accounting
**/
if (this.getAccountingType().equals(AccountingRuleType.CASH_BASED.getValue())
|| this.getAccountingType().equals(AccountingRuleType.ACCRUAL_BASED.getValue())) {
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue())
.value(this.loanPortfolioAccountId).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue()).value(this.fundAccountId)
.notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue())
.value(this.interestOnLoanId).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue()).value(this.incomeFromFeeId)
.notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue())
.value(this.incomeFromPenaltyId).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue())
.value(this.writeOffAccountId).notNull().integerGreaterThanZero();
}// validation for accrual based accounting specific heads
if (this.getAccountingType().equals(AccountingRuleType.ACCRUAL_BASED.getValue())) {
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue())
.value(this.receivableInterestAccountId).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue())
.value(this.receivableFeeAccountId).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue())
.value(this.receivablePenaltyAccountId).notNull().integerGreaterThanZero();
}
}
baseDataValidator.reset().anyOfNotNull(this.fundId, this.transactionProcessingStrategyId, this.name, this.description,
this.currencyCode, this.digitsAfterDecimal, this.principal, this.inArrearsTolerance, this.repaymentFrequencyType,
this.repaymentEvery, this.numberOfRepayments, this.interestRatePerPeriod, this.interestRateFrequencyType,
this.amortizationType, this.interestType, this.interestCalculationPeriodType, this.charges, this.accountingType);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
}
@@ -4,6 +4,7 @@ import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.mifosplatform.accounting.api.data.GLAccountData;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
@@ -24,33 +25,31 @@ public class LoanProductData {
private final Long id;
private final String name;
private final String description;
private final Long fundId;
private final String fundName;
private final Long transactionProcessingStrategyId;
private final String transactionProcessingStrategyName;
private final CurrencyData currency;
private final BigDecimal principal;
private final BigDecimal inArrearsTolerance;
private final Integer numberOfRepayments;
private final Integer loanTermFrequency;
private final Integer repaymentEvery;
private final BigDecimal interestRatePerPeriod;
private final BigDecimal annualInterestRate;
private final Integer accountingType;
private final EnumOptionData loanTermFrequencyType;
private final EnumOptionData repaymentFrequencyType;
private final Long transactionProcessingStrategyId;
private final String transactionProcessingStrategyName;
private final BigDecimal interestRatePerPeriod;
private final EnumOptionData interestRateFrequencyType;
private final BigDecimal annualInterestRate;
private final EnumOptionData amortizationType;
private final EnumOptionData interestType;
private final EnumOptionData interestCalculationPeriodType;
private final BigDecimal inArrearsTolerance;
// FIXME - KW - This should be returned for serialization as an EnumOptionData like the other Enumerated values
private final Integer accountingType;
private final Collection<ChargeData> charges;
// template related
private final Collection<CurrencyData> currencyOptions;
private final Collection<FundData> fundOptions;
private final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions;
@@ -67,17 +66,9 @@ public class LoanProductData {
private final List<GLAccountData> incomeAccountOptions;
private final List<GLAccountData> expenseAccountOptions;
// accounting related mapping feilds
private Long loanPortfolioAccountId;
private Long fundSourceAccountId;
private Long interestOnLoanAccountId;
private Long incomeFromFeeAccountId;
private Long incomeFromPenaltyAccountId;
private Long writeOffAccountId;
// accounting heads used only for accrual based accounting
private Long receivableInterestAccountId;
private Long receivableFeeAccountId;
private Long receivablePenaltyAccountId;
// accounting related mapping fields
@SuppressWarnings("unused")
private final Map<String, Object> accountingMappings;
/**
* Used when returning lookup information about loan product for dropdowns.
@@ -143,6 +134,15 @@ public class LoanProductData {
transactionProcessingStrategyId, transactionProcessingStrategyName, charges, accountingType);
}
public static LoanProductData withAccountingMappings(final LoanProductData productData, final Map<String, Object> accountingMappings) {
return new LoanProductData(productData, productData.chargeOptions, productData.currencyOptions,
productData.amortizationTypeOptions, productData.interestTypeOptions, productData.interestCalculationPeriodTypeOptions,
productData.loanTermFrequencyTypeOptions, productData.repaymentFrequencyTypeOptions,
productData.interestRateFrequencyTypeOptions, productData.fundOptions, productData.transactionProcessingStrategyOptions,
productData.assetAccountOptions, productData.incomeAccountOptions, productData.expenseAccountOptions, accountingMappings);
}
public LoanProductData(final Long id, final String name, final String description, final CurrencyData currency,
final BigDecimal principal, final BigDecimal tolerance, final Integer numberOfRepayments, final Integer loanTermFrequency,
final Integer repaymentEvery, final BigDecimal interestRatePerPeriod, final BigDecimal annualInterestRate,
@@ -189,6 +189,7 @@ public class LoanProductData {
this.assetAccountOptions = null;
this.incomeAccountOptions = null;
this.expenseAccountOptions = null;
this.accountingMappings = null;
}
public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
@@ -198,7 +199,7 @@ public class LoanProductData {
final List<EnumOptionData> interestRateFrequencyTypeOptions, final Collection<FundData> fundOptions,
final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions,
final List<GLAccountData> assetAccountOptions, final List<GLAccountData> incomeAccountOptions,
final List<GLAccountData> expenseAccountOptions) {
final List<GLAccountData> expenseAccountOptions, final Map<String, Object> accountingMappings) {
this.id = productData.id;
this.name = productData.name;
this.description = productData.description;
@@ -224,7 +225,7 @@ public class LoanProductData {
this.chargeOptions = chargeOptions;
this.currencyOptions = currencyOptions;
if (this.currencyOptions.size() == 1) {
if (this.currencyOptions != null && this.currencyOptions.size() == 1) {
this.currency = new ArrayList<CurrencyData>(this.currencyOptions).get(0);
} else {
this.currency = productData.currency;
@@ -241,6 +242,7 @@ public class LoanProductData {
this.assetAccountOptions = assetAccountOptions;
this.incomeAccountOptions = incomeAccountOptions;
this.expenseAccountOptions = expenseAccountOptions;
this.accountingMappings = accountingMappings;
}
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
@@ -259,6 +261,14 @@ public class LoanProductData {
return chargesLocal;
}
public Integer accountingRuleType() {
return this.accountingType;
}
public boolean hasAccountingEnabled() {
return this.accountingType > 1;
}
public Long getId() {
return id;
}
@@ -378,92 +388,4 @@ public class LoanProductData {
public Collection<ChargeData> getChargeOptions() {
return chargeOptions;
}
public List<GLAccountData> getAssetAccountOptions() {
return this.assetAccountOptions;
}
public List<GLAccountData> getIncomeAccountOptions() {
return this.incomeAccountOptions;
}
public List<GLAccountData> getExpenseAccountOptions() {
return this.expenseAccountOptions;
}
public Long getLoanPortfolioAccountId() {
return this.loanPortfolioAccountId;
}
public void setLoanPortfolioAccountId(Long loanPortfolioAccountId) {
this.loanPortfolioAccountId = loanPortfolioAccountId;
}
public Long getFundSourceAccountId() {
return this.fundSourceAccountId;
}
public void setFundSourceAccountId(Long fundSourceAccountId) {
this.fundSourceAccountId = fundSourceAccountId;
}
public Long getInterestOnLoanAccountId() {
return this.interestOnLoanAccountId;
}
public void setInterestOnLoanAccountId(Long interestOnLoanAccountId) {
this.interestOnLoanAccountId = interestOnLoanAccountId;
}
public Long getIncomeFromFeeAccountId() {
return this.incomeFromFeeAccountId;
}
public void setIncomeFromFeeAccountId(Long incomeFromFeeAccountId) {
this.incomeFromFeeAccountId = incomeFromFeeAccountId;
}
public Long getIncomeFromPenaltyAccountId() {
return this.incomeFromPenaltyAccountId;
}
public void setIncomeFromPenaltyAccountId(Long incomeFromPenaltyAccountId) {
this.incomeFromPenaltyAccountId = incomeFromPenaltyAccountId;
}
public Long getWriteOffAccountId() {
return this.writeOffAccountId;
}
public void setWriteOffAccountId(Long writeOffAccountId) {
this.writeOffAccountId = writeOffAccountId;
}
public Long getReceivableInterestAccountId() {
return this.receivableInterestAccountId;
}
public void setReceivableInterestAccountId(Long receivableInterestAccountId) {
this.receivableInterestAccountId = receivableInterestAccountId;
}
public Long getReceivableFeeAccountId() {
return this.receivableFeeAccountId;
}
public void setReceivableFeeAccountId(Long receivableFeeAccountId) {
this.receivableFeeAccountId = receivableFeeAccountId;
}
public Long getReceivablePenaltyAccountId() {
return this.receivablePenaltyAccountId;
}
public void setReceivablePenaltyAccountId(Long receivablePenaltyAccountId) {
this.receivablePenaltyAccountId = receivablePenaltyAccountId;
}
public Integer getAccountingType() {
return this.accountingType;
}
}
@@ -2,30 +2,30 @@ package org.mifosplatform.portfolio.loanproduct.serialization;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.accounting.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.serialization.AbstractFromApiJsonDeserializer;
import org.mifosplatform.infrastructure.core.serialization.FromApiJsonDeserializer;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.loanproduct.command.LoanProductCommand;
import org.mifosplatform.portfolio.loanproduct.domain.AccountingRuleType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
/**
* Implementation of {@link FromApiJsonDeserializer} for
* {@link LoanProductCommand}'s.
*/
@Component
public final class LoanProductCommandFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer<LoanProductCommand> {
public final class LoanProductCommandFromApiJsonDeserializer {
/**
* The parameters supported for this command.
@@ -33,10 +33,12 @@ public final class LoanProductCommandFromApiJsonDeserializer extends AbstractFro
private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("name", "description", "fundId",
"transactionProcessingStrategyId", "currencyCode", "digitsAfterDecimal", "principal", "inArrearsTolerance",
"interestRatePerPeriod", "repaymentEvery", "numberOfRepayments", "repaymentFrequencyType", "interestRateFrequencyType",
"amortizationType", "interestType", "interestCalculationPeriodType", "charges", "locale", "accountingType" , 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()));
"amortizationType", "interestType", "interestCalculationPeriodType", "charges", "locale", "accountingType",
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()));
private final FromJsonHelper fromApiJsonHelper;
@@ -45,54 +47,297 @@ public final class LoanProductCommandFromApiJsonDeserializer extends AbstractFro
this.fromApiJsonHelper = fromApiJsonHelper;
}
@Override
public LoanProductCommand commandFromApiJson(final String json) {
public void validateForCreate(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final JsonElement element = fromApiJsonHelper.parse(json);
final String name = fromApiJsonHelper.extractStringNamed("name", element);
final String description = fromApiJsonHelper.extractStringNamed("description", element);
final Long fundId = fromApiJsonHelper.extractLongNamed("fundId", element);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loanproduct");
final Long transactionProcessingStrategyId = fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element);
final JsonElement element = fromApiJsonHelper.parse(json);
final String name = fromApiJsonHelper.extractStringNamed("name", element);
baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(100);
final String description = fromApiJsonHelper.extractStringNamed("description", element);
baseDataValidator.reset().parameter("description").value(description).notExceedingLengthOf(500);
if (fromApiJsonHelper.parameterExists("fundId", element)) {
final Long fundId = fromApiJsonHelper.extractLongNamed("fundId", element);
baseDataValidator.reset().parameter("fundId").value(fundId).ignoreIfNull().integerGreaterThanZero();
}
final String currencyCode = fromApiJsonHelper.extractStringNamed("currencyCode", element);
final Integer digitsAfterDecimal = fromApiJsonHelper.extractIntegerWithLocaleNamed("digitsAfterDecimal", element);
baseDataValidator.reset().parameter("currencyCode").value(currencyCode).notBlank().notExceedingLengthOf(3);
final Integer digitsAfterDecimal = fromApiJsonHelper.extractIntegerNamed("digitsAfterDecimal", element, Locale.getDefault());
baseDataValidator.reset().parameter("digitsAfterDecimal").value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6);
final BigDecimal principal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
final BigDecimal inArrearsTolerance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element);
final BigDecimal interestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
final Integer repaymentEvery = fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentEvery", element);
final Integer numberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element);
final Integer repaymentFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentFrequencyType", element);
final Integer interestRateFrequencyType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestRateFrequencyType", element);
final Integer amortizationType = fromApiJsonHelper.extractIntegerWithLocaleNamed("amortizationType", element);
final Integer interestType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestType", element);
final Integer interestCalculationPeriodType = fromApiJsonHelper.extractIntegerWithLocaleNamed("interestCalculationPeriodType",
element);
final String[] charges = fromApiJsonHelper.extractArrayNamed("charges", element);
//extract accounting related fields
final Integer accountingType = fromApiJsonHelper.extractIntegerWithLocaleNamed("accountingType",
element);
final Long loanPortfolioAccountId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), element);
final Long fundAccountId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), element);
final Long incomeFromInterestId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), element);
final Long incomeFromFeeId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), element);
final Long incomeFromPenaltyId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), element);
final Long writeOffAccountId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), element);
final Long receivableInterestAccountId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), element);
final Long receivableFeeAccountId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), element);
final Long receivablePenaltyAccountId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), element);
baseDataValidator.reset().parameter("principal").value(principal).notNull().positiveAmount();
return new LoanProductCommand(name, description, fundId, transactionProcessingStrategyId, currencyCode, digitsAfterDecimal,
principal, inArrearsTolerance, numberOfRepayments, repaymentEvery, interestRatePerPeriod, repaymentFrequencyType,
interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, charges, accountingType,
loanPortfolioAccountId, fundAccountId, incomeFromInterestId, incomeFromFeeId, incomeFromPenaltyId, writeOffAccountId,
receivableInterestAccountId, receivableFeeAccountId, receivablePenaltyAccountId);
}
final BigDecimal inArrearsTolerance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element);
baseDataValidator.reset().parameter("inArrearsTolerance").value(inArrearsTolerance).ignoreIfNull().zeroOrPositiveAmount();
final BigDecimal interestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).notNull().zeroOrPositiveAmount();
final Integer numberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element);
baseDataValidator.reset().parameter("numberOfRepayments").value(numberOfRepayments).notNull().integerGreaterThanZero();
final Integer repaymentEvery = fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentEvery", element);
baseDataValidator.reset().parameter("repaymentEvery").value(repaymentEvery).notNull().integerGreaterThanZero();
final Integer repaymentFrequencyType = fromApiJsonHelper
.extractIntegerNamed("repaymentFrequencyType", element, Locale.getDefault());
baseDataValidator.reset().parameter("repaymentFrequencyType").value(repaymentFrequencyType).notNull().inMinMaxRange(0, 3);
final Long transactionProcessingStrategyId = fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element);
baseDataValidator.reset().parameter("transactionProcessingStrategyId").value(transactionProcessingStrategyId).notNull()
.integerGreaterThanZero();
final Integer interestRateFrequencyType = fromApiJsonHelper.extractIntegerNamed("interestRateFrequencyType", element,
Locale.getDefault());
baseDataValidator.reset().parameter("interestRateFrequencyType").value(interestRateFrequencyType).notNull().inMinMaxRange(0, 3);
final Integer amortizationType = fromApiJsonHelper.extractIntegerNamed("amortizationType", element, Locale.getDefault());
baseDataValidator.reset().parameter("amortizationType").value(amortizationType).notNull().inMinMaxRange(0, 1);
final Integer interestType = fromApiJsonHelper.extractIntegerNamed("interestType", element, Locale.getDefault());
baseDataValidator.reset().parameter("interestType").value(interestType).notNull().inMinMaxRange(0, 1);
final Integer interestCalculationPeriodType = fromApiJsonHelper.extractIntegerNamed("interestCalculationPeriodType", element,
Locale.getDefault());
baseDataValidator.reset().parameter("interestCalculationPeriodType").value(interestCalculationPeriodType).notNull()
.inMinMaxRange(0, 1);
// accounting related data validation
final Integer accountingRuleType = fromApiJsonHelper.extractIntegerNamed("accountingType", element, Locale.getDefault());
baseDataValidator.reset().parameter("accountingType").value(accountingRuleType).notNull().inMinMaxRange(1, 3);
if (isCashBased(accountingRuleType) || isAccrualBased(accountingRuleType)) {
final Long fundAccountId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue()).value(fundAccountId).notNull()
.integerGreaterThanZero();
final Long loanPortfolioAccountId = fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue()).value(loanPortfolioAccountId)
.notNull().integerGreaterThanZero();
}
if (isAccrualBased(accountingRuleType)) {
final Long receivableInterestAccountId = fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue())
.value(receivableInterestAccountId).notNull().integerGreaterThanZero();
final Long receivableFeeAccountId = fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue()).value(receivableFeeAccountId)
.notNull().integerGreaterThanZero();
final Long receivablePenaltyAccountId = fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue())
.value(receivablePenaltyAccountId).notNull().integerGreaterThanZero();
}
if (isCashBased(accountingRuleType) || isAccrualBased(accountingRuleType)) {
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)
.notNull().integerGreaterThanZero();
final Long incomeFromFeeId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(),
element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue()).value(incomeFromFeeId)
.notNull().integerGreaterThanZero();
final Long incomeFromPenaltyId = fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue()).value(incomeFromPenaltyId)
.notNull().integerGreaterThanZero();
final Long writeOffAccountId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(),
element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue()).value(writeOffAccountId)
.notNull().integerGreaterThanZero();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateForUpdate(String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loanproduct");
final JsonElement element = fromApiJsonHelper.parse(json);
if (fromApiJsonHelper.parameterExists("name", element)) {
final String name = fromApiJsonHelper.extractStringNamed("name", element);
baseDataValidator.reset().parameter("name").value(name).notBlank().notExceedingLengthOf(100);
}
if (fromApiJsonHelper.parameterExists("description", element)) {
final String description = fromApiJsonHelper.extractStringNamed("description", element);
baseDataValidator.reset().parameter("description").value(description).notExceedingLengthOf(500);
}
if (fromApiJsonHelper.parameterExists("fundId", element)) {
final Long fundId = fromApiJsonHelper.extractLongNamed("fundId", element);
baseDataValidator.reset().parameter("fundId").value(fundId).ignoreIfNull().integerGreaterThanZero();
}
if (fromApiJsonHelper.parameterExists("currencyCode", element)) {
final String currencyCode = fromApiJsonHelper.extractStringNamed("currencyCode", element);
baseDataValidator.reset().parameter("currencyCode").value(currencyCode).notBlank().notExceedingLengthOf(3);
}
if (fromApiJsonHelper.parameterExists("digitsAfterDecimal", element)) {
final Integer digitsAfterDecimal = fromApiJsonHelper.extractIntegerNamed("digitsAfterDecimal", element, Locale.getDefault());
baseDataValidator.reset().parameter("digitsAfterDecimal").value(digitsAfterDecimal).notNull().inMinMaxRange(0, 6);
}
if (fromApiJsonHelper.parameterExists("principal", element)) {
final BigDecimal principal = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
baseDataValidator.reset().parameter("principal").value(principal).notNull().positiveAmount();
}
if (fromApiJsonHelper.parameterExists("inArrearsTolerance", element)) {
final BigDecimal inArrearsTolerance = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element);
baseDataValidator.reset().parameter("inArrearsTolerance").value(inArrearsTolerance).ignoreIfNull().zeroOrPositiveAmount();
}
if (fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) {
final BigDecimal interestRatePerPeriod = fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).notNull().zeroOrPositiveAmount();
}
if (fromApiJsonHelper.parameterExists("numberOfRepayments", element)) {
final Integer numberOfRepayments = fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element);
baseDataValidator.reset().parameter("numberOfRepayments").value(numberOfRepayments).notNull().integerGreaterThanZero();
}
if (fromApiJsonHelper.parameterExists("repaymentEvery", element)) {
final Integer repaymentEvery = fromApiJsonHelper.extractIntegerWithLocaleNamed("repaymentEvery", element);
baseDataValidator.reset().parameter("repaymentEvery").value(repaymentEvery).notNull().integerGreaterThanZero();
}
if (fromApiJsonHelper.parameterExists("repaymentFrequencyType", element)) {
final Integer repaymentFrequencyType = fromApiJsonHelper.extractIntegerNamed("repaymentFrequencyType", element,
Locale.getDefault());
baseDataValidator.reset().parameter("repaymentFrequencyType").value(repaymentFrequencyType).notNull().inMinMaxRange(0, 3);
}
if (fromApiJsonHelper.parameterExists("transactionProcessingStrategyId", element)) {
final Long transactionProcessingStrategyId = fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element);
baseDataValidator.reset().parameter("transactionProcessingStrategyId").value(transactionProcessingStrategyId).notNull()
.integerGreaterThanZero();
}
if (fromApiJsonHelper.parameterExists("interestRateFrequencyType", element)) {
final Integer interestRateFrequencyType = fromApiJsonHelper.extractIntegerNamed("interestRateFrequencyType", element,
Locale.getDefault());
baseDataValidator.reset().parameter("interestRateFrequencyType").value(interestRateFrequencyType).notNull().inMinMaxRange(0, 3);
}
if (fromApiJsonHelper.parameterExists("amortizationType", element)) {
final Integer amortizationType = fromApiJsonHelper.extractIntegerNamed("amortizationType", element, Locale.getDefault());
baseDataValidator.reset().parameter("amortizationType").value(amortizationType).notNull().inMinMaxRange(0, 1);
}
if (fromApiJsonHelper.parameterExists("interestType", element)) {
final Integer interestType = fromApiJsonHelper.extractIntegerNamed("interestType", element, Locale.getDefault());
baseDataValidator.reset().parameter("interestType").value(interestType).notNull().inMinMaxRange(0, 1);
}
if (fromApiJsonHelper.parameterExists("interestCalculationPeriodType", element)) {
final Integer interestCalculationPeriodType = fromApiJsonHelper.extractIntegerNamed("interestCalculationPeriodType", element,
Locale.getDefault());
baseDataValidator.reset().parameter("interestCalculationPeriodType").value(interestCalculationPeriodType).notNull()
.inMinMaxRange(0, 1);
}
// FIXME - KW - accounting here doesnt allow me to just update one part
// of cash based or accrual based settings?
// accounting related data validation
final Integer accountingRuleType = fromApiJsonHelper.extractIntegerNamed("accountingType", element, Locale.getDefault());
baseDataValidator.reset().parameter("accountingType").value(accountingRuleType).notNull().inMinMaxRange(1, 3);
if (isCashBased(accountingRuleType) || isAccrualBased(accountingRuleType)) {
final Long fundAccountId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue()).value(fundAccountId).notNull()
.integerGreaterThanZero();
final Long loanPortfolioAccountId = fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue()).value(loanPortfolioAccountId)
.notNull().integerGreaterThanZero();
}
if (isAccrualBased(accountingRuleType)) {
final Long receivableInterestAccountId = fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue())
.value(receivableInterestAccountId).notNull().integerGreaterThanZero();
final Long receivableFeeAccountId = fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue()).value(receivableFeeAccountId)
.notNull().integerGreaterThanZero();
final Long receivablePenaltyAccountId = fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue())
.value(receivablePenaltyAccountId).notNull().integerGreaterThanZero();
}
if (isCashBased(accountingRuleType) || isAccrualBased(accountingRuleType)) {
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)
.notNull().integerGreaterThanZero();
final Long incomeFromFeeId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(),
element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue()).value(incomeFromFeeId)
.notNull().integerGreaterThanZero();
final Long incomeFromPenaltyId = fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue()).value(incomeFromPenaltyId)
.notNull().integerGreaterThanZero();
final Long writeOffAccountId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(),
element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue()).value(writeOffAccountId)
.notNull().integerGreaterThanZero();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private boolean isCashBased(final Integer accountingRuleType) {
return AccountingRuleType.CASH_BASED.getValue().equals(accountingRuleType);
}
private boolean isAccrualBased(final Integer accountingRuleType) {
return AccountingRuleType.ACCRUAL_BASED.getValue().equals(accountingRuleType);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
}
@@ -6,11 +6,11 @@ import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
public interface LoanProductReadPlatformService {
Collection<LoanProductData> retrieveAllLoanProducts();
Collection<LoanProductData> retrieveAllLoanProducts();
Collection<LoanProductData> retrieveAllLoanProductsForLookup();
Collection<LoanProductData> retrieveAllLoanProductsForLookup();
LoanProductData retrieveLoanProduct(Long productId);
LoanProductData retrieveLoanProduct(Long productId);
LoanProductData retrieveNewLoanProductDetails();
LoanProductData retrieveNewLoanProductDetails();
}
@@ -19,7 +19,6 @@ import org.mifosplatform.portfolio.fund.exception.FundNotFoundException;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionProcessingStrategyRepository;
import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionProcessingStrategyNotFoundException;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
import org.mifosplatform.portfolio.loanproduct.command.LoanProductCommand;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository;
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
@@ -65,24 +64,29 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
this.context.authenticatedUser();
final LoanProductCommand loanProductCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
loanProductCommand.validateForCreate();
this.fromApiJsonDeserializer.validateForCreate(command.json());
// associating fund with loan product at creation is optional for now.
final Fund fund = findFundByIdIfProvided(command.longValueOfParameterNamed("fundId"));
final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(command
.longValueOfParameterNamed("transactionProcessingStrategyId"));
final Set<Charge> charges = this.assembleSetOfCharges(command, null);
final Long transactionProcessingStrategyId = command.longValueOfParameterNamed("transactionProcessingStrategyId");
final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(transactionProcessingStrategyId);
final String currencyCode = command.stringValueOfParameterNamed("currencyCode");
final Set<Charge> charges = this.assembleSetOfCharges(command, currencyCode);
final LoanProduct loanproduct = LoanProduct.assembleFromJson(fund, loanTransactionProcessingStrategy, charges, command,
this.aprCalculator);
this.loanProductRepository.save(loanproduct);
//save accounting mappings
accountMappingWritePlatformService.createLoanProductToGLAccountMapping(loanproduct.getId(), loanProductCommand);
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(loanproduct.getId()).build();
// save accounting mappings
accountMappingWritePlatformService.createLoanProductToGLAccountMapping(loanproduct.getId(), command);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(loanproduct.getId()) //
.build();
}
private LoanTransactionProcessingStrategy findStrategyByIdIfProvided(final Long transactionProcessingStrategyId) {
@@ -108,13 +112,12 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
public CommandProcessingResult updateLoanProduct(final Long loanProductId, final JsonCommand command) {
this.context.authenticatedUser();
final LoanProductCommand loanProductCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
loanProductCommand.validateForUpdate();
this.fromApiJsonDeserializer.validateForUpdate(command.json());
final LoanProduct product = this.loanProductRepository.findOne(loanProductId);
if (product == null) { throw new LoanProductNotFoundException(loanProductId); }
final Map<String, Object> changes = product.update(command, this.aprCalculator);
// associating fund with loan product at creation is optional for now.
@@ -129,23 +132,27 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(transactionProcessingStrategyId);
product.update(loanTransactionProcessingStrategy);
}
if (changes.containsKey("charges")) {
final Set<Charge> charges = this.assembleSetOfCharges(command, product.getCurrency().getCode());
product.update(charges);
}
// accounting related changes
if (loanProductCommand.getAccountingType() != null) {
boolean accountingTypeChanged = changes.containsKey("accountingType");
accountMappingWritePlatformService.updateLoanProductToGLAccountMapping(product.getId(), loanProductCommand, accountingTypeChanged);
boolean accountingTypeChanged = changes.containsKey("accountingType");
if (accountingTypeChanged) {
accountMappingWritePlatformService.updateLoanProductToGLAccountMapping(product.getId(), command, accountingTypeChanged);
}
if (!changes.isEmpty()) {
this.loanProductRepository.save(product);
}
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(loanProductId).with(changes).build();
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(loanProductId) //
.with(changes) //
.build();
}
private Set<Charge> assembleSetOfCharges(final JsonCommand command, final String currencyCode) {
@@ -176,5 +183,5 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
return charges;
}
}