FINERACT-1724: Move product account mapping packages to core

This commit is contained in:
Adam Saghy
2023-11-03 16:56:40 +01:00
parent a42f46957d
commit f5aa8d22e8
13 changed files with 8 additions and 5 deletions
@@ -0,0 +1,73 @@
/**
* 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.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentType;
@Getter
@Setter
@NoArgsConstructor
@Accessors(chain = true)
@Entity
@Table(name = "acc_product_mapping", uniqueConstraints = { @UniqueConstraint(columnNames = { "product_id", "product_type",
"financial_account_type", "payment_type" }, name = "financial_action") })
public class ProductToGLAccountMapping extends AbstractPersistableCustom {
@ManyToOne(optional = true)
@JoinColumn(name = "gl_account_id")
private GLAccount glAccount;
@Column(name = "product_id", nullable = true)
private Long productId;
@ManyToOne
@JoinColumn(name = "payment_type", nullable = true)
private PaymentType paymentType;
@ManyToOne
@JoinColumn(name = "charge_id", nullable = true)
private Charge charge;
@Column(name = "product_type", nullable = true)
private int productType;
@Column(name = "financial_account_type", nullable = true)
private int financialAccountType;
public static ProductToGLAccountMapping createNew(final GLAccount glAccount, final Long productId, final int productType,
final int financialAccountType) {
return new ProductToGLAccountMapping().setGlAccount(glAccount).setProductId(productId).setProductType(productType)
.setFinancialAccountType(financialAccountType);
}
}
@@ -0,0 +1,64 @@
/**
* 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.domain;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface ProductToGLAccountMappingRepository
extends JpaRepository<ProductToGLAccountMapping, Long>, JpaSpecificationExecutor<ProductToGLAccountMapping> {
ProductToGLAccountMapping findByProductIdAndProductTypeAndFinancialAccountTypeAndPaymentTypeId(Long productId, int productType,
int financialAccountType, Long paymentType);
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId= :productId and mapping.productType= :productType and mapping.financialAccountType= :financialAccountType and mapping.charge.id= :chargeId")
ProductToGLAccountMapping findProductIdAndProductTypeAndFinancialAccountTypeAndChargeId(@Param("productId") Long productId,
@Param("productType") int productType, @Param("financialAccountType") int financialAccountType,
@Param("chargeId") Long ChargeId);
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.financialAccountType=:financialAccountType and mapping.paymentType is NULL and mapping.charge is NULL")
ProductToGLAccountMapping findCoreProductToFinAccountMapping(@Param("productId") Long productId, @Param("productType") int productType,
@Param("financialAccountType") int financialAccountType);
/***
* The financial Account Type for a fund source will always be an asset (1)
***/
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.financialAccountType=1 and mapping.paymentType is not NULL")
List<ProductToGLAccountMapping> findAllPaymentTypeToFundSourceMappings(@Param("productId") Long productId,
@Param("productType") int productType);
/***
* The financial Account Type for income from interest will always be 4
***/
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.financialAccountType=4 and mapping.charge is not NULL")
List<ProductToGLAccountMapping> findAllFeeToIncomeAccountMappings(@Param("productId") Long productId,
@Param("productType") int productType);
/***
* The financial Account Type for income from interest will always be 5
***/
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.financialAccountType=5 and mapping.charge is not NULL")
List<ProductToGLAccountMapping> findAllPenaltyToIncomeAccountMappings(@Param("productId") Long productId,
@Param("productType") int productType);
List<ProductToGLAccountMapping> findByProductIdAndProductType(Long productId, int productType);
}
@@ -0,0 +1,35 @@
/**
* 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.exception;
import org.apache.fineract.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, final String accountName, final Long accountId,
final String actualAccountCategory, final 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 one among" + expectedAccountCategory,
paramName, accountId, accountName, actualAccountCategory, expectedAccountCategory);
}
}
@@ -0,0 +1,33 @@
/**
* 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.exception;
import org.apache.fineract.accounting.producttoaccountmapping.domain.PortfolioProductType;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
/**
* A {@link RuntimeException} thrown when product to GL account mapping are not found.
*/
public class ProductToGLAccountMappingNotFoundException extends AbstractPlatformResourceNotFoundException {
public ProductToGLAccountMappingNotFoundException(final PortfolioProductType type, final Long productId, final String accountType) {
super("error.msg.productToAccountMapping.not.found", "Mapping for product of type " + type.toString() + " with Id " + productId
+ " does not exist for an account of type " + accountType, type.toString(), productId, accountType);
}
}
@@ -0,0 +1,291 @@
/**
* 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.serialization;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.isDormancyTrackingActiveParamName;
import com.google.gson.JsonElement;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.accounting.common.AccountingConstants.LoanProductAccountingParams;
import org.apache.fineract.accounting.common.AccountingConstants.SavingProductAccountingParams;
import org.apache.fineract.accounting.common.AccountingConstants.SharesProductAccountingParams;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.savings.DepositAccountType;
import org.apache.fineract.portfolio.shareproducts.constants.ShareProductApiConstants;
import org.springframework.stereotype.Component;
/**
* 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 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 #validateForLoanProductCreate(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
@RequiredArgsConstructor
public final class ProductToGLAccountMappingFromApiJsonDeserializer {
private final FromJsonHelper fromApiJsonHelper;
public void validateForLoanProductCreate(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loanproduct");
final JsonElement element = this.fromApiJsonHelper.parse(json);
// accounting related data validation
final Integer accountingRuleType = this.fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 4);
if (isCashBasedAccounting(accountingRuleType) || isAccrualBasedAccounting(accountingRuleType)) {
final Long fundAccountId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.FUND_SOURCE.getValue(), element);
baseDataValidator.reset().parameter(LoanProductAccountingParams.FUND_SOURCE.getValue()).value(fundAccountId).notNull()
.integerGreaterThanZero();
final Long loanPortfolioAccountId = this.fromApiJsonHelper
.extractLongNamed(LoanProductAccountingParams.LOAN_PORTFOLIO.getValue(), element);
baseDataValidator.reset().parameter(LoanProductAccountingParams.LOAN_PORTFOLIO.getValue()).value(loanPortfolioAccountId)
.notNull().integerGreaterThanZero();
final Long incomeFromInterestId = this.fromApiJsonHelper
.extractLongNamed(LoanProductAccountingParams.INTEREST_ON_LOANS.getValue(), element);
baseDataValidator.reset().parameter(LoanProductAccountingParams.INTEREST_ON_LOANS.getValue()).value(incomeFromInterestId)
.notNull().integerGreaterThanZero();
final Long incomeFromFeeId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.INCOME_FROM_FEES.getValue(),
element);
baseDataValidator.reset().parameter(LoanProductAccountingParams.INCOME_FROM_FEES.getValue()).value(incomeFromFeeId).notNull()
.integerGreaterThanZero();
final Long incomeFromPenaltyId = this.fromApiJsonHelper
.extractLongNamed(LoanProductAccountingParams.INCOME_FROM_PENALTIES.getValue(), element);
baseDataValidator.reset().parameter(LoanProductAccountingParams.INCOME_FROM_PENALTIES.getValue()).value(incomeFromPenaltyId)
.notNull().integerGreaterThanZero();
final Long incomeFromRecoveryAccountId = this.fromApiJsonHelper
.extractLongNamed(LoanProductAccountingParams.INCOME_FROM_RECOVERY.getValue(), element);
baseDataValidator.reset().parameter(LoanProductAccountingParams.INCOME_FROM_RECOVERY.getValue())
.value(incomeFromRecoveryAccountId).notNull().integerGreaterThanZero();
final Long writeOffAccountId = this.fromApiJsonHelper
.extractLongNamed(LoanProductAccountingParams.LOSSES_WRITTEN_OFF.getValue(), element);
baseDataValidator.reset().parameter(LoanProductAccountingParams.LOSSES_WRITTEN_OFF.getValue()).value(writeOffAccountId)
.notNull().integerGreaterThanZero();
final Long overpaymentAccountId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.OVERPAYMENT.getValue(),
element);
baseDataValidator.reset().parameter(LoanProductAccountingParams.OVERPAYMENT.getValue()).value(overpaymentAccountId).notNull()
.integerGreaterThanZero();
final Long transfersInSuspenseAccountId = this.fromApiJsonHelper
.extractLongNamed(LoanProductAccountingParams.TRANSFERS_SUSPENSE.getValue(), element);
baseDataValidator.reset().parameter(LoanProductAccountingParams.TRANSFERS_SUSPENSE.getValue())
.value(transfersInSuspenseAccountId).notNull().integerGreaterThanZero();
}
if (isAccrualBasedAccounting(accountingRuleType)) {
final Long receivableInterestAccountId = this.fromApiJsonHelper
.extractLongNamed(LoanProductAccountingParams.INTEREST_RECEIVABLE.getValue(), element);
baseDataValidator.reset().parameter(LoanProductAccountingParams.INTEREST_RECEIVABLE.getValue())
.value(receivableInterestAccountId).notNull().integerGreaterThanZero();
final Long receivableFeeAccountId = this.fromApiJsonHelper
.extractLongNamed(LoanProductAccountingParams.FEES_RECEIVABLE.getValue(), element);
baseDataValidator.reset().parameter(LoanProductAccountingParams.FEES_RECEIVABLE.getValue()).value(receivableFeeAccountId)
.notNull().integerGreaterThanZero();
final Long receivablePenaltyAccountId = this.fromApiJsonHelper
.extractLongNamed(LoanProductAccountingParams.PENALTIES_RECEIVABLE.getValue(), element);
baseDataValidator.reset().parameter(LoanProductAccountingParams.PENALTIES_RECEIVABLE.getValue())
.value(receivablePenaltyAccountId).notNull().integerGreaterThanZero();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateForSavingsProductCreate(final String json, DepositAccountType accountType) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SAVINGS_PRODUCT_RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
// accounting related data validation
final Integer accountingRuleType = this.fromApiJsonHelper.extractIntegerNamed(accountingRuleParamName, element,
Locale.getDefault());
baseDataValidator.reset().parameter(accountingRuleParamName).value(accountingRuleType).notNull().inMinMaxRange(1, 3);
if (isCashBasedAccounting(accountingRuleType)) {
final Long savingsControlAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.SAVINGS_CONTROL.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.SAVINGS_CONTROL.getValue()).value(savingsControlAccountId)
.notNull().integerGreaterThanZero();
final Long savingsReferenceAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.SAVINGS_REFERENCE.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.SAVINGS_REFERENCE.getValue()).value(savingsReferenceAccountId)
.notNull().integerGreaterThanZero();
final Long transfersInSuspenseAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.TRANSFERS_SUSPENSE.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.TRANSFERS_SUSPENSE.getValue())
.value(transfersInSuspenseAccountId).notNull().integerGreaterThanZero();
final Long interestOnSavingsAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.INTEREST_ON_SAVINGS.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.INTEREST_ON_SAVINGS.getValue())
.value(interestOnSavingsAccountId).notNull().integerGreaterThanZero();
final Long incomeFromFeeId = this.fromApiJsonHelper.extractLongNamed(SavingProductAccountingParams.INCOME_FROM_FEES.getValue(),
element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.INCOME_FROM_FEES.getValue()).value(incomeFromFeeId).notNull()
.integerGreaterThanZero();
final Long incomeFromPenaltyId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.INCOME_FROM_PENALTIES.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.INCOME_FROM_PENALTIES.getValue()).value(incomeFromPenaltyId)
.notNull().integerGreaterThanZero();
final Boolean isDormancyTrackingActive = this.fromApiJsonHelper.extractBooleanNamed(isDormancyTrackingActiveParamName, element);
if (null != isDormancyTrackingActive && isDormancyTrackingActive) {
final Long escheatLiabilityId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.ESCHEAT_LIABILITY.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.ESCHEAT_LIABILITY.getValue()).value(escheatLiabilityId)
.notNull().integerGreaterThanZero();
}
if (!accountType.equals(DepositAccountType.RECURRING_DEPOSIT) && !accountType.equals(DepositAccountType.FIXED_DEPOSIT)) {
final Long overdraftAccount = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.OVERDRAFT_PORTFOLIO_CONTROL.getValue())
.value(overdraftAccount).notNull().integerGreaterThanZero();
final Long incomeFromInterest = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.INCOME_FROM_INTEREST.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.INCOME_FROM_INTEREST.getValue()).value(incomeFromInterest)
.notNull().integerGreaterThanZero();
final Long writtenOff = this.fromApiJsonHelper.extractLongNamed(SavingProductAccountingParams.LOSSES_WRITTEN_OFF.getValue(),
element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.LOSSES_WRITTEN_OFF.getValue()).value(writtenOff).notNull()
.integerGreaterThanZero();
}
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateForShareProductCreate(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(ShareProductApiConstants.SHARE_PRODUCT_RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
// accounting related data validation
final Integer accountingRuleType = this.fromApiJsonHelper.extractIntegerNamed(accountingRuleParamName, element,
Locale.getDefault());
baseDataValidator.reset().parameter(accountingRuleParamName).value(accountingRuleType).notNull().inMinMaxRange(1, 3);
if (isCashBasedAccounting(accountingRuleType)) {
final Long shareReferenceId = this.fromApiJsonHelper.extractLongNamed(SharesProductAccountingParams.SHARES_REFERENCE.getValue(),
element);
baseDataValidator.reset().parameter(SharesProductAccountingParams.SHARES_REFERENCE.getValue()).value(shareReferenceId).notNull()
.integerGreaterThanZero();
final Long shareSuspenseId = this.fromApiJsonHelper.extractLongNamed(SharesProductAccountingParams.SHARES_SUSPENSE.getValue(),
element);
baseDataValidator.reset().parameter(SharesProductAccountingParams.SHARES_SUSPENSE.getValue()).value(shareSuspenseId).notNull()
.integerGreaterThanZero();
final Long incomeFromFeeAccountId = this.fromApiJsonHelper
.extractLongNamed(SharesProductAccountingParams.INCOME_FROM_FEES.getValue(), element);
baseDataValidator.reset().parameter(SharesProductAccountingParams.INCOME_FROM_FEES.getValue()).value(incomeFromFeeAccountId)
.notNull().integerGreaterThanZero();
final Long shareEquityId = this.fromApiJsonHelper.extractLongNamed(SharesProductAccountingParams.SHARES_EQUITY.getValue(),
element);
baseDataValidator.reset().parameter(SharesProductAccountingParams.SHARES_EQUITY.getValue()).value(shareEquityId).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_PERIODIC.getValue().equals(accountingRuleType)
|| AccountingRuleType.ACCRUAL_UPFRONT.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);
}
}
}
@@ -0,0 +1,463 @@
/**
* 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 com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForLoan;
import org.apache.fineract.accounting.common.AccountingConstants.LoanProductAccountingParams;
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepository;
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
import org.apache.fineract.accounting.producttoaccountmapping.domain.PortfolioProductType;
import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMapping;
import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository;
import org.apache.fineract.accounting.producttoaccountmapping.exception.ProductToGLAccountMappingInvalidException;
import org.apache.fineract.accounting.producttoaccountmapping.exception.ProductToGLAccountMappingNotFoundException;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentType;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepositoryWrapper;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class ProductToGLAccountMappingHelper {
protected static final List<GLAccountType> ASSET_LIABILITY_TYPES = List.of(GLAccountType.ASSET, GLAccountType.LIABILITY);
protected final GLAccountRepository accountRepository;
protected final ProductToGLAccountMappingRepository accountMappingRepository;
protected final FromJsonHelper fromApiJsonHelper;
private final ChargeRepositoryWrapper chargeRepositoryWrapper;
protected final GLAccountRepositoryWrapper accountRepositoryWrapper;
private final PaymentTypeRepositoryWrapper paymentTypeRepositoryWrapper;
public void saveProductToAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId, final GLAccountType expectedAccountType, final PortfolioProductType portfolioProductType) {
final Long accountId = this.fromApiJsonHelper.extractLongNamed(paramName, element);
if (accountId != null) { // optional entries may be null
final GLAccount glAccount = getAccountByIdAndType(paramName, expectedAccountType, accountId);
final ProductToGLAccountMapping accountMapping = new ProductToGLAccountMapping().setGlAccount(glAccount).setProductId(productId)
.setProductType(portfolioProductType.getValue()).setFinancialAccountType(placeHolderTypeId);
this.accountMappingRepository.saveAndFlush(accountMapping);
}
}
public void mergeProductToAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes,
final GLAccountType expectedAccountType, final PortfolioProductType portfolioProductType) {
final Long accountId = this.fromApiJsonHelper.extractLongNamed(paramName, element);
// get the existing product
if (accountId != null) {
final ProductToGLAccountMapping accountMapping = this.accountMappingRepository.findCoreProductToFinAccountMapping(productId,
portfolioProductType.getValue(), accountTypeId);
if (accountMapping == null) {
ArrayList<String> optionalProductToGLAccountMappingEntries = new ArrayList<String>();
optionalProductToGLAccountMappingEntries.add(LoanProductAccountingParams.GOODWILL_CREDIT.getValue());
optionalProductToGLAccountMappingEntries.add(LoanProductAccountingParams.INCOME_FROM_CHARGE_OFF_INTEREST.getValue());
optionalProductToGLAccountMappingEntries.add(LoanProductAccountingParams.INCOME_FROM_CHARGE_OFF_FEES.getValue());
optionalProductToGLAccountMappingEntries.add(LoanProductAccountingParams.CHARGE_OFF_EXPENSE.getValue());
optionalProductToGLAccountMappingEntries.add(LoanProductAccountingParams.CHARGE_OFF_FRAUD_EXPENSE.getValue());
optionalProductToGLAccountMappingEntries.add(LoanProductAccountingParams.INCOME_FROM_CHARGE_OFF_PENALTY.getValue());
optionalProductToGLAccountMappingEntries.add("incomeFromGoodwillCreditInterestAccountId");
optionalProductToGLAccountMappingEntries.add("incomeFromGoodwillCreditFeesAccountId");
optionalProductToGLAccountMappingEntries.add("incomeFromGoodwillCreditPenaltyAccountId");
if (optionalProductToGLAccountMappingEntries.contains(paramName)) {
saveProductToAccountMapping(element, paramName, productId, accountTypeId, expectedAccountType, portfolioProductType);
} else {
throw new ProductToGLAccountMappingNotFoundException(portfolioProductType, productId, accountTypeName);
}
} else {
if (!accountMapping.getGlAccount().getId().equals(accountId)) {
final GLAccount glAccount = getAccountByIdAndType(paramName, expectedAccountType, accountId);
changes.put(paramName, accountId);
accountMapping.setGlAccount(glAccount);
this.accountMappingRepository.saveAndFlush(accountMapping);
}
}
}
}
public void createOrmergeProductToAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final Map<String, Object> changes, final GLAccountType expectedAccountType,
final PortfolioProductType portfolioProductType) {
final Long accountId = this.fromApiJsonHelper.extractLongNamed(paramName, element);
// get the existing product
if (accountId != null) {
final ProductToGLAccountMapping accountMapping = this.accountMappingRepository.findCoreProductToFinAccountMapping(productId,
portfolioProductType.getValue(), accountTypeId);
if (accountMapping == null) {
final GLAccount glAccount = getAccountByIdAndType(paramName, expectedAccountType, accountId);
changes.put(paramName, accountId);
ProductToGLAccountMapping newAccountMapping = new ProductToGLAccountMapping().setGlAccount(glAccount)
.setProductId(productId).setProductType(portfolioProductType.getValue()).setFinancialAccountType(accountTypeId);
this.accountMappingRepository.saveAndFlush(newAccountMapping);
} else if (!accountMapping.getGlAccount().getId().equals(accountId)) {
final GLAccount glAccount = getAccountByIdAndType(paramName, expectedAccountType, accountId);
changes.put(paramName, accountId);
accountMapping.setGlAccount(glAccount);
this.accountMappingRepository.saveAndFlush(accountMapping);
}
}
}
/**
* Saves the payment type to Fund source mappings for a particular product/product type (also populates the changes
* array if passed in)
*
* @param command
* @param element
* @param productId
* @param changes
*/
public void savePaymentChannelToFundSourceMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes, final PortfolioProductType portfolioProductType) {
final JsonArray paymentChannelMappingArray = this.fromApiJsonHelper
.extractJsonArrayNamed(LoanProductAccountingParams.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), element);
if (paymentChannelMappingArray != null) {
if (changes != null) {
changes.put(LoanProductAccountingParams.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(),
command.jsonFragment(LoanProductAccountingParams.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue()));
}
for (int i = 0; i < paymentChannelMappingArray.size(); i++) {
final JsonObject jsonObject = paymentChannelMappingArray.get(i).getAsJsonObject();
final Long paymentTypeId = jsonObject.get(LoanProductAccountingParams.PAYMENT_TYPE.getValue()).getAsLong();
final Long paymentSpecificFundAccountId = jsonObject.get(LoanProductAccountingParams.FUND_SOURCE.getValue()).getAsLong();
savePaymentChannelToFundSourceMapping(productId, paymentTypeId, paymentSpecificFundAccountId, portfolioProductType);
}
}
}
/**
* Saves the Charge to Income / Liability account mappings for a particular product/product type (also populates the
* changes array if passed in)
*
* @param command
* @param element
* @param productId
* @param changes
*/
public void saveChargesToGLAccountMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes, final PortfolioProductType portfolioProductType, final boolean isPenalty) {
String arrayName;
if (isPenalty) {
arrayName = LoanProductAccountingParams.PENALTY_INCOME_ACCOUNT_MAPPING.getValue();
} else {
arrayName = LoanProductAccountingParams.FEE_INCOME_ACCOUNT_MAPPING.getValue();
}
final JsonArray chargeToIncomeAccountMappingArray = this.fromApiJsonHelper.extractJsonArrayNamed(arrayName, element);
if (chargeToIncomeAccountMappingArray != null) {
if (changes != null) {
changes.put(LoanProductAccountingParams.FEE_INCOME_ACCOUNT_MAPPING.getValue(),
command.jsonFragment(LoanProductAccountingParams.FEE_INCOME_ACCOUNT_MAPPING.getValue()));
}
for (int i = 0; i < chargeToIncomeAccountMappingArray.size(); i++) {
final JsonObject jsonObject = chargeToIncomeAccountMappingArray.get(i).getAsJsonObject();
final Long chargeId = jsonObject.get(LoanProductAccountingParams.CHARGE_ID.getValue()).getAsLong();
final Long incomeAccountId = jsonObject.get(LoanProductAccountingParams.INCOME_ACCOUNT_ID.getValue()).getAsLong();
saveChargeToFundSourceMapping(productId, chargeId, incomeAccountId, portfolioProductType, isPenalty);
}
}
}
/**
* @param command
* @param element
* @param productId
* @param changes
*/
public void updateChargeToIncomeAccountMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes, final PortfolioProductType portfolioProductType, final boolean isPenalty) {
// find all existing payment Channel to Fund source Mappings
List<ProductToGLAccountMapping> existingChargeToIncomeAccountMappings;
String arrayFragmentName;
if (isPenalty) {
existingChargeToIncomeAccountMappings = this.accountMappingRepository.findAllPenaltyToIncomeAccountMappings(productId,
portfolioProductType.getValue());
arrayFragmentName = LoanProductAccountingParams.PENALTY_INCOME_ACCOUNT_MAPPING.getValue();
} else {
existingChargeToIncomeAccountMappings = this.accountMappingRepository.findAllFeeToIncomeAccountMappings(productId,
portfolioProductType.getValue());
arrayFragmentName = LoanProductAccountingParams.FEE_INCOME_ACCOUNT_MAPPING.getValue();
}
final JsonArray chargeToIncomeAccountMappingArray = this.fromApiJsonHelper.extractJsonArrayNamed(arrayFragmentName, element);
/**
* Variable stores a map representation of charges (key) and their associated income Id's (value) extracted from
* the passed in Jsoncommand
**/
final Map<Long, Long> inputChargeToIncomeAccountMap = new HashMap<>();
/***
* Variable stores all charges which have already been mapped to Income Accounts in the system
**/
final Set<Long> existingCharges = new HashSet<>();
if (chargeToIncomeAccountMappingArray != null) {
if (changes != null) {
changes.put(arrayFragmentName, command.jsonFragment(arrayFragmentName));
}
for (int i = 0; i < chargeToIncomeAccountMappingArray.size(); i++) {
final JsonObject jsonObject = chargeToIncomeAccountMappingArray.get(i).getAsJsonObject();
final Long chargeId = jsonObject.get(LoanProductAccountingParams.CHARGE_ID.getValue()).getAsLong();
final Long incomeAccountId = jsonObject.get(LoanProductAccountingParams.INCOME_ACCOUNT_ID.getValue()).getAsLong();
inputChargeToIncomeAccountMap.put(chargeId, incomeAccountId);
}
// If input map is empty, delete all existing mappings
if (inputChargeToIncomeAccountMap.size() == 0) {
this.accountMappingRepository.deleteAllInBatch(existingChargeToIncomeAccountMappings);
} /**
* Else, <br/>
* update existing mappings OR <br/>
* delete old mappings (which are already present, but not passed in as a part of Jsoncommand)<br/>
* Create new mappings for charges that are passed in as a part of the Jsoncommand but not already present
*
**/
else {
for (final ProductToGLAccountMapping chargeToIncomeAccountMapping : existingChargeToIncomeAccountMappings) {
final Long currentCharge = chargeToIncomeAccountMapping.getCharge().getId();
existingCharges.add(currentCharge);
// update existing mappings (if required)
if (inputChargeToIncomeAccountMap.containsKey(currentCharge)) {
final Long newGLAccountId = inputChargeToIncomeAccountMap.get(currentCharge);
if (!newGLAccountId.equals(chargeToIncomeAccountMapping.getGlAccount().getId())) {
final GLAccount glAccount;
if (isPenalty) {
glAccount = getAccountByIdAndType(LoanProductAccountingParams.INCOME_ACCOUNT_ID.getValue(),
GLAccountType.INCOME, newGLAccountId);
} else {
List<GLAccountType> allowedAccountTypes = getAllowedAccountTypesForFeeMapping();
glAccount = getAccountByIdAndType(LoanProductAccountingParams.INCOME_ACCOUNT_ID.getValue(),
allowedAccountTypes, newGLAccountId);
}
chargeToIncomeAccountMapping.setGlAccount(glAccount);
this.accountMappingRepository.saveAndFlush(chargeToIncomeAccountMapping);
}
} // deleted payment type
else {
this.accountMappingRepository.delete(chargeToIncomeAccountMapping);
}
}
// create new mappings
final Set<Long> incomingCharges = inputChargeToIncomeAccountMap.keySet();
incomingCharges.removeAll(existingCharges);
// incomingPaymentTypes now only contains the newly added
// payment Type mappings
for (final Long newCharge : incomingCharges) {
final Long newGLAccountId = inputChargeToIncomeAccountMap.get(newCharge);
saveChargeToFundSourceMapping(productId, newCharge, newGLAccountId, portfolioProductType, isPenalty);
}
}
}
}
/**
* @param command
* @param element
* @param productId
* @param changes
*/
public void updatePaymentChannelToFundSourceMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes, final PortfolioProductType portfolioProductType) {
// find all existing payment Channel to Fund source Mappings
final List<ProductToGLAccountMapping> existingPaymentChannelToFundSourceMappings = this.accountMappingRepository
.findAllPaymentTypeToFundSourceMappings(productId, portfolioProductType.getValue());
final JsonArray paymentChannelMappingArray = this.fromApiJsonHelper
.extractJsonArrayNamed(LoanProductAccountingParams.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), element);
/**
* Variable stores a map representation of Payment channels (key) and their fund sources (value) extracted from
* the passed in Jsoncommand
**/
final Map<Long, Long> inputPaymentChannelFundSourceMap = new HashMap<>();
/***
* Variable stores all payment types which have already been mapped to Fund Sources in the system
**/
final Set<Long> existingPaymentTypes = new HashSet<>();
if (paymentChannelMappingArray != null) {
if (changes != null) {
changes.put(LoanProductAccountingParams.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(),
command.jsonFragment(LoanProductAccountingParams.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue()));
}
for (int i = 0; i < paymentChannelMappingArray.size(); i++) {
final JsonObject jsonObject = paymentChannelMappingArray.get(i).getAsJsonObject();
final Long paymentTypeId = jsonObject.get(LoanProductAccountingParams.PAYMENT_TYPE.getValue()).getAsLong();
final Long paymentSpecificFundAccountId = jsonObject.get(LoanProductAccountingParams.FUND_SOURCE.getValue()).getAsLong();
inputPaymentChannelFundSourceMap.put(paymentTypeId, paymentSpecificFundAccountId);
}
// If input map is empty, delete all existing mappings
if (inputPaymentChannelFundSourceMap.size() == 0) {
this.accountMappingRepository.deleteAllInBatch(existingPaymentChannelToFundSourceMappings);
} /**
* Else, <br/>
* update existing mappings OR <br/>
* delete old mappings (which re already present, but not passed in as a part of Jsoncommand)<br/>
* Create new mappings for payment types that are passed in as a part of the Jsoncommand but not already
* present
*
**/
else {
for (final ProductToGLAccountMapping existingPaymentChannelToFundSourceMapping : existingPaymentChannelToFundSourceMappings) {
final Long currentPaymentChannelId = existingPaymentChannelToFundSourceMapping.getPaymentType().getId();
existingPaymentTypes.add(currentPaymentChannelId);
// update existing mappings (if required)
if (inputPaymentChannelFundSourceMap.containsKey(currentPaymentChannelId)) {
final Long newGLAccountId = inputPaymentChannelFundSourceMap.get(currentPaymentChannelId);
if (!newGLAccountId.equals(existingPaymentChannelToFundSourceMapping.getGlAccount().getId())) {
final GLAccount glAccount = getAccountById(LoanProductAccountingParams.FUND_SOURCE.getValue(), newGLAccountId);
existingPaymentChannelToFundSourceMapping.setGlAccount(glAccount);
this.accountMappingRepository.saveAndFlush(existingPaymentChannelToFundSourceMapping);
}
} // deleted payment type
else {
this.accountMappingRepository.delete(existingPaymentChannelToFundSourceMapping);
}
}
// create new mappings
final Set<Long> incomingPaymentTypes = inputPaymentChannelFundSourceMap.keySet();
incomingPaymentTypes.removeAll(existingPaymentTypes);
// incomingPaymentTypes now only contains the newly added
// payment Type mappings
for (final Long newPaymentType : incomingPaymentTypes) {
final Long newGLAccountId = inputPaymentChannelFundSourceMap.get(newPaymentType);
savePaymentChannelToFundSourceMapping(productId, newPaymentType, newGLAccountId, portfolioProductType);
}
}
}
}
/**
* @param productId
*
*/
private void savePaymentChannelToFundSourceMapping(final Long productId, final Long paymentTypeId,
final Long paymentTypeSpecificFundAccountId, final PortfolioProductType portfolioProductType) {
final PaymentType paymentType = this.paymentTypeRepositoryWrapper.findOneWithNotFoundDetection(paymentTypeId);
final GLAccount glAccount = getAccountById(LoanProductAccountingParams.FUND_SOURCE.getValue(), paymentTypeSpecificFundAccountId);
final ProductToGLAccountMapping accountMapping = new ProductToGLAccountMapping().setGlAccount(glAccount).setProductId(productId)
.setProductType(portfolioProductType.getValue()).setFinancialAccountType(CashAccountsForLoan.FUND_SOURCE.getValue())
.setPaymentType(paymentType);
this.accountMappingRepository.saveAndFlush(accountMapping);
}
/**
* @param productId
*
*/
private void saveChargeToFundSourceMapping(final Long productId, final Long chargeId, final Long incomeAccountId,
final PortfolioProductType portfolioProductType, final boolean isPenalty) {
final Charge charge = this.chargeRepositoryWrapper.findOneWithNotFoundDetection(chargeId);
// TODO Vishwas: Need to validate if given charge is fee or Penalty
// based on input condition
GLAccount glAccount;
/**
* Both CASH and Accrual placeholders have the same value for income from Interest and penalties
**/
CashAccountsForLoan placeHolderAccountType;
if (isPenalty) {
glAccount = getAccountByIdAndType(LoanProductAccountingParams.INCOME_ACCOUNT_ID.getValue(), GLAccountType.INCOME,
incomeAccountId);
placeHolderAccountType = CashAccountsForLoan.INCOME_FROM_PENALTIES;
} else {
List<GLAccountType> allowedAccountTypes = getAllowedAccountTypesForFeeMapping();
glAccount = getAccountByIdAndType(LoanProductAccountingParams.INCOME_ACCOUNT_ID.getValue(), allowedAccountTypes,
incomeAccountId);
placeHolderAccountType = CashAccountsForLoan.INCOME_FROM_FEES;
}
final ProductToGLAccountMapping accountMapping = new ProductToGLAccountMapping().setGlAccount(glAccount).setProductId(productId)
.setProductType(portfolioProductType.getValue()).setFinancialAccountType(placeHolderAccountType.getValue())
.setCharge(charge);
this.accountMappingRepository.saveAndFlush(accountMapping);
}
private List<GLAccountType> getAllowedAccountTypesForFeeMapping() {
List<GLAccountType> allowedAccountTypes = new ArrayList<>();
allowedAccountTypes.add(GLAccountType.INCOME);
allowedAccountTypes.add(GLAccountType.LIABILITY);
return allowedAccountTypes;
}
/**
* Fetches account with a particular Id and throws and Exception it is not of the expected Account Category
* ('ASSET','liability' etc)
*
* @param paramName
* @param expectedAccountType
* @param accountId
* @return
*/
public GLAccount getAccountByIdAndType(final String paramName, final GLAccountType expectedAccountType, final Long accountId) {
final GLAccount glAccount = this.accountRepositoryWrapper.findOneWithNotFoundDetection(accountId);
// validate account is of the expected Type
if (glAccount.getType().intValue() != expectedAccountType.getValue()) {
throw new ProductToGLAccountMappingInvalidException(paramName, glAccount.getName(), accountId,
GLAccountType.fromInt(glAccount.getType()).toString(), expectedAccountType.toString());
}
return glAccount;
}
public GLAccount getAccountById(final String paramName, final Long accountId) {
final GLAccount glAccount = this.accountRepositoryWrapper.findOneWithNotFoundDetection(accountId);
return glAccount;
}
public GLAccount getAccountByIdAndType(final String paramName, final List<GLAccountType> expectedAccountTypes, final Long accountId) {
final GLAccount glAccount = this.accountRepositoryWrapper.findOneWithNotFoundDetection(accountId);
// validate account is of the expected Type
List<Integer> glAccountTypeValues = new ArrayList<>();
for (GLAccountType glAccountType : expectedAccountTypes) {
glAccountTypeValues.add(glAccountType.getValue());
}
if (!glAccountTypeValues.contains(glAccount.getType())) {
throw new ProductToGLAccountMappingInvalidException(paramName, glAccount.getName(), accountId,
GLAccountType.fromInt(glAccount.getType()).toString(), glAccountTypeValues.toString());
}
return glAccount;
}
public void deleteProductToGLAccountMapping(final Long loanProductId, final PortfolioProductType portfolioProductType) {
final List<ProductToGLAccountMapping> productToGLAccountMappings = this.accountMappingRepository
.findByProductIdAndProductType(loanProductId, portfolioProductType.getValue());
if (productToGLAccountMappings != null && productToGLAccountMappings.size() > 0) {
this.accountMappingRepository.deleteAllInBatch(productToGLAccountMappings);
}
}
}
@@ -0,0 +1,50 @@
/**
* 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 java.util.List;
import java.util.Map;
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
public interface ProductToGLAccountMappingReadPlatformService {
Map<String, Object> fetchAccountMappingDetailsForLoanProduct(Long loanProductId, Integer accountingType);
List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappingsForLoanProduct(Long loanProductId);
List<ChargeToGLAccountMapper> fetchFeeToGLAccountMappingsForLoanProduct(Long loanProductId);
List<ChargeToGLAccountMapper> fetchPenaltyToIncomeAccountMappingsForLoanProduct(Long loanProductId);
Map<String, Object> fetchAccountMappingDetailsForSavingsProduct(Long savingsProductId, Integer accountingType);
List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappingsForSavingsProduct(Long savingsProductId);
List<ChargeToGLAccountMapper> fetchFeeToIncomeAccountMappingsForSavingsProduct(Long savingsProductId);
List<ChargeToGLAccountMapper> fetchPenaltyToIncomeAccountMappingsForSavingsProduct(Long savingsProductId);
Map<String, Object> fetchAccountMappingDetailsForShareProduct(Long productId, Integer accountingType);
List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappingsForShareProduct(Long productId);
List<ChargeToGLAccountMapper> fetchFeeToIncomeAccountMappingsForShareProduct(Long productId);
}
@@ -0,0 +1,415 @@
/**
* 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 java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.common.AccountingConstants.AccrualAccountsForLoan;
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForLoan;
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForSavings;
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForShares;
import org.apache.fineract.accounting.common.AccountingConstants.LoanProductAccountingDataParams;
import org.apache.fineract.accounting.common.AccountingConstants.SavingProductAccountingDataParams;
import org.apache.fineract.accounting.common.AccountingConstants.SharesProductAccountingParams;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper;
import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper;
import org.apache.fineract.accounting.producttoaccountmapping.domain.PortfolioProductType;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class ProductToGLAccountMappingReadPlatformServiceImpl implements ProductToGLAccountMappingReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private static final class ProductToGLAccountMappingMapper implements RowMapper<Map<String, Object>> {
public String schema() {
return " mapping.id as id, mapping.gl_account_id as glAccountId,glaccount.name as name,glaccount.gl_code as code,"
+ " mapping.product_id as productId, mapping.product_type as productType,mapping.financial_account_type as financialAccountType, "
+ " mapping.payment_type as paymentTypeId,pt.value as paymentTypeValue, mapping.charge_id as chargeId, charge.is_penalty as penalty, "
+ " charge.name as chargeName "
+ " from acc_product_mapping mapping left join m_charge charge on mapping.charge_id=charge.id "
+ " left join acc_gl_account as glaccount on mapping.gl_account_id = glaccount.id"
+ " left join m_payment_type pt on mapping.payment_type=pt.id" + " where mapping.product_type= ? ";
}
@Override
public Map<String, Object> mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final Long glAccountId = rs.getLong("glAccountId");
final Long productId = rs.getLong("productId");
final Long paymentTypeId = JdbcSupport.getLong(rs, "paymentTypeId");
final Long chargeId = rs.getLong("chargeId");
final Integer productType = rs.getInt("productType");
final String paymentTypeValue = rs.getString("paymentTypeValue");
final Integer financialAccountType = rs.getInt("financialAccountType");
final String glAccountName = rs.getString("name");
final String glCode = rs.getString("code");
final String chargeName = rs.getString("chargeName");
final Boolean penalty = rs.getBoolean("penalty");
final Map<String, Object> loanProductToGLAccountMap = new LinkedHashMap<>(5);
loanProductToGLAccountMap.put("id", id);
loanProductToGLAccountMap.put("glAccountId", glAccountId);
loanProductToGLAccountMap.put("productId", productId);
loanProductToGLAccountMap.put("productType", productType);
loanProductToGLAccountMap.put("financialAccountType", financialAccountType);
loanProductToGLAccountMap.put("paymentTypeId", paymentTypeId);
loanProductToGLAccountMap.put("paymentTypeValue", paymentTypeValue);
loanProductToGLAccountMap.put("chargeId", chargeId);
loanProductToGLAccountMap.put("chargeName", chargeName);
loanProductToGLAccountMap.put("penalty", penalty);
loanProductToGLAccountMap.put("glAccountName", glAccountName);
loanProductToGLAccountMap.put("glCode", glCode);
return loanProductToGLAccountMap;
}
}
@Override
public Map<String, Object> fetchAccountMappingDetailsForLoanProduct(final Long loanProductId, final Integer accountingType) {
final Map<String, Object> accountMappingDetails = new LinkedHashMap<>(8);
final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper();
final String sql = "select " + rm.schema() + " and product_id = ? and payment_type is null and mapping.charge_id is null";
final List<Map<String, Object>> listOfProductToGLAccountMaps = this.jdbcTemplate.query(sql, rm, // NOSONAR
new Object[] { PortfolioProductType.LOAN.getValue(), loanProductId });
if (AccountingRuleType.CASH_BASED.getValue().equals(accountingType)) {
for (final Map<String, Object> productToGLAccountMap : listOfProductToGLAccountMaps) {
final Integer financialAccountType = (Integer) productToGLAccountMap.get("financialAccountType");
final CashAccountsForLoan glAccountForLoan = CashAccountsForLoan.fromInt(financialAccountType);
final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId");
final String glAccountName = (String) productToGLAccountMap.get("glAccountName");
final String glCode = (String) productToGLAccountMap.get("glCode");
final GLAccountData gLAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode);
if (glAccountForLoan.equals(CashAccountsForLoan.FUND_SOURCE)) {
accountMappingDetails.put(LoanProductAccountingDataParams.FUND_SOURCE.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(CashAccountsForLoan.INCOME_FROM_FEES)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_FEES.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(CashAccountsForLoan.INCOME_FROM_PENALTIES)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_PENALTIES.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(CashAccountsForLoan.INTEREST_ON_LOANS)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INTEREST_ON_LOANS.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(CashAccountsForLoan.LOAN_PORTFOLIO)) {
accountMappingDetails.put(LoanProductAccountingDataParams.LOAN_PORTFOLIO.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(CashAccountsForLoan.TRANSFERS_SUSPENSE)) {
accountMappingDetails.put(LoanProductAccountingDataParams.TRANSFERS_SUSPENSE.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(CashAccountsForLoan.LOSSES_WRITTEN_OFF)) {
accountMappingDetails.put(LoanProductAccountingDataParams.LOSSES_WRITTEN_OFF.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(CashAccountsForLoan.GOODWILL_CREDIT)) {
accountMappingDetails.put(LoanProductAccountingDataParams.GOODWILL_CREDIT.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(CashAccountsForLoan.OVERPAYMENT)) {
accountMappingDetails.put(LoanProductAccountingDataParams.OVERPAYMENT.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(CashAccountsForLoan.INCOME_FROM_RECOVERY)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_RECOVERY.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(CashAccountsForLoan.INCOME_FROM_CHARGE_OFF_FEES)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_CHARGE_OFF_FEES.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(CashAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(CashAccountsForLoan.CHARGE_OFF_EXPENSE)) {
accountMappingDetails.put(LoanProductAccountingDataParams.CHARGE_OFF_EXPENSE.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(CashAccountsForLoan.CHARGE_OFF_FRAUD_EXPENSE)) {
accountMappingDetails.put(LoanProductAccountingDataParams.CHARGE_OFF_FRAUD_EXPENSE.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(CashAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_INTEREST)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(),
gLAccountData);
} else if (glAccountForLoan.equals(CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_FEES)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(),
gLAccountData);
}
}
} else if (AccountingRuleType.ACCRUAL_UPFRONT.getValue().equals(accountingType)
|| AccountingRuleType.ACCRUAL_PERIODIC.getValue().equals(accountingType)) {
for (final Map<String, Object> productToGLAccountMap : listOfProductToGLAccountMaps) {
final Integer financialAccountType = (Integer) productToGLAccountMap.get("financialAccountType");
final AccrualAccountsForLoan glAccountForLoan = AccrualAccountsForLoan.fromInt(financialAccountType);
final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId");
final String glAccountName = (String) productToGLAccountMap.get("glAccountName");
final String glCode = (String) productToGLAccountMap.get("glCode");
final GLAccountData gLAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode);
if (glAccountForLoan.equals(AccrualAccountsForLoan.FUND_SOURCE)) {
accountMappingDetails.put(LoanProductAccountingDataParams.FUND_SOURCE.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.INCOME_FROM_FEES)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_FEES.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.INCOME_FROM_PENALTIES)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_PENALTIES.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.INTEREST_ON_LOANS)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INTEREST_ON_LOANS.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.LOAN_PORTFOLIO)) {
accountMappingDetails.put(LoanProductAccountingDataParams.LOAN_PORTFOLIO.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.OVERPAYMENT)) {
accountMappingDetails.put(LoanProductAccountingDataParams.OVERPAYMENT.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.TRANSFERS_SUSPENSE)) {
accountMappingDetails.put(LoanProductAccountingDataParams.TRANSFERS_SUSPENSE.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.LOSSES_WRITTEN_OFF)) {
accountMappingDetails.put(LoanProductAccountingDataParams.LOSSES_WRITTEN_OFF.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.GOODWILL_CREDIT)) {
accountMappingDetails.put(LoanProductAccountingDataParams.GOODWILL_CREDIT.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.INTEREST_RECEIVABLE)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INTEREST_RECEIVABLE.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.FEES_RECEIVABLE)) {
accountMappingDetails.put(LoanProductAccountingDataParams.FEES_RECEIVABLE.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.PENALTIES_RECEIVABLE)) {
accountMappingDetails.put(LoanProductAccountingDataParams.PENALTIES_RECEIVABLE.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.INCOME_FROM_RECOVERY)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_RECOVERY.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_FEES)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_CHARGE_OFF_FEES.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.CHARGE_OFF_EXPENSE)) {
accountMappingDetails.put(LoanProductAccountingDataParams.CHARGE_OFF_EXPENSE.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.CHARGE_OFF_FRAUD_EXPENSE)) {
accountMappingDetails.put(LoanProductAccountingDataParams.CHARGE_OFF_FRAUD_EXPENSE.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_INTEREST)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(),
gLAccountData);
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_FEES)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(), gLAccountData);
} else if (glAccountForLoan.equals(AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY)) {
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(),
gLAccountData);
}
}
}
return accountMappingDetails;
}
@Override
public Map<String, Object> fetchAccountMappingDetailsForSavingsProduct(final Long savingsProductId, final Integer accountingType) {
final Map<String, Object> accountMappingDetails = new LinkedHashMap<>(8);
final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper();
final String sql = "select " + rm.schema() + " and product_id = ? and payment_type is null and mapping.charge_id is null ";
final List<Map<String, Object>> listOfProductToGLAccountMaps = this.jdbcTemplate.query(sql, rm, // NOSONAR
new Object[] { PortfolioProductType.SAVING.getValue(), savingsProductId });
if (AccountingRuleType.CASH_BASED.getValue().equals(accountingType)) {
for (final Map<String, Object> productToGLAccountMap : listOfProductToGLAccountMaps) {
final Integer financialAccountType = (Integer) productToGLAccountMap.get("financialAccountType");
final CashAccountsForSavings glAccountForSavings = CashAccountsForSavings.fromInt(financialAccountType);
final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId");
final String glAccountName = (String) productToGLAccountMap.get("glAccountName");
final String glCode = (String) productToGLAccountMap.get("glCode");
final GLAccountData gLAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode);
if (glAccountForSavings.equals(CashAccountsForSavings.SAVINGS_REFERENCE)) {
accountMappingDetails.put(SavingProductAccountingDataParams.SAVINGS_REFERENCE.getValue(), gLAccountData);
} else if (glAccountForSavings.equals(CashAccountsForSavings.SAVINGS_CONTROL)) {
accountMappingDetails.put(SavingProductAccountingDataParams.SAVINGS_CONTROL.getValue(), gLAccountData);
} else if (glAccountForSavings.equals(CashAccountsForSavings.INCOME_FROM_FEES)) {
accountMappingDetails.put(SavingProductAccountingDataParams.INCOME_FROM_FEES.getValue(), gLAccountData);
} else if (glAccountForSavings.equals(CashAccountsForSavings.INCOME_FROM_PENALTIES)) {
accountMappingDetails.put(SavingProductAccountingDataParams.INCOME_FROM_PENALTIES.getValue(), gLAccountData);
} else if (glAccountForSavings.equals(CashAccountsForSavings.TRANSFERS_SUSPENSE)) {
accountMappingDetails.put(SavingProductAccountingDataParams.TRANSFERS_SUSPENSE.getValue(), gLAccountData);
} else if (glAccountForSavings.equals(CashAccountsForSavings.INTEREST_ON_SAVINGS)) {
accountMappingDetails.put(SavingProductAccountingDataParams.INTEREST_ON_SAVINGS.getValue(), gLAccountData);
} else if (glAccountForSavings.equals(CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL)) {
accountMappingDetails.put(SavingProductAccountingDataParams.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), gLAccountData);
} else if (glAccountForSavings.equals(CashAccountsForSavings.LOSSES_WRITTEN_OFF)) {
accountMappingDetails.put(SavingProductAccountingDataParams.LOSSES_WRITTEN_OFF.getValue(), gLAccountData);
} else if (glAccountForSavings.equals(CashAccountsForSavings.INCOME_FROM_INTEREST)) {
accountMappingDetails.put(SavingProductAccountingDataParams.INCOME_FROM_INTEREST.getValue(), gLAccountData);
} else if (glAccountForSavings.equals(CashAccountsForSavings.ESCHEAT_LIABILITY)) {
accountMappingDetails.put(SavingProductAccountingDataParams.ESCHEAT_LIABILITY.getValue(), gLAccountData);
}
}
}
return accountMappingDetails;
}
@Override
public List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappingsForLoanProduct(final Long loanProductId) {
return fetchPaymentTypeToFundSourceMappings(PortfolioProductType.LOAN, loanProductId);
}
@Override
public List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappingsForSavingsProduct(final Long savingsProductId) {
return fetchPaymentTypeToFundSourceMappings(PortfolioProductType.SAVING, savingsProductId);
}
/**
* @param loanProductId
* @return
*/
private List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappings(final PortfolioProductType portfolioProductType,
final Long loanProductId) {
final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper();
final String sql = "select " + rm.schema() + " and product_id = ? and payment_type is not null";
final List<Map<String, Object>> paymentTypeToFundSourceMappingsList = this.jdbcTemplate.query(sql, rm, // NOSONAR
new Object[] { portfolioProductType.getValue(), loanProductId });
List<PaymentTypeToGLAccountMapper> paymentTypeToGLAccountMappers = null;
for (final Map<String, Object> productToGLAccountMap : paymentTypeToFundSourceMappingsList) {
if (paymentTypeToGLAccountMappers == null) {
paymentTypeToGLAccountMappers = new ArrayList<>();
}
final Long paymentTypeId = (Long) productToGLAccountMap.get("paymentTypeId");
final String paymentTypeValue = (String) productToGLAccountMap.get("paymentTypeValue");
final PaymentTypeData paymentTypeData = PaymentTypeData.instance(paymentTypeId, paymentTypeValue);
final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId");
final String glAccountName = (String) productToGLAccountMap.get("glAccountName");
final String glCode = (String) productToGLAccountMap.get("glCode");
final GLAccountData gLAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode);
final PaymentTypeToGLAccountMapper paymentTypeToGLAccountMapper = new PaymentTypeToGLAccountMapper()
.setPaymentType(paymentTypeData).setFundSourceAccount(gLAccountData);
paymentTypeToGLAccountMappers.add(paymentTypeToGLAccountMapper);
}
return paymentTypeToGLAccountMappers;
}
@Override
public List<ChargeToGLAccountMapper> fetchFeeToGLAccountMappingsForLoanProduct(final Long loanProductId) {
return fetchChargeToIncomeAccountMappings(PortfolioProductType.LOAN, loanProductId, false);
}
@Override
public List<ChargeToGLAccountMapper> fetchPenaltyToIncomeAccountMappingsForLoanProduct(final Long loanProductId) {
return fetchChargeToIncomeAccountMappings(PortfolioProductType.LOAN, loanProductId, true);
}
@Override
public List<ChargeToGLAccountMapper> fetchFeeToIncomeAccountMappingsForSavingsProduct(Long savingsProductId) {
return fetchChargeToIncomeAccountMappings(PortfolioProductType.SAVING, savingsProductId, false);
}
@Override
public List<ChargeToGLAccountMapper> fetchPenaltyToIncomeAccountMappingsForSavingsProduct(Long savingsProductId) {
return fetchChargeToIncomeAccountMappings(PortfolioProductType.SAVING, savingsProductId, true);
}
private List<ChargeToGLAccountMapper> fetchChargeToIncomeAccountMappings(final PortfolioProductType portfolioProductType,
final Long loanProductId, final boolean penalty) {
final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper();
String sql = "select " + rm.schema() + " and product_id = ? and mapping.charge_id is not null and charge.is_penalty=";
if (penalty) {
sql = sql + " true";
} else {
sql = sql + " false";
}
final List<Map<String, Object>> chargeToFundSourceMappingsList = this.jdbcTemplate.query(sql, rm, // NOSONAR
new Object[] { portfolioProductType.getValue(), loanProductId });
List<ChargeToGLAccountMapper> chargeToGLAccountMappers = null;
for (final Map<String, Object> chargeToIncomeAccountMap : chargeToFundSourceMappingsList) {
if (chargeToGLAccountMappers == null) {
chargeToGLAccountMappers = new ArrayList<>();
}
final Long glAccountId = (Long) chargeToIncomeAccountMap.get("glAccountId");
final String glAccountName = (String) chargeToIncomeAccountMap.get("glAccountName");
final String glCode = (String) chargeToIncomeAccountMap.get("glCode");
final GLAccountData gLAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode);
final Long chargeId = (Long) chargeToIncomeAccountMap.get("chargeId");
final String chargeName = (String) chargeToIncomeAccountMap.get("chargeName");
final Boolean penalty1 = (Boolean) chargeToIncomeAccountMap.get("penalty");
final ChargeData chargeData = ChargeData.lookup(chargeId, chargeName, penalty1);
final ChargeToGLAccountMapper chargeToGLAccountMapper = new ChargeToGLAccountMapper().setCharge(chargeData)
.setIncomeAccount(gLAccountData);
chargeToGLAccountMappers.add(chargeToGLAccountMapper);
}
return chargeToGLAccountMappers;
}
@Override
public Map<String, Object> fetchAccountMappingDetailsForShareProduct(Long productId, Integer accountingType) {
final Map<String, Object> accountMappingDetails = new LinkedHashMap<>(8);
final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper();
final String sql = "select " + rm.schema() + " and product_id = ? and payment_type is null and mapping.charge_id is null ";
final List<Map<String, Object>> listOfProductToGLAccountMaps = this.jdbcTemplate.query(sql, rm, // NOSONAR
new Object[] { PortfolioProductType.SHARES.getValue(), productId });
if (AccountingRuleType.CASH_BASED.getValue().equals(accountingType)) {
for (final Map<String, Object> productToGLAccountMap : listOfProductToGLAccountMaps) {
final Integer financialAccountType = (Integer) productToGLAccountMap.get("financialAccountType");
final CashAccountsForShares glAccountForShares = CashAccountsForShares.fromInt(financialAccountType);
final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId");
final String glAccountName = (String) productToGLAccountMap.get("glAccountName");
final String glCode = (String) productToGLAccountMap.get("glCode");
final GLAccountData gLAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode);
if (glAccountForShares.equals(CashAccountsForShares.SHARES_REFERENCE)) {
accountMappingDetails.put(SharesProductAccountingParams.SHARES_REFERENCE.getValue(), gLAccountData);
} else if (glAccountForShares.equals(CashAccountsForShares.SHARES_SUSPENSE)) {
accountMappingDetails.put(SharesProductAccountingParams.SHARES_SUSPENSE.getValue(), gLAccountData);
} else if (glAccountForShares.equals(CashAccountsForShares.INCOME_FROM_FEES)) {
accountMappingDetails.put(SharesProductAccountingParams.INCOME_FROM_FEES.getValue(), gLAccountData);
} else if (glAccountForShares.equals(CashAccountsForShares.SHARES_EQUITY)) {
accountMappingDetails.put(SharesProductAccountingParams.SHARES_EQUITY.getValue(), gLAccountData);
}
}
}
return accountMappingDetails;
}
@Override
public List<PaymentTypeToGLAccountMapper> fetchPaymentTypeToFundSourceMappingsForShareProduct(Long productId) {
return fetchPaymentTypeToFundSourceMappings(PortfolioProductType.SHARES, productId);
}
@Override
public List<ChargeToGLAccountMapper> fetchFeeToIncomeAccountMappingsForShareProduct(Long productId) {
return fetchChargeToIncomeAccountMappings(PortfolioProductType.SHARES, productId, false);
}
}
@@ -0,0 +1,42 @@
/**
* 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 java.util.Map;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.portfolio.savings.DepositAccountType;
public interface ProductToGLAccountMappingWritePlatformService {
void createLoanProductToGLAccountMapping(Long loanProductId, JsonCommand command);
void createSavingProductToGLAccountMapping(Long savingProductId, JsonCommand command, DepositAccountType accountType);
Map<String, Object> updateLoanProductToGLAccountMapping(Long loanProductId, JsonCommand command, boolean accountingRuleChanged,
int accountingRuleTypeId);
Map<String, Object> updateSavingsProductToGLAccountMapping(Long savingsProductId, JsonCommand command, boolean accountingRuleChanged,
int accountingRuleTypeId, DepositAccountType accountType);
void createShareProductToGLAccountMapping(Long shareProductId, JsonCommand command);
Map<String, Object> updateShareProductToGLAccountMapping(Long shareProductId, JsonCommand command, boolean accountingRuleChanged,
int accountingRuleTypeId);
}
@@ -0,0 +1,241 @@
/**
* 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 com.google.gson.JsonElement;
import java.util.HashMap;
import java.util.Map;
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForSavings;
import org.apache.fineract.accounting.common.AccountingConstants.SavingProductAccountingParams;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepository;
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
import org.apache.fineract.accounting.producttoaccountmapping.domain.PortfolioProductType;
import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepositoryWrapper;
import org.springframework.stereotype.Component;
@Component
public class SavingsProductToGLAccountMappingHelper extends ProductToGLAccountMappingHelper {
public SavingsProductToGLAccountMappingHelper(final GLAccountRepository glAccountRepository,
final ProductToGLAccountMappingRepository glAccountMappingRepository, final FromJsonHelper fromApiJsonHelper,
final ChargeRepositoryWrapper chargeRepositoryWrapper, final GLAccountRepositoryWrapper accountRepositoryWrapper,
final PaymentTypeRepositoryWrapper paymentTypeRepositoryWrapper) {
super(glAccountRepository, glAccountMappingRepository, fromApiJsonHelper, chargeRepositoryWrapper, accountRepositoryWrapper,
paymentTypeRepositoryWrapper);
}
/***
* Set of abstractions for saving Saving Products to GL Account Mappings
***/
public void saveSavingsToAssetAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId) {
saveProductToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.ASSET, PortfolioProductType.SAVING);
}
public void saveSavingsToIncomeAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId) {
saveProductToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.INCOME, PortfolioProductType.SAVING);
}
public void saveSavingsToExpenseAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId) {
saveProductToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.EXPENSE, PortfolioProductType.SAVING);
}
public void saveSavingsToLiabilityAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId) {
saveProductToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.LIABILITY, PortfolioProductType.SAVING);
}
/***
* Set of abstractions for merging Savings Products to GL Account Mappings
***/
public void mergeSavingsToAssetAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes) {
mergeProductToAccountMappingChanges(element, paramName, productId, accountTypeId, accountTypeName, changes, GLAccountType.ASSET,
PortfolioProductType.SAVING);
}
public void mergeSavingsToIncomeAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes) {
mergeProductToAccountMappingChanges(element, paramName, productId, accountTypeId, accountTypeName, changes, GLAccountType.INCOME,
PortfolioProductType.SAVING);
}
public void mergeSavingsToExpenseAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes) {
mergeProductToAccountMappingChanges(element, paramName, productId, accountTypeId, accountTypeName, changes, GLAccountType.EXPENSE,
PortfolioProductType.SAVING);
}
public void mergeSavingsToLiabilityAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes) {
mergeProductToAccountMappingChanges(element, paramName, productId, accountTypeId, accountTypeName, changes, GLAccountType.LIABILITY,
PortfolioProductType.SAVING);
}
public void createOrmergeSavingsToLiabilityAccountMappingChanges(final JsonElement element, final String paramName,
final Long productId, final int accountTypeId, final Map<String, Object> changes) {
createOrmergeProductToAccountMappingChanges(element, paramName, productId, accountTypeId, changes, GLAccountType.LIABILITY,
PortfolioProductType.SAVING);
}
/*** Abstractions for payments channel related to savings products ***/
public void savePaymentChannelToFundSourceMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes) {
savePaymentChannelToFundSourceMappings(command, element, productId, changes, PortfolioProductType.SAVING);
}
public void updatePaymentChannelToFundSourceMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes) {
updatePaymentChannelToFundSourceMappings(command, element, productId, changes, PortfolioProductType.SAVING);
}
public void saveChargesToIncomeAccountMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes) {
// save both fee and penalty charges
saveChargesToGLAccountMappings(command, element, productId, changes, PortfolioProductType.SAVING, true);
saveChargesToGLAccountMappings(command, element, productId, changes, PortfolioProductType.SAVING, false);
}
public void updateChargesToIncomeAccountMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes) {
// update both fee and penalty charges
updateChargeToIncomeAccountMappings(command, element, productId, changes, PortfolioProductType.SAVING, true);
updateChargeToIncomeAccountMappings(command, element, productId, changes, PortfolioProductType.SAVING, false);
}
public Map<String, Object> populateChangesForNewSavingsProductToGLAccountMappingCreation(final JsonElement element,
final AccountingRuleType accountingRuleType) {
final Map<String, Object> changes = new HashMap<>();
final Long savingsReferenceId = this.fromApiJsonHelper.extractLongNamed(SavingProductAccountingParams.SAVINGS_REFERENCE.getValue(),
element);
final Long incomeFromFeesId = this.fromApiJsonHelper.extractLongNamed(SavingProductAccountingParams.INCOME_FROM_FEES.getValue(),
element);
final Long incomeFromPenaltiesId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.INCOME_FROM_PENALTIES.getValue(), element);
final Long interestOnSavingsId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.INTEREST_ON_SAVINGS.getValue(), element);
final Long savingsControlId = this.fromApiJsonHelper.extractLongNamed(SavingProductAccountingParams.SAVINGS_CONTROL.getValue(),
element);
final Long transfersInSuspenseAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.TRANSFERS_SUSPENSE.getValue(), element);
final Long overdraftControlId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), element);
final Long incomeFromInterest = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.INCOME_FROM_INTEREST.getValue(), element);
final Long writeOffId = this.fromApiJsonHelper.extractLongNamed(SavingProductAccountingParams.LOSSES_WRITTEN_OFF.getValue(),
element);
switch (accountingRuleType) {
case NONE:
break;
case CASH_BASED:
changes.put(SavingProductAccountingParams.SAVINGS_CONTROL.getValue(), savingsControlId);
changes.put(SavingProductAccountingParams.SAVINGS_REFERENCE.getValue(), savingsReferenceId);
changes.put(SavingProductAccountingParams.INTEREST_ON_SAVINGS.getValue(), interestOnSavingsId);
changes.put(SavingProductAccountingParams.INCOME_FROM_FEES.getValue(), incomeFromFeesId);
changes.put(SavingProductAccountingParams.INCOME_FROM_PENALTIES.getValue(), incomeFromPenaltiesId);
changes.put(SavingProductAccountingParams.TRANSFERS_SUSPENSE.getValue(), transfersInSuspenseAccountId);
changes.put(SavingProductAccountingParams.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), overdraftControlId);
changes.put(SavingProductAccountingParams.INCOME_FROM_INTEREST.getValue(), incomeFromInterest);
changes.put(SavingProductAccountingParams.LOSSES_WRITTEN_OFF.getValue(), writeOffId);
break;
case ACCRUAL_PERIODIC:
break;
case ACCRUAL_UPFRONT:
break;
}
return changes;
}
/**
* Examines and updates each account mapping for given loan product with changes passed in from the Json element
*
* @param savingsProductId
* @param changes
* @param element
* @param accountingRuleType
*/
public void handleChangesToSavingsProductToGLAccountMappings(final Long savingsProductId, final Map<String, Object> changes,
final JsonElement element, final AccountingRuleType accountingRuleType) {
switch (accountingRuleType) {
case NONE:
break;
case CASH_BASED:
// asset
mergeSavingsToAssetAccountMappingChanges(element, SavingProductAccountingParams.SAVINGS_REFERENCE.getValue(),
savingsProductId, CashAccountsForSavings.SAVINGS_REFERENCE.getValue(),
CashAccountsForSavings.SAVINGS_REFERENCE.toString(), changes);
mergeSavingsToAssetAccountMappingChanges(element, SavingProductAccountingParams.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
savingsProductId, CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
CashAccountsForSavings.OVERDRAFT_PORTFOLIO_CONTROL.toString(), changes);
// income
mergeSavingsToIncomeAccountMappingChanges(element, SavingProductAccountingParams.INCOME_FROM_FEES.getValue(),
savingsProductId, CashAccountsForSavings.INCOME_FROM_FEES.getValue(),
CashAccountsForSavings.INCOME_FROM_FEES.toString(), changes);
mergeSavingsToIncomeAccountMappingChanges(element, SavingProductAccountingParams.INCOME_FROM_PENALTIES.getValue(),
savingsProductId, CashAccountsForSavings.INCOME_FROM_PENALTIES.getValue(),
CashAccountsForSavings.INCOME_FROM_PENALTIES.toString(), changes);
mergeSavingsToIncomeAccountMappingChanges(element, SavingProductAccountingParams.INCOME_FROM_INTEREST.getValue(),
savingsProductId, CashAccountsForSavings.INCOME_FROM_INTEREST.getValue(),
CashAccountsForSavings.INCOME_FROM_INTEREST.toString(), changes);
// expenses
mergeSavingsToExpenseAccountMappingChanges(element, SavingProductAccountingParams.INTEREST_ON_SAVINGS.getValue(),
savingsProductId, CashAccountsForSavings.INTEREST_ON_SAVINGS.getValue(),
CashAccountsForSavings.INTEREST_ON_SAVINGS.toString(), changes);
mergeSavingsToExpenseAccountMappingChanges(element, SavingProductAccountingParams.LOSSES_WRITTEN_OFF.getValue(),
savingsProductId, CashAccountsForSavings.LOSSES_WRITTEN_OFF.getValue(),
CashAccountsForSavings.LOSSES_WRITTEN_OFF.toString(), changes);
// liability
mergeSavingsToLiabilityAccountMappingChanges(element, SavingProductAccountingParams.SAVINGS_CONTROL.getValue(),
savingsProductId, CashAccountsForSavings.SAVINGS_CONTROL.getValue(),
CashAccountsForSavings.SAVINGS_CONTROL.toString(), changes);
mergeSavingsToLiabilityAccountMappingChanges(element, SavingProductAccountingParams.TRANSFERS_SUSPENSE.getValue(),
savingsProductId, CashAccountsForSavings.TRANSFERS_SUSPENSE.getValue(),
CashAccountsForSavings.TRANSFERS_SUSPENSE.toString(), changes);
createOrmergeSavingsToLiabilityAccountMappingChanges(element, SavingProductAccountingParams.ESCHEAT_LIABILITY.getValue(),
savingsProductId, CashAccountsForSavings.ESCHEAT_LIABILITY.getValue(), changes);
break;
case ACCRUAL_PERIODIC:
break;
case ACCRUAL_UPFRONT:
break;
}
}
public void deleteSavingsProductToGLAccountMapping(final Long savingsProductId) {
deleteProductToGLAccountMapping(savingsProductId, PortfolioProductType.SAVING);
}
}
@@ -0,0 +1,196 @@
/**
* 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 com.google.gson.JsonElement;
import java.util.HashMap;
import java.util.Map;
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForShares;
import org.apache.fineract.accounting.common.AccountingConstants.SharesProductAccountingParams;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepository;
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
import org.apache.fineract.accounting.producttoaccountmapping.domain.PortfolioProductType;
import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepositoryWrapper;
import org.springframework.stereotype.Component;
@Component
public class ShareProductToGLAccountMappingHelper extends ProductToGLAccountMappingHelper {
public ShareProductToGLAccountMappingHelper(final GLAccountRepository glAccountRepository,
final ProductToGLAccountMappingRepository glAccountMappingRepository, final FromJsonHelper fromApiJsonHelper,
final ChargeRepositoryWrapper chargeRepositoryWrapper, final GLAccountRepositoryWrapper accountRepositoryWrapper,
final PaymentTypeRepositoryWrapper paymentTypeRepositoryWrapper) {
super(glAccountRepository, glAccountMappingRepository, fromApiJsonHelper, chargeRepositoryWrapper, accountRepositoryWrapper,
paymentTypeRepositoryWrapper);
}
/***
* Set of abstractions for saving Share Products to GL Account Mappings
***/
public void saveSharesToAssetAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId) {
saveProductToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.ASSET, PortfolioProductType.SHARES);
}
public void saveSharesToIncomeAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId) {
saveProductToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.INCOME, PortfolioProductType.SHARES);
}
public void saveSharesToEquityAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId) {
saveProductToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.EQUITY, PortfolioProductType.SHARES);
}
public void saveSharesToLiabilityAccountMapping(final JsonElement element, final String paramName, final Long productId,
final int placeHolderTypeId) {
saveProductToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.LIABILITY, PortfolioProductType.SHARES);
}
/***
* Set of abstractions for merging Shares Products to GL Account Mappings
***/
public void mergeSharesToAssetAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes) {
mergeProductToAccountMappingChanges(element, paramName, productId, accountTypeId, accountTypeName, changes, GLAccountType.ASSET,
PortfolioProductType.SHARES);
}
public void mergeSharesToIncomeAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes) {
mergeProductToAccountMappingChanges(element, paramName, productId, accountTypeId, accountTypeName, changes, GLAccountType.INCOME,
PortfolioProductType.SHARES);
}
public void mergeSharesToEquityAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes) {
mergeProductToAccountMappingChanges(element, paramName, productId, accountTypeId, accountTypeName, changes, GLAccountType.EQUITY,
PortfolioProductType.SHARES);
}
public void mergeSharesToLiabilityAccountMappingChanges(final JsonElement element, final String paramName, final Long productId,
final int accountTypeId, final String accountTypeName, final Map<String, Object> changes) {
mergeProductToAccountMappingChanges(element, paramName, productId, accountTypeId, accountTypeName, changes, GLAccountType.LIABILITY,
PortfolioProductType.SHARES);
}
/*** Abstractions for payments channel related to Shares products ***/
public void savePaymentChannelToFundSourceMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes) {
savePaymentChannelToFundSourceMappings(command, element, productId, changes, PortfolioProductType.SHARES);
}
public void updatePaymentChannelToFundSourceMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes) {
updatePaymentChannelToFundSourceMappings(command, element, productId, changes, PortfolioProductType.SHARES);
}
public void saveChargesToIncomeAccountMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes) {
saveChargesToGLAccountMappings(command, element, productId, changes, PortfolioProductType.SHARES, true);
saveChargesToGLAccountMappings(command, element, productId, changes, PortfolioProductType.SHARES, false);
}
public void updateChargesToIncomeAccountMappings(final JsonCommand command, final JsonElement element, final Long productId,
final Map<String, Object> changes) {
updateChargeToIncomeAccountMappings(command, element, productId, changes, PortfolioProductType.SHARES, true);
updateChargeToIncomeAccountMappings(command, element, productId, changes, PortfolioProductType.SHARES, false);
}
public Map<String, Object> populateChangesForNewSharesProductToGLAccountMappingCreation(final JsonElement element,
final AccountingRuleType accountingRuleType) {
final Map<String, Object> changes = new HashMap<>();
final Long shareReferenceId = this.fromApiJsonHelper.extractLongNamed(SharesProductAccountingParams.SHARES_REFERENCE.getValue(),
element);
final Long incomeFromFeeAccountId = this.fromApiJsonHelper
.extractLongNamed(SharesProductAccountingParams.INCOME_FROM_FEES.getValue(), element);
final Long shareSuspenseId = this.fromApiJsonHelper.extractLongNamed(SharesProductAccountingParams.SHARES_SUSPENSE.getValue(),
element);
final Long shareEquityId = this.fromApiJsonHelper.extractLongNamed(SharesProductAccountingParams.SHARES_EQUITY.getValue(), element);
switch (accountingRuleType) {
case NONE:
break;
case CASH_BASED:
changes.put(SharesProductAccountingParams.SHARES_REFERENCE.getValue(), shareReferenceId);
changes.put(SharesProductAccountingParams.INCOME_FROM_FEES.getValue(), incomeFromFeeAccountId);
changes.put(SharesProductAccountingParams.SHARES_SUSPENSE.getValue(), shareSuspenseId);
changes.put(SharesProductAccountingParams.SHARES_EQUITY.getValue(), shareEquityId);
break;
case ACCRUAL_PERIODIC:
break;
case ACCRUAL_UPFRONT:
break;
}
return changes;
}
/**
* Examines and updates each account mapping for given loan product with changes passed in from the Json element
*
* @param sharesProductId
* @param changes
* @param element
* @param accountingRuleType
*/
public void handleChangesToSharesProductToGLAccountMappings(final Long sharesProductId, final Map<String, Object> changes,
final JsonElement element, final AccountingRuleType accountingRuleType) {
switch (accountingRuleType) {
case NONE:
break;
case CASH_BASED:
// asset
mergeSharesToAssetAccountMappingChanges(element, SharesProductAccountingParams.SHARES_REFERENCE.getValue(), sharesProductId,
CashAccountsForShares.SHARES_REFERENCE.getValue(), CashAccountsForShares.SHARES_REFERENCE.toString(), changes);
// income
mergeSharesToIncomeAccountMappingChanges(element, SharesProductAccountingParams.INCOME_FROM_FEES.getValue(),
sharesProductId, CashAccountsForShares.INCOME_FROM_FEES.getValue(),
CashAccountsForShares.INCOME_FROM_FEES.toString(), changes);
// liability
mergeSharesToLiabilityAccountMappingChanges(element, SharesProductAccountingParams.SHARES_SUSPENSE.getValue(),
sharesProductId, CashAccountsForShares.SHARES_SUSPENSE.getValue(), CashAccountsForShares.SHARES_SUSPENSE.toString(),
changes);
// equity
mergeSharesToEquityAccountMappingChanges(element, SharesProductAccountingParams.SHARES_EQUITY.getValue(), sharesProductId,
CashAccountsForShares.SHARES_EQUITY.getValue(), CashAccountsForShares.SHARES_EQUITY.toString(), changes);
break;
case ACCRUAL_PERIODIC:
break;
case ACCRUAL_UPFRONT:
break;
}
}
public void deleteSharesProductToGLAccountMapping(final Long sharesProductId) {
deleteProductToGLAccountMapping(sharesProductId, PortfolioProductType.SHARES);
}
}