This is a combination of 2 commits.
Addressing review comments for Loan-Accounting bridge
This commit is contained in:
+4
-1
@@ -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;
|
||||
|
||||
+3
-2
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+17
@@ -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);
|
||||
}
|
||||
}
|
||||
+135
@@ -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); }
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -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);
|
||||
}
|
||||
+4
-3
@@ -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);
|
||||
|
||||
+342
-182
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+7
-7
@@ -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() {
|
||||
|
||||
+8
-2
@@ -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()) {
|
||||
|
||||
+58
-70
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+10
@@ -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;
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-4
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user