This is a combination of 2 commits.

Addressing review comments for Loan-Accounting bridge
This commit is contained in:
Vishwas Babu AJ
2013-02-08 15:47:51 +05:30
parent b84abcee2e
commit 8776d1e13a
15 changed files with 608 additions and 276 deletions
@@ -24,6 +24,8 @@ public class GLJournalEntryData {
@SuppressWarnings("unused")
private final Long glAccountId;
@SuppressWarnings("unused")
private final String glAccountCode;
@SuppressWarnings("unused")
private final EnumOptionData glAccountClassification;
@SuppressWarnings("unused")
private final LocalDate entryDate;
@@ -50,7 +52,7 @@ public class GLJournalEntryData {
@SuppressWarnings("unused")
private final Boolean reversed;
public GLJournalEntryData(Long id, Long officeId, String officeName, String glAccountName, Long glAccountId,
public GLJournalEntryData(Long id, Long officeId, String officeName, String glAccountName, Long glAccountId, String glAccountCode,
EnumOptionData glAccountClassification, LocalDate entryDate, EnumOptionData entryType, BigDecimal amount, String transactionId,
Boolean portfolioGenerated, String entityType, Long entityId, Long createdByUserId, LocalDate createdDate,
String createdByUserName, String comments, Boolean reversed) {
@@ -59,6 +61,7 @@ public class GLJournalEntryData {
this.officeName = officeName;
this.glAccountName = glAccountName;
this.glAccountId = glAccountId;
this.glAccountCode = glAccountCode;
this.glAccountClassification = glAccountClassification;
this.entryDate = entryDate;
this.entryType = entryType;
@@ -35,8 +35,9 @@ public class GoogleGsonAccountingApiJsonSerializerService implements AccountingA
"lastUpdatedByUsername", "comments", "allowedOffices"));
private static final Set<String> GL_JOURNAL_ENTRY_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "officeId", "officeName",
"glAccountName", "glAccountId", "glAccountClassification", "entryDate", "entryType", "amount", "transactionId",
"portfolioGenerated", "entityType", "entityId", "createdByUserId", "createdDate", "createdByUserName", "comments", "reversed"));
"glAccountName", "glAccountId", "glAccountCode", "glAccountClassification", "entryDate", "entryType", "amount",
"transactionId", "portfolioGenerated", "entityType", "entityId", "createdByUserId", "createdDate", "createdByUserName",
"comments", "reversed"));
private final GoogleGsonSerializerHelper helper;
@@ -42,7 +42,6 @@ public class GLAccount extends AbstractPersistable<Long> {
@Column(name = "manual_journal_entries_allowed", nullable = false)
private boolean manualEntriesAllowed = true;
@SuppressWarnings("unused")
@Column(name = "classification_enum", nullable = false)
private Integer classification;
@@ -133,4 +132,12 @@ public class GLAccount extends AbstractPersistable<Long> {
return this.name;
}
public Integer getClassification() {
return this.classification;
}
public void setClassification(Integer classification) {
this.classification = classification;
}
}
@@ -0,0 +1,17 @@
package org.mifosplatform.accounting.exceptions;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* A {@link RuntimeException} thrown when product to GL account mapping are not
* found.
*/
public class ProductToGLAccountMappingInvalidException extends AbstractPlatformDomainRuleException {
public ProductToGLAccountMappingInvalidException(final String paramName, String accountName, Long accountId,
String actualAccountCategory, String expectedAccountCategory) {
super("error.msg." + paramName + ".invalid.account.type", "Passed in GLAccount " + paramName + " with Id " + accountId
+ "maps to the account " + accountName + " of type " + actualAccountCategory + ", the expected account type was "
+ expectedAccountCategory, paramName, accountId, accountName, actualAccountCategory, expectedAccountCategory);
}
}
@@ -0,0 +1,135 @@
package org.mifosplatform.accounting.serialization;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.accounting.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
import org.mifosplatform.accounting.domain.GLAccount;
import org.mifosplatform.accounting.service.ProductToGLAccountMappingWritePlatformService;
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.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.loanproduct.domain.AccountingRuleType;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.JsonElement;
/**
* TODO Vishwas find a better approach for validation
*
* Currently, validation of the passed in JSON is done before calling save or
* update method on the target resource (in our case the loan Product)
*
* However, in the case of a loan product it would be difficult to validate the
* passed in JSON for valid {@link LoanProduct} to {@link GLAccount} mappings
* during update because of the following scenario
*
* The accounting rule type may be changed in the update command, so we would
* have to validate if all required account heads for a particular account type
* have been passed in (would be different for CASH and Accrual based). However,
* till we have access to the domain object it would not be possible to detect
* if an accounting rule has actually been changed
*
* Hence, method {@link #validateForCreate(String)} from this class is called
* separately for validation only if an accounting rule change is detected by
* {@link ProductToGLAccountMappingWritePlatformService}
*
* Also, the class is probably named wrong (*FromApiJsonDeserializer) should
* probably be named as (*Validator) instead
*
*/
@Component
public final class ProductToGLAccountMappingFromApiJsonDeserializer {
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public ProductToGLAccountMappingFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateForCreate(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loanproduct");
final JsonElement element = fromApiJsonHelper.parse(json);
// accounting related data validation
final Integer accountingRuleType = fromApiJsonHelper.extractIntegerNamed("accountingType", element, Locale.getDefault());
baseDataValidator.reset().parameter("accountingType").value(accountingRuleType).notNull().inMinMaxRange(1, 3);
if (isCashBasedAccounting(accountingRuleType) || isAccrualBasedAccounting(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();
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();
}
if (isAccrualBasedAccounting(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();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private boolean isCashBasedAccounting(final Integer accountingRuleType) {
return AccountingRuleType.CASH_BASED.getValue().equals(accountingRuleType);
}
private boolean isAccrualBasedAccounting(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); }
}
}
@@ -8,7 +8,8 @@ public interface ProductToGLAccountMappingWritePlatformService {
void createLoanProductToGLAccountMapping(Long loanProductId, JsonCommand command);
Map<String, Object> updateLoanProductToGLAccountMapping(Long loanProductId, JsonCommand command, boolean accountingRuleChanged);
Map<String, Object> updateLoanProductToGLAccountMapping(Long loanProductId, JsonCommand command, boolean accountingRuleChanged,
int accountingRuleTypeId);
void deleteLoanProductToGLAccountMapping(Long loanProductId);
}
@@ -37,7 +37,7 @@ public class GLJournalEntryReadPlatformServiceImpl implements GLJournalEntryRead
public String schema() {
return " journalEntry.id as journalEntryId, glAccount.classification_enum as classification ,"
+ " glAccount.name as glAccountName, journalEntry.account_id as glAccountId,"
+ " glAccount.name as glAccountName, glAccount.gl_code as glCode, journalEntry.account_id as glAccountId,"
+ " journalEntry.office_id as officeId, office.name as officeName, "
+ " journalEntry.portfolio_generated as portfolioGenerated,journalEntry.entry_date as entryDate, "
+ " journalEntry.type_enum as entryType,journalEntry.amount as amount, journalEntry.transaction_id as transactionId,"
@@ -55,6 +55,7 @@ public class GLJournalEntryReadPlatformServiceImpl implements GLJournalEntryRead
Long journalEntryId = rs.getLong("journalEntryId");
Long officeId = rs.getLong("officeId");
String officeName = rs.getString("officeName");
String glCode = rs.getString("glCode");
String glAccountName = rs.getString("glAccountName");
Long glAccountId = rs.getLong("glAccountId");
final int accountTypeId = JdbcSupport.getInteger(rs, "classification");
@@ -73,7 +74,7 @@ public class GLJournalEntryReadPlatformServiceImpl implements GLJournalEntryRead
String comments = rs.getString("comments");
Boolean reversed = rs.getBoolean("reversed");
return new GLJournalEntryData(journalEntryId, officeId, officeName, glAccountName, glAccountId, accountType, entryDate,
return new GLJournalEntryData(journalEntryId, officeId, officeName, glAccountName, glAccountId, glCode, accountType, entryDate,
entryType, amount, transactionId, portfolioGenerated, entityType, entityId, createdByUserId, createdDate,
createdByUserName, comments, reversed);
}
@@ -133,7 +134,7 @@ public class GLJournalEntryReadPlatformServiceImpl implements GLJournalEntryRead
}
}
sql += " order by journalEntry.entry_date";
sql += " order by journalEntry.entry_date,journalEntry.transaction_id";
Object[] finalObjectArray = Arrays.copyOf(objectArray, arrayPos);
return this.jdbcTemplate.query(sql, rm, finalObjectArray);
@@ -10,11 +10,14 @@ import org.mifosplatform.accounting.AccountingConstants.CASH_ACCOUNTS_FOR_LOAN;
import org.mifosplatform.accounting.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
import org.mifosplatform.accounting.domain.GLAccount;
import org.mifosplatform.accounting.domain.GLAccountRepository;
import org.mifosplatform.accounting.domain.GLAccountType;
import org.mifosplatform.accounting.domain.PortfolioProductType;
import org.mifosplatform.accounting.domain.ProductToGLAccountMapping;
import org.mifosplatform.accounting.domain.ProductToGLAccountMappingRepository;
import org.mifosplatform.accounting.exceptions.GLAccountNotFoundException;
import org.mifosplatform.accounting.exceptions.ProductToGLAccountMappingInvalidException;
import org.mifosplatform.accounting.exceptions.ProductToGLAccountMappingNotFoundException;
import org.mifosplatform.accounting.serialization.ProductToGLAccountMappingFromApiJsonDeserializer;
import org.mifosplatform.accounting.service.ProductToGLAccountMappingWritePlatformService;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
@@ -31,75 +34,72 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
private final GLAccountRepository accountRepository;
private final ProductToGLAccountMappingRepository accountMappingRepository;
private final FromJsonHelper fromApiJsonHelper;
private final ProductToGLAccountMappingFromApiJsonDeserializer deserializer;
@Autowired
public ProductToGLAccountMappingWritePlatformServiceImpl(final GLAccountRepository glAccountRepository,
final ProductToGLAccountMappingRepository glAccountMappingRepository, final FromJsonHelper fromApiJsonHelper) {
final ProductToGLAccountMappingRepository glAccountMappingRepository, final FromJsonHelper fromApiJsonHelper,
final ProductToGLAccountMappingFromApiJsonDeserializer deserializer) {
this.accountRepository = glAccountRepository;
this.accountMappingRepository = glAccountMappingRepository;
this.fromApiJsonHelper = fromApiJsonHelper;
this.deserializer = deserializer;
}
@Override
@Transactional
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 JsonElement element = this.fromApiJsonHelper.parse(command.json());
final Integer accountingRuleTypeId = this.fromApiJsonHelper.extractIntegerNamed("accountingType", element, Locale.getDefault());
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(accountingRuleTypeId);
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);
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());
saveLoanToAssetAccountMapping(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE.getValue());
saveLoanToAssetAccountMapping(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue());
// income
saveLoanToAccountMapping(incomeFromFeeId, loanProductId, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue());
saveLoanToAccountMapping(incomeFromPenaltyId, loanProductId, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue());
saveLoanToIncomeAccountMapping(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS.getValue());
saveLoanToIncomeAccountMapping(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue());
saveLoanToIncomeAccountMapping(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue());
// expenses
saveLoanToAccountMapping(writeOffAccountId, loanProductId, CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue());
saveLoanToExpenseAccountMapping(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), 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);
// 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());
saveLoanToAccountMapping(receivableInterestAccountId, loanProductId,
saveLoanToAssetAccountMapping(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE.getValue());
saveLoanToAssetAccountMapping(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue());
saveLoanToAssetAccountMapping(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE.getValue());
saveLoanToAccountMapping(receivableFeeAccountId, loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.FEES_RECEIVABLE.getValue());
saveLoanToAccountMapping(receivablePenaltyAccountId, loanProductId,
saveLoanToAssetAccountMapping(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.FEES_RECEIVABLE.getValue());
saveLoanToAssetAccountMapping(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), 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());
saveLoanToIncomeAccountMapping(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS.getValue());
saveLoanToIncomeAccountMapping(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue());
saveLoanToIncomeAccountMapping(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue());
// expenses
saveLoanToAccountMapping(writeOffAccountId, loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue());
saveLoanToExpenseAccountMapping(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue());
break;
}
}
@@ -107,162 +107,322 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
@Override
@Transactional
public Map<String, Object> updateLoanProductToGLAccountMapping(final Long loanProductId, final JsonCommand command,
boolean accountingRuleChanged) {
final boolean accountingRuleChanged, int accountingRuleTypeId) {
/***
* Variable tracks all accounting mapping properties that have been
* updated
***/
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 JsonElement element = this.fromApiJsonHelper.parse(command.json());
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 the accounting rule has been changed, delete all existing mapping
* for the product and recreate a new set of mappings
***/
if (accountingRuleChanged) {
deleteLoanProductToGLAccountMapping(loanProductId);
createLoanProductToGLAccountMapping(loanProductId, command);
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);
// 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);
// 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);
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(receivableFeeAccountId, loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.FEES_RECEIVABLE.getValue(),
ACCRUAL_ACCOUNTS_FOR_LOAN.FEES_RECEIVABLE.toString(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.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);
// 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;
}
deserializer.validateForCreate(command.json());
handleChangesToAccountingRuleType(loanProductId, command, changes, element, accountingRuleType);
}/*** else examine and update individual changes ***/
else {
handleChangesToLoanProductToGLAccountMappings(loanProductId, changes, element, accountingRuleType);
}
return changes;
}
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,
PortfolioProductType.LOAN.getValue(), accountTypeId);
accountMappingRepository.save(accountMapping);
}
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);
if (accountMapping == null) { throw new ProductToGLAccountMappingNotFoundException(PortfolioProductType.LOAN, productId,
accountTypeName); }
if (accountMapping.getGlAccount().getId() != glAccountId) {
GLAccount glAccount = accountRepository.findOne(glAccountId);
if (glAccount == null) { throw new GLAccountNotFoundException(glAccountId); }
changes.put(accountParamValue, glAccountId);
accountMapping.setGlAccount(glAccount);
accountMappingRepository.save(accountMapping);
}
}
@Override
public void deleteLoanProductToGLAccountMapping(final Long loanProductId) {
List<ProductToGLAccountMapping> productToGLAccountMappings = accountMappingRepository.findByProductIdAndProductType(loanProductId,
PortfolioProductType.LOAN.getValue());
final List<ProductToGLAccountMapping> productToGLAccountMappings = this.accountMappingRepository.findByProductIdAndProductType(
loanProductId, PortfolioProductType.LOAN.getValue());
if (productToGLAccountMappings != null && productToGLAccountMappings.size() > 0) {
accountMappingRepository.deleteInBatch(productToGLAccountMappings);
this.accountMappingRepository.deleteInBatch(productToGLAccountMappings);
}
}
/**
* Examines and updates each account mapping for given loan product with
* changes passed in from the Json element
*
* @param loanProductId
* @param changes
* @param element
* @param accountingRuleType
*/
private void handleChangesToLoanProductToGLAccountMappings(final Long loanProductId, final Map<String, Object> changes,
final JsonElement element, final AccountingRuleType accountingRuleType) {
switch (accountingRuleType) {
case NONE:
break;
case CASH_BASED:
// asset
mergeLoanToAssetAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE.getValue(), CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE.toString(), changes);
mergeLoanToAssetAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue(), CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.toString(), changes);
// income
mergeLoanToIncomeAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS.getValue(), CASH_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS.toString(), changes);
mergeLoanToIncomeAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue(), CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.toString(), changes);
mergeLoanToIncomeAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(),
loanProductId, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue(),
CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.toString(), changes);
// expenses
mergeLoanToExpenseAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(),
loanProductId, CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue(),
CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.toString(), changes);
break;
case ACCRUAL_BASED:
// assets (including receivables)
mergeLoanToAssetAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE.getValue(), ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE.toString(), changes);
mergeLoanToAssetAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue(), ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.toString(), changes);
mergeLoanToAssetAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(),
loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE.getValue(),
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE.toString(), changes);
mergeLoanToAssetAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.FEES_RECEIVABLE.getValue(), ACCRUAL_ACCOUNTS_FOR_LOAN.FEES_RECEIVABLE.toString(), changes);
mergeLoanToAssetAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(),
loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE.getValue(),
ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE.toString(), changes);
// income
mergeLoanToIncomeAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS.getValue(), ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS.toString(),
changes);
mergeLoanToIncomeAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue(), ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.toString(),
changes);
mergeLoanToIncomeAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(),
loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue(),
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.toString(), changes);
// expenses
mergeLoanToExpenseAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(),
loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue(),
ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.toString(), changes);
break;
}
}
/**
* delete all existing mapping for a product and recreates a new set of
* mappings (to be used exclusively when the selected accounting rule has
* changed)
*
* @param loanProductId
* @param command
* @param changes
* @param element
* @param accountingRuleType
*/
private void handleChangesToAccountingRuleType(final Long loanProductId, final JsonCommand command, final Map<String, Object> changes,
final JsonElement element, final AccountingRuleType accountingRuleType) {
deleteLoanProductToGLAccountMapping(loanProductId);
createLoanProductToGLAccountMapping(loanProductId, command);
final Long fundAccountId = this.fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), element);
final Long loanPortfolioAccountId = this.fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), element);
final Long incomeFromInterestId = this.fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), element);
final Long incomeFromFeeId = this.fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(),
element);
final Long incomeFromPenaltyId = this.fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), element);
final Long writeOffAccountId = this.fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), element);
final Long receivableInterestAccountId = this.fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), element);
final Long receivableFeeAccountId = this.fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), element);
final Long receivablePenaltyAccountId = this.fromApiJsonHelper.extractLongNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), element);
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;
}
}
/**
* Utility wrapper for
* {@link #saveLoanToAccountMapping(JsonElement, String, Long, int, GLAccountType)}
*
* @param element
* @param paramName
* @param productId
* @param placeHolderTypeId
*/
private void saveLoanToAssetAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId) {
saveLoanToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.ASSET);
}
/**
* Utility wrapper for
* {@link #saveLoanToAccountMapping(JsonElement, String, Long, int, GLAccountType)}
*
* @param element
* @param paramName
* @param productId
* @param placeHolderTypeId
*/
private void saveLoanToIncomeAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId) {
saveLoanToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.INCOME);
}
/**
* Utility wrapper for
* {@link #saveLoanToAccountMapping(JsonElement, String, Long, int, GLAccountType)}
*
* @param element
* @param paramName
* @param productId
* @param placeHolderTypeId
*/
private void saveLoanToExpenseAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId) {
saveLoanToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.EXPENSE);
}
/**
* @param element
* @param paramName
* @param productId
* @param placeHolderTypeId
* @param expectedAccountType
*/
private void saveLoanToAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId, final GLAccountType expectedAccountType) {
final Long accountId = this.fromApiJsonHelper.extractLongNamed(paramName, element);
final GLAccount glAccount = getAccountByIdAndType(paramName, expectedAccountType, accountId);
final ProductToGLAccountMapping accountMapping = new ProductToGLAccountMapping(glAccount, productId,
PortfolioProductType.LOAN.getValue(), placeHolderTypeId);
this.accountMappingRepository.save(accountMapping);
}
/**
* Utility wrapper for
* {@link #mergeLoanToAccountMappingChanges(JsonElement, String, Long, int, String, Map, GLAccountType)}
*
* @param element
* @param paramName
* @param productId
* @param accountTypeId
* @param accountTypeName
* @param changes
*/
private void mergeLoanToAssetAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes) {
mergeLoanToAccountMappingChanges(element, paramName, productId, accountTypeId, accountTypeName, changes, GLAccountType.ASSET);
}
/**
* Utility wrapper for
* {@link #mergeLoanToAccountMappingChanges(JsonElement, String, Long, int, String, Map, GLAccountType)}
*
* @param element
* @param paramName
* @param productId
* @param accountTypeId
* @param accountTypeName
* @param changes
*/
private void mergeLoanToIncomeAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes) {
mergeLoanToAccountMappingChanges(element, paramName, productId, accountTypeId, accountTypeName, changes, GLAccountType.INCOME);
}
/**
* Utility wrapper for
* {@link #mergeLoanToAccountMappingChanges(JsonElement, String, Long, int, String, Map, GLAccountType)}
*
* @param element
* @param paramName
* @param productId
* @param accountTypeId
* @param accountTypeName
* @param changes
*/
private void mergeLoanToExpenseAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes) {
mergeLoanToAccountMappingChanges(element, paramName, productId, accountTypeId, accountTypeName, changes, GLAccountType.EXPENSE);
}
/**
* Method merges changes passed in through JSON element
*
* @param element
* @param paramName
* @param productId
* @param accountTypeId
* @param accountTypeName
* @param changes
* @param expectedAccountType
*/
private void mergeLoanToAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes,
final GLAccountType expectedAccountType) {
final Long accountId = this.fromApiJsonHelper.extractLongNamed(paramName, element);
// get the existing product
if (accountId != null) {
final ProductToGLAccountMapping accountMapping = this.accountMappingRepository
.findByProductIdAndProductTypeAndFinancialAccountType(productId, PortfolioProductType.LOAN.getValue(), accountTypeId);
if (accountMapping == null) { throw new ProductToGLAccountMappingNotFoundException(PortfolioProductType.LOAN, productId,
accountTypeName); }
if (accountMapping.getGlAccount().getId() != accountId) {
final GLAccount glAccount = getAccountByIdAndType(paramName, expectedAccountType, accountId);
changes.put(paramName, accountId);
accountMapping.setGlAccount(glAccount);
this.accountMappingRepository.save(accountMapping);
}
}
}
/**
* Fetches account with a particular Id and throws and Exception it is not
* of the expected Account Category ('DEBIT','CREDIT' etc)
*
* @param paramName
* @param expectedAccountType
* @param accountId
* @return
*/
private GLAccount getAccountByIdAndType(final String paramName, final GLAccountType expectedAccountType, final Long accountId) {
final GLAccount glAccount = this.accountRepository.findOne(accountId);
if (glAccount == null) { throw new GLAccountNotFoundException(accountId); }
// validate account is of the expected Type
if (glAccount.getClassification().intValue() != expectedAccountType.getValue()) { throw new ProductToGLAccountMappingInvalidException(
paramName, glAccount.getName(), accountId, GLAccountType.fromInt(glAccount.getClassification()).toString(),
expectedAccountType.toString()); }
return glAccount;
}
}
@@ -155,7 +155,7 @@ public class LoanProductsApiResource {
Map<String, Object> accountingMappings = null;
if (loanProduct.hasAccountingEnabled()) {
accountingMappings = accountMappingReadPlatformService.fetchAccountMappingDetailsForLoanProduct(productId, loanProduct.accountingRuleType());
accountingMappings = accountMappingReadPlatformService.fetchAccountMappingDetailsForLoanProduct(productId, loanProduct.accountingRuleType().getId().intValue());
loanProduct = LoanProductData.withAccountingMappings(loanProduct, accountingMappings);
}
@@ -11,6 +11,7 @@ import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.portfolio.charge.data.ChargeData;
import org.mifosplatform.portfolio.fund.data.FundData;
import org.mifosplatform.portfolio.loanproduct.domain.AccountingRuleType;
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
@@ -44,8 +45,7 @@ public class LoanProductData {
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 EnumOptionData accountingType;
private final Collection<ChargeData> charges;
@@ -94,7 +94,7 @@ public class LoanProductData {
final Long transactionProcessingStrategyId = null;
final String transactionProcessingStrategyName = null;
final Collection<ChargeData> charges = null;
final Integer accountingType = null;
final EnumOptionData accountingType = null;
return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, loanTermFrequency,
repaymentEvery, interestRatePerPeriod, annualInterestRate, loanTermFrequencyType, repaymentFrequencyType,
@@ -126,7 +126,7 @@ public class LoanProductData {
final Long transactionProcessingStrategyId = null;
final String transactionProcessingStrategyName = null;
final Collection<ChargeData> charges = null;
final Integer accountingType = 1;
final EnumOptionData accountingType = LoanEnumerations.accountingRuleType(AccountingRuleType.NONE);
return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, loanTermFrequency,
repaymentEvery, interestRatePerPeriod, annualInterestRate, loanTermFrequencyType, repaymentFrequencyType,
@@ -150,7 +150,7 @@ public class LoanProductData {
final EnumOptionData interestRateFrequencyType, final EnumOptionData amortizationType, final EnumOptionData interestType,
final EnumOptionData interestCalculationPeriodType, final Long fundId, final String fundName,
final Long transactionProcessingStrategyId, final String transactionProcessingStrategyName,
final Collection<ChargeData> charges, final Integer accountingType) {
final Collection<ChargeData> charges, final EnumOptionData accountingType) {
this.id = id;
this.name = name;
this.description = description;
@@ -261,12 +261,12 @@ public class LoanProductData {
return chargesLocal;
}
public Integer accountingRuleType() {
public EnumOptionData accountingRuleType() {
return this.accountingType;
}
public boolean hasAccountingEnabled() {
return this.accountingType > 1;
return this.accountingType.getId() > 1;
}
public Long getId() {
@@ -5,12 +5,14 @@ import java.util.Map;
public enum AccountingRuleType {
NONE(1), CASH_BASED(2), ACCRUAL_BASED(3);
NONE(1, "accountingRuleType.none"), CASH_BASED(2, "accountingRuleType.cash"), ACCRUAL_BASED(3, "accountingRuleType.accrual");
private final Integer value;
private final String code;
private AccountingRuleType(final Integer value) {
private AccountingRuleType(final Integer value, String code) {
this.value = value;
this.code = code;
}
@Override
@@ -22,6 +24,10 @@ public enum AccountingRuleType {
return this.value;
}
public String getCode() {
return this.code;
}
private static final Map<Integer, AccountingRuleType> intToEnumMap = new HashMap<Integer, AccountingRuleType>();
static {
for (AccountingRuleType type : AccountingRuleType.values()) {
@@ -117,7 +117,7 @@ public final class LoanProductCommandFromApiJsonDeserializer {
final Integer accountingRuleType = fromApiJsonHelper.extractIntegerNamed("accountingType", element, Locale.getDefault());
baseDataValidator.reset().parameter("accountingType").value(accountingRuleType).notNull().inMinMaxRange(1, 3);
if (isCashBased(accountingRuleType) || isAccrualBased(accountingRuleType)) {
if (isCashBasedAccounting(accountingRuleType) || isAccrualBasedAccounting(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()
@@ -127,27 +127,7 @@ public final class LoanProductCommandFromApiJsonDeserializer {
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)
@@ -167,6 +147,25 @@ public final class LoanProductCommandFromApiJsonDeserializer {
element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue()).value(writeOffAccountId)
.notNull().integerGreaterThanZero();
}
if (isAccrualBasedAccounting(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();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
@@ -267,72 +266,61 @@ public final class LoanProductCommandFromApiJsonDeserializer {
.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);
baseDataValidator.reset().parameter("accountingType").value(accountingRuleType).ignoreIfNull().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).ignoreIfNull()
.integerGreaterThanZero();
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)
.ignoreIfNull().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();
}
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)
.ignoreIfNull().integerGreaterThanZero();
if (isAccrualBased(accountingRuleType)) {
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)
.ignoreIfNull().integerGreaterThanZero();
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 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)
.ignoreIfNull().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 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)
.ignoreIfNull().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();
}
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).ignoreIfNull().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 receivableFeeAccountId = fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(),
element);
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue()).value(receivableFeeAccountId)
.ignoreIfNull().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();
}
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).ignoreIfNull().integerGreaterThanZero();
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private boolean isCashBased(final Integer accountingRuleType) {
private boolean isCashBasedAccounting(final Integer accountingRuleType) {
return AccountingRuleType.CASH_BASED.getValue().equals(accountingRuleType);
}
private boolean isAccrualBased(final Integer accountingRuleType) {
private boolean isAccrualBasedAccounting(final Integer accountingRuleType) {
return AccountingRuleType.ACCRUAL_BASED.getValue().equals(accountingRuleType);
}
@@ -3,6 +3,7 @@ package org.mifosplatform.portfolio.loanproduct.service;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType;
import org.mifosplatform.portfolio.loanproduct.domain.AccountingRuleType;
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
@@ -248,4 +249,13 @@ public class LoanEnumerations {
return optionData;
}
public static EnumOptionData accountingRuleType(final int id) {
return accountingRuleType(AccountingRuleType.fromInt(id));
}
public static EnumOptionData accountingRuleType(final AccountingRuleType type) {
EnumOptionData optionData = new EnumOptionData(type.getValue().longValue(), type.getCode(), type.toString());
return optionData;
}
}
@@ -129,7 +129,10 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
final Integer numberOfRepayments = JdbcSupport.getInteger(rs, "numberOfRepayments");
final Integer repaymentEvery = JdbcSupport.getInteger(rs, "repaidEvery");
final Integer accountingType = JdbcSupport.getInteger(rs, "accountingType");
final Integer accountingTypeId = JdbcSupport.getInteger(rs, "accountingType");
final EnumOptionData accountingRuleType = LoanEnumerations.accountingRuleType(accountingTypeId);
final Integer loanTermFrequency = repaymentEvery * numberOfRepayments;
final BigDecimal interestRatePerPeriod = rs.getBigDecimal("interestRatePerPeriod");
final BigDecimal annualInterestRate = rs.getBigDecimal("annualInterestRate");
@@ -155,7 +158,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
return new LoanProductData(id, name, description, currency, principal, tolerance, numberOfRepayments, loanTermFrequency,
repaymentEvery, interestRatePerPeriod, annualInterestRate, loanTermFrequencyType, repaymentFrequencyType,
interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName,
transactionStrategyId, transactionStrategyName, this.charges, accountingType);
transactionStrategyId, transactionStrategyName, this.charges, accountingRuleType);
}
}
@@ -154,12 +154,12 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
final Set<Charge> charges = this.assembleSetOfCharges(command, product.getCurrency().getCode());
product.update(charges);
}
// accounting related changes
boolean accountingTypeChanged = changes.containsKey("accountingType");
if (accountingTypeChanged) {
accountMappingWritePlatformService.updateLoanProductToGLAccountMapping(product.getId(), command, accountingTypeChanged);
}
final Map<String, Object> accountingMappingChanges = accountMappingWritePlatformService.updateLoanProductToGLAccountMapping(
product.getId(), command, accountingTypeChanged, product.getAccountingType());
changes.putAll(accountingMappingChanges);
if (!changes.isEmpty()) {
this.loanProductRepository.saveAndFlush(product);