blob: 8e0c87b7500ebddc6c288f2d99a7e1d566313009 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.producttoaccountmapping.service;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.accounting.common.AccountingConstants.ACCRUAL_ACCOUNTS_FOR_LOAN;
import org.apache.fineract.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_LOAN;
import org.apache.fineract.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_SAVINGS;
import org.apache.fineract.accounting.common.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
import org.apache.fineract.accounting.common.AccountingConstants.SAVINGS_PRODUCT_ACCOUNTING_PARAMS;
import org.apache.fineract.accounting.producttoaccountmapping.serialization.ProductToGLAccountMappingFromApiJsonDeserializer;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.savings.DepositAccountType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.google.gson.JsonElement;
@Service
public class ProductToGLAccountMappingWritePlatformServiceImpl implements ProductToGLAccountMappingWritePlatformService {
private final FromJsonHelper fromApiJsonHelper;
private final ProductToGLAccountMappingFromApiJsonDeserializer deserializer;
private final LoanProductToGLAccountMappingHelper loanProductToGLAccountMappingHelper;
private final SavingsProductToGLAccountMappingHelper savingsProductToGLAccountMappingHelper;
@Autowired
public ProductToGLAccountMappingWritePlatformServiceImpl(final FromJsonHelper fromApiJsonHelper,
final ProductToGLAccountMappingFromApiJsonDeserializer deserializer,
final LoanProductToGLAccountMappingHelper loanProductToGLAccountMappingHelper,
final SavingsProductToGLAccountMappingHelper savingsProductToGLAccountMappingHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
this.deserializer = deserializer;
this.loanProductToGLAccountMappingHelper = loanProductToGLAccountMappingHelper;
this.savingsProductToGLAccountMappingHelper = savingsProductToGLAccountMappingHelper;
}
@Override
@Transactional
public void createLoanProductToGLAccountMapping(final Long loanProductId, final JsonCommand command) {
final JsonElement element = this.fromApiJsonHelper.parse(command.json());
final Integer accountingRuleTypeId = this.fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(accountingRuleTypeId);
switch (accountingRuleType) {
case NONE:
break;
case CASH_BASED:
// asset
this.loanProductToGLAccountMappingHelper
.saveLoanToAssetAccountMapping(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE.getValue());
this.loanProductToGLAccountMappingHelper.saveLoanToAssetAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue());
this.loanProductToGLAccountMappingHelper.saveLoanToAssetAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.TRANSFERS_SUSPENSE.getValue());
// income
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS.getValue());
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue());
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue());
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_RECOVERY.getValue());
// expenses
this.loanProductToGLAccountMappingHelper.saveLoanToExpenseAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue());
// liabilities
this.loanProductToGLAccountMappingHelper
.saveLoanToLiabilityAccountMapping(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.OVERPAYMENT.getValue());
// advanced accounting mappings
this.loanProductToGLAccountMappingHelper.savePaymentChannelToFundSourceMappings(command, element, loanProductId, null);
this.loanProductToGLAccountMappingHelper.saveChargesToIncomeAccountMappings(command, element, loanProductId, null);
break;
case ACCRUAL_UPFRONT:
// Fall Through
case ACCRUAL_PERIODIC:
// assets (including receivables)
this.loanProductToGLAccountMappingHelper.saveLoanToAssetAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE.getValue());
this.loanProductToGLAccountMappingHelper.saveLoanToAssetAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue());
this.loanProductToGLAccountMappingHelper.saveLoanToAssetAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.TRANSFERS_SUSPENSE.getValue());
this.loanProductToGLAccountMappingHelper.saveLoanToAssetAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE.getValue());
this.loanProductToGLAccountMappingHelper.saveLoanToAssetAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.FEES_RECEIVABLE.getValue());
this.loanProductToGLAccountMappingHelper.saveLoanToAssetAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE.getValue());
// income
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS.getValue());
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue());
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue());
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_RECOVERY.getValue());
// expenses
this.loanProductToGLAccountMappingHelper.saveLoanToExpenseAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue());
// liabilities
this.loanProductToGLAccountMappingHelper.saveLoanToLiabilityAccountMapping(element,
LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.OVERPAYMENT.getValue());
// advanced accounting mappings
this.loanProductToGLAccountMappingHelper.savePaymentChannelToFundSourceMappings(command, element, loanProductId, null);
this.loanProductToGLAccountMappingHelper.saveChargesToIncomeAccountMappings(command, element, loanProductId, null);
break;
}
}
@Override
@Transactional
public void createSavingProductToGLAccountMapping(final Long savingProductId, final JsonCommand command, DepositAccountType accountType) {
final JsonElement element = this.fromApiJsonHelper.parse(command.json());
final Integer accountingRuleTypeId = this.fromApiJsonHelper.extractIntegerNamed(accountingRuleParamName, element,
Locale.getDefault());
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(accountingRuleTypeId);
switch (accountingRuleType) {
case NONE:
break;
case CASH_BASED:
// asset
this.savingsProductToGLAccountMappingHelper.saveSavingsToAssetAccountMapping(element,
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_REFERENCE.getValue(), savingProductId,
CASH_ACCOUNTS_FOR_SAVINGS.SAVINGS_REFERENCE.getValue());
if (!accountType.equals(DepositAccountType.RECURRING_DEPOSIT) && !accountType.equals(DepositAccountType.FIXED_DEPOSIT)) {
this.savingsProductToGLAccountMappingHelper.saveSavingsToAssetAccountMapping(element,
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingProductId,
CASH_ACCOUNTS_FOR_SAVINGS.OVERDRAFT_PORTFOLIO_CONTROL.getValue());
}
// income
this.savingsProductToGLAccountMappingHelper.saveSavingsToIncomeAccountMapping(element,
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), savingProductId,
CASH_ACCOUNTS_FOR_SAVINGS.INCOME_FROM_FEES.getValue());
this.savingsProductToGLAccountMappingHelper.saveSavingsToIncomeAccountMapping(element,
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), savingProductId,
CASH_ACCOUNTS_FOR_SAVINGS.INCOME_FROM_PENALTIES.getValue());
if (!accountType.equals(DepositAccountType.RECURRING_DEPOSIT) && !accountType.equals(DepositAccountType.FIXED_DEPOSIT)) {
this.savingsProductToGLAccountMappingHelper.saveSavingsToIncomeAccountMapping(element,
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_INTEREST.getValue(), savingProductId,
CASH_ACCOUNTS_FOR_SAVINGS.INCOME_FROM_INTEREST.getValue());
}
// expenses
this.savingsProductToGLAccountMappingHelper.saveSavingsToExpenseAccountMapping(element,
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_SAVINGS.getValue(), savingProductId,
CASH_ACCOUNTS_FOR_SAVINGS.INTEREST_ON_SAVINGS.getValue());
if (!accountType.equals(DepositAccountType.RECURRING_DEPOSIT) && !accountType.equals(DepositAccountType.FIXED_DEPOSIT)) {
this.savingsProductToGLAccountMappingHelper.saveSavingsToExpenseAccountMapping(element,
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), savingProductId,
CASH_ACCOUNTS_FOR_SAVINGS.LOSSES_WRITTEN_OFF.getValue());
}
// liability
this.savingsProductToGLAccountMappingHelper.saveSavingsToLiabilityAccountMapping(element,
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_CONTROL.getValue(), savingProductId,
CASH_ACCOUNTS_FOR_SAVINGS.SAVINGS_CONTROL.getValue());
this.savingsProductToGLAccountMappingHelper.saveSavingsToLiabilityAccountMapping(element,
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), savingProductId,
CASH_ACCOUNTS_FOR_SAVINGS.TRANSFERS_SUSPENSE.getValue());
// advanced accounting mappings
this.savingsProductToGLAccountMappingHelper.savePaymentChannelToFundSourceMappings(command, element, savingProductId, null);
this.savingsProductToGLAccountMappingHelper.saveChargesToIncomeAccountMappings(command, element, savingProductId, null);
break;
default:
break;
}
}
@Override
@Transactional
public Map<String, Object> updateLoanProductToGLAccountMapping(final Long loanProductId, final JsonCommand command,
final boolean accountingRuleChanged, final int accountingRuleTypeId) {
/***
* Variable tracks all accounting mapping properties that have been
* updated
***/
Map<String, Object> changes = new HashMap<>();
final JsonElement element = this.fromApiJsonHelper.parse(command.json());
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(accountingRuleTypeId);
/***
* If the accounting rule has been changed, delete all existing mapping
* for the product and recreate a new set of mappings
***/
if (accountingRuleChanged) {
this.deserializer.validateForLoanProductCreate(command.json());
this.loanProductToGLAccountMappingHelper.deleteLoanProductToGLAccountMapping(loanProductId);
createLoanProductToGLAccountMapping(loanProductId, command);
changes = this.loanProductToGLAccountMappingHelper.populateChangesForNewLoanProductToGLAccountMappingCreation(element,
accountingRuleType);
}/*** else examine and update individual changes ***/
else {
this.loanProductToGLAccountMappingHelper.handleChangesToLoanProductToGLAccountMappings(loanProductId, changes, element,
accountingRuleType);
this.loanProductToGLAccountMappingHelper.updatePaymentChannelToFundSourceMappings(command, element, loanProductId, changes);
this.loanProductToGLAccountMappingHelper.updateChargesToIncomeAccountMappings(command, element, loanProductId, changes);
}
return changes;
}
@Override
public Map<String, Object> updateSavingsProductToGLAccountMapping(final Long savingsProductId, final JsonCommand command,
final boolean accountingRuleChanged, final int accountingRuleTypeId, final DepositAccountType accountType) {
/***
* Variable tracks all accounting mapping properties that have been
* updated
***/
Map<String, Object> changes = new HashMap<>();
final JsonElement element = this.fromApiJsonHelper.parse(command.json());
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(accountingRuleTypeId);
/***
* If the accounting rule has been changed, delete all existing mapping
* for the product and recreate a new set of mappings
***/
if (accountingRuleChanged) {
this.deserializer.validateForSavingsProductCreate(command.json(), accountType);
this.savingsProductToGLAccountMappingHelper.deleteSavingsProductToGLAccountMapping(savingsProductId);
createSavingProductToGLAccountMapping(savingsProductId, command, accountType);
changes = this.savingsProductToGLAccountMappingHelper.populateChangesForNewSavingsProductToGLAccountMappingCreation(element,
accountingRuleType);
}/*** else examine and update individual changes ***/
else {
this.savingsProductToGLAccountMappingHelper.handleChangesToSavingsProductToGLAccountMappings(savingsProductId, changes,
element, accountingRuleType);
this.savingsProductToGLAccountMappingHelper.updatePaymentChannelToFundSourceMappings(command, element, savingsProductId,
changes);
this.savingsProductToGLAccountMappingHelper.updateChargesToIncomeAccountMappings(command, element, savingsProductId, changes);
}
return changes;
}
}